String identity template tag

Isiah Meadows isiahmeadows at gmail.com
Wed Dec 19 12:53:31 UTC 2018


Could you bring that up in a different thread instead of driving this
off-topic? Also, please don't forget that a *very* significant chunk
of JS doesn't even run in a GUI environment (consider: servers, IoT,
satellites, etc.).

-----

Isiah Meadows
contact at isiahmeadows.com
www.isiahmeadows.com

On Wed, Dec 19, 2018 at 1:33 AM kai zhu <kaizhu256 at gmail.com> wrote:
>>
>> I could go with an iterator equivalent, but I'd like to defer that to
>> the seemingly-planned "iterlib" thing that's been considered since
>> before ES2015 was released.
>
>
> i'm against iterator-based design-patterns and libraries, as they lack a clear fit in most javascript solutions.  the vast majority of looping in UX-workflows (javascript’s primary problem-domain) are over [serializable] JSON lists/dicts/strings, where Array/Object/String looping-methods will suffice.
>
> all other use-cases are uncommon/complicated enough that custom for/while loops are usually better suited than custom-iterators.
>
> -kai
>
>
>
> On 18 Dec 2018, at 16:42, Mike Samuel <mikesamuel at gmail.com> wrote:
>
> Fair enough.
>
> On Tue, Dec 18, 2018, 5:29 PM Isiah Meadows <isiahmeadows at gmail.com wrote:
>>
>> I could go with an iterator equivalent, but I'd like to defer that to
>> the seemingly-planned "iterlib" thing that's been considered since
>> before ES2015 was released. Something that works with arrays is good
>> enough for now.
>>
>> BTW, your `ziperator` isn't really the same as my `Array.interpolate`
>> (which is better named `Array.interleave`). It needs to be this:
>>
>> ```js
>> function *ziperator(...iters) {
>>     for (let i = 0; i < iters.length; i++) {
>>         iters[i] = iters[i][Symbol.iterator]()
>>     }
>>     while (true) {
>>         for (let i = 0; i < iters.length; i++) {
>>             const {done, value} = iters[i].next()
>>             if (done) return undefined
>>             yield value
>>         }
>>     }
>> }
>> ```
>>
>> The optimized version is pretty straightforward (using private fields
>> + methods here):
>>
>> ```js
>> function ziperator(...iters) { return new InterleavedIterator(iters) }
>>
>> class InterleavedIterator {
>>     #iters, #index
>>     constructor(iters) { this.#iters = iters; this.#index = 0 }
>>     [Symbol.iterator]() { return this }
>>     next(value) { return this.#invoke("next", value) }
>>     throw(value) { return this.#invoke("throw", value) }
>>     return(value) { return this.#invoke("return", value) }
>>     #invoke(method, value) {
>>         if (this.#iters == null) return {done: true, value: undefined}
>>         const index = this.#index
>>         this.#index = (index + 1) % this.#iters.length
>>         const {done, value} = this.#iters[index][method](value)
>>         if (done) this.#iters = undefined
>>         return {done, value}
>>     }
>> }
>> ```
>>
>> -----
>>
>> Isiah Meadows
>> contact at isiahmeadows.com
>> www.isiahmeadows.com
>> On Fri, Dec 14, 2018 at 2:55 PM Mike Samuel <mikesamuel at gmail.com> wrote:
>> >
>> >
>> >
>> > 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.
>> >>>
>> >>> 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.
>> >>> }
>> >>>
>> >>>
>> >>>
>> >>>
>
>


More information about the es-discuss mailing list