Why aren't interpolation values in tagged template calls saved as a cached array?

# #!/JoePea (4 years ago)

What I mean is,

We can currently do this (try it in console):

arrays = []

function html(parts) { arrays.push(parts) }

let n = 0
let n2 = 0

function render() { return html`1st: ${n++}, 2nd: ${n2++}.` }

render()
render()

console.log(arrays[0] === arrays[1]) // true! <------------------- This!

But why don't specs allows us to do the following as well? (don't run it, it doesn't work as intended)

partsArrays = []
valuesArrays = []

function html(parts, values) {
    partsArrays.push(parts)
    valuesArrays.push(values)
}

let n = 0
let n2 = 0

function render() { return html`1st: ${n++}, 2nd: ${n2++}.` }

render()
render()

console.log(partsArrays[0] === partsArrays[1]) // true!
console.log(valuesArrays[0] === valuesArrays[1]) // This would be
convenient too! I think?

Instead, if we want an array of the values, we have to use arguments or ...args. For example:

// ... same ...
function html(parts, ...values) {
    partsArrays.push(parts)
    valuesArrays.push(values)
}
// ... same ...
console.log(partsArrays[0] === partsArrays[1]) // true!
console.log(valuesArrays[0] === valuesArrays[1]) // false! New array every time.

Seems like it would've been great to have the cached values arrays too. Why isn't this the case?

#!/JoePea

# #!/JoePea (4 years ago)

The following doesn't work either, and obviously it probably will never change because it is historical:

argsArrays = []

function html() {
    argsArrays.push(arguments)
}

let n = 0
let n2 = 0

function render() { return html`1st: ${n++}, 2nd: ${n2++}.` }

render()
render()

console.log(argsArrays[0] === argsArrays[1]) // false!

So yeah, just wondering why our only option is to use ...args and create new arrays each time, thus the performance is not optimized like it is with the string parts.

#!/JoePea

# Andrea Giammarchi (4 years ago)

there is some fundamental misconception of the ECMAScript standard in these examples ... to start with, assuming the same arguments object is passed twice to two different invokes of a callback, would break down to ES3 and lower.

Secondly, interpolated values you are passing around are always different, so I can't understand why would you expect an always same Array in there ... it's not the same as the template one, which is immutable, aka frozen, and it can carry any different value at any different position.

Accordingly, the current standard for template literals tags function is nearly perfect, and none of these suggestions would improve anything in the wild (using template literals since 2015, I think I've consumed them all in all flavours).

.

# #!/JoePea (4 years ago)

to start with, assuming the same arguments object is passed twice to two different invokes of a callback, would break down to ES3 and lower.

I know, that's what I mentioned (it is historical, will never be the same object). :)

interpolated values you are passing around are always different, so I can't understand why would you expect an always same Array in there

That's true. However, making a new array each time causes garbage collection that can otherwise be avoided.

The interpolation values array could be made immutable for the user, while only the engine could be allowed to modify the content. The length of the array will never change. This will be much better than making a new array every time.

Imagine, for example, hundreds of templates being called repeatedly for animations. It would simply be better if the values (f.e. numbers) were the only things changing. People could of course pass new objects as values on each update, but then that's their problem and an experienced developer will know about the cost in that case.

none of these suggestions would improve anything in the wild

You don't think this idea would help for performance in some way?

#!/JoePea

# Richard Gibson (4 years ago)

There is no "interpolation values array"; a tag function receives a single frozen array of static template contents (unique per call site) and a distinct argument for each substitution value. Source code can collect them into an array by use of ... "rest" syntax, but every evaluation of that construct for a function call creates a new array tc39.es/ecma262/#_ref_4403. But if there were a data structure to

modify in place, I think doing so would break the currently-specified behavior of reentrant tagged templates and therefore not be an option.

Authors can use memoization and similar techniques to minimize garbage, and there's certainly room for implementations to detect optimizable patterns, but changing already-published semantics is rarely an option.