docs: function memoizedTagFunction (computeStaticHelper, computeResultHelper) {
    const memoTable = new LruCache()
  
    /**
     * @param {!Array.<string>} staticStrings
     * @return {T}
     */
    function staticStateFor (staticStrings) {
      let staticState = null
      const canMemoize = Object.isFrozen(staticStrings) &&
            Object.isFrozen(staticStrings.raw)
      if (canMemoize) {
        staticState = memoTable.get(staticStrings)
      }
      let failure = null
      if (!staticState) {
        try {
          staticState = { pass: computeStaticHelper(staticStrings) }
        } catch (exc) {
          failure = exc
          staticState = { fail: exc.message || 'Failure' }
        }
        if (canMemoize) {
          memoTable.set(staticStrings, staticState)
        }
      }
      if (staticState.fail) {
        throw failure || new Error(staticState.fail)
      }
      return staticState.pass
    }
  
    /** @param {O} options */
    function usingOptions (options) {
      return (staticStringsOrOptions, ...dynamicValues) => {
        // If we've only been passed an options object,
        // return a template tag that uses it.
        if (dynamicValues.length === 0 &&
            !Array.isArray(staticStringsOrOptions)) {
          return usingOptions(staticStringsOrOptions)
        }
  
        const staticStrings = staticStringsOrOptions
        requireValidTagInputs(staticStrings, dynamicValues)
  
        return computeResultHelper(
          options, staticStateFor(staticStrings), staticStrings, dynamicValues)
      }
    }
  
    // eslint-disable-next-line no-inline-comments
    return usingOptions(/** @type {O} */ ({}))
  }