String identity template tag

Mike Samuel mikesamuel at gmail.com
Fri Dec 14 19:55:12 UTC 2018


On Fri, Dec 14, 2018 at 2:26 PM Isiah Meadows <isiahmeadows at gmail.com>
wrote:

> The main difference with that loop is that it's generalized to any number
> of arrays, not just two with the second array having length one less than
> the first. Otherwise, it'd look exactly the same. BTW, I like this route
> (`Array.interleave`) better since it doesn't have to result in just a
> single string result - it could just be an array of strings plugged into
> some API instead, or it could be procedurally streamed out in chunks.
>

Fair enough.
If you're not looking for something template tag specific then a simple zip
over iterators should do it?

function *ziperator(iterators) {
    let progressed;
    do {
        progressed = false;
        for (let iterator of iterators) {
            for (let element of iterator) {
                yield element;
                progressed = true;
                break;
            }
        }
    } while (progressed);
}

console.log(Array.from(ziperator([ ['a', 'b', 'c'][Symbol.iterator](), [1,
2][Symbol.iterator]() ])).join(''));
// -> a1b2c

(but optimized :)




> On Fri, Dec 14, 2018 at 14:04 Mike Samuel <mikesamuel at gmail.com> wrote:
>
>>
>>
>> On Fri, Dec 14, 2018 at 12:51 PM Isiah Meadows <isiahmeadows at gmail.com>
>> wrote:
>>
>>> I'll point out Kai could be on to something, although I disagree `zip`
>>> would be the right abstraction. Maybe `Array.interleave(...arrays)`? You
>>> could do `Array.interleave(template, args).map(String).join("")` for
>>> similar effect, and it'd be more generally useful.
>>>
>>> The key here is that iteration would stop after the index hits any
>>> array's length, so it'd be polyfilled kinda like this:
>>>
>>> ```js
>>> Array.interpolate = (...args) => {
>>>     let ret = []
>>>     let lengths = []
>>>     let count = 0
>>>     for (let i = 0; i < args.length; i++) {
>>>         lengths[i] = args[i].count
>>>     }
>>>     for (let index = 0; ; index++) {
>>>         for (let i = 0; i < args.length; i++) {
>>>             if (index === lengths[i]) return ret
>>>             ret[count++] = args[i][index]
>>>         }
>>>     }
>>> }
>>> ```
>>>
>>> (This could be optimized, though.)
>>>
>>
>> As a data point, something like this loop appears in most of the template
>> tags I've written but
>> it's never had these precise semantics so I didn't bother putting it into
>> template-tag-common <https://www.npmjs.com/package/template-tag-common>.
>>
>> That library makes it easy to split the operation of a template tag into
>> 3 stages:
>> 1. An optional configuration stage accessed by calling the template tag
>> as a regular function: mytag({ /* options */)`...`
>> 2. Static analysis over the strings.   This is memoized.
>> 3. Computing a result from (options, strings, results of step 2,
>> interoplated values)
>>
>> The final loop (step 3) in the template tags I maintain tends to looks
>> like
>>
>> function computeResult(options, staticState /* from step 2 */, strings,
>> ...values) {
>>   let n = values.length;  // Could do Math.max(strings.length - 1,
>> values.length);
>>   let result = strings[0];  // Usually strings.raw
>>   for (let i = 0; i < n;) {
>>     const interpolatedValue = f(options, staticState[i], values[i]);
>>     // Sometimes code here looks backwards at the result to see if it
>> needs to avoid token-merging hazards.
>>     result += interpolatedValue;
>>     result += strings[++i];
>>   }
>>   return wrapResult(result);  // Produce a value of a type that
>> encapsulates the tag's security guarantees.
>> }
>>
>>
>>
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20181214/b6ce48b2/attachment-0001.html>


More information about the es-discuss mailing list