Quasi-literals for templating?

# Axel Rauschmayer (14 years ago)

harmony:quasis

Quasi-literals look like they could be used for templating (similar to jQuery templating [1]). For example, to turn JSON data into HTML.

Has that been considered as a use case (e.g. after a compilation/generation step?). But it doesn’t seem like identifier names are accessible to quasi handlers, which would make this difficult.

BTW: “Desugaring” at the beginning of “Semantics” seems mangled.

[1] api.jquery.com/template

Axel

# Gavin Barraclough (14 years ago)

On Jun 12, 2011, at 4:35 AM, Axel Rauschmayer wrote:

BTW: “Desugaring” at the beginning of “Semantics” seems mangled.

I was just about to hit send an email saying the same thing!

Also, the section on the default quasi tag the name expandedLP is used, where I think this should be unescapedLP?

On this topic, rawLP and unescapedLP seem to be poor names. 'LP' seems to be meaningless line noise to developers using the feature who are not familiar with the grammar in the spec (and should not need to be). Maybe these could just raw & unescaped? - or rawLiterals/rawLiteralPortion/etc for something more verbose?

Has that been considered as a use case (e.g. after a compilation/generation step?). But it doesn’t seem like identifier names are accessible to quasi handlers, which would make this difficult.

This too! Given the example for the proposal:

msg`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`

It would seem useful if the same quasi could handle both of these formatting tasks:

msg`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
msg`Welcome, you are visitor number ${visitorNumber} to ${siteName}!`

And it seems the identifiers could help. Presumably the identifier names could be stored in a new frozen array on the call site specific object? Might it also be useful to provide a mechanism to override the identifier with something clearer, for example, if the visitor number were being read from the property 'obj().x',

msg`Welcome to ${siteName}, you are visitor number ${visitorNumber: obj().x}!`

Also, what is the 'this' object passed to the quasi handler? - might it make sense for 'this' to be the site specific object, rather than the passing this as an argument? - this seems to make some logical sense, and makes the arguments passed to function simpler to comprehend - they are all the substitution values, and arguments.length now matches the number of substitutions, indexed from zero, rather than being arguments length - 1. E.g. the first example in the proposal: // hoisted declaration. const callSiteId1234 = { rawLP: ['quasiLiteralPart1 ', ' quasiLiteralPart2'], unescapedLP: ['quasiLiteralPart1 ', ' quasiLiteralPart2'] };

// in-situ quasiHandlerName(callSiteId1234, quasiSubstitution) would instead call: // in-situ quasiHandlerName.call(callSiteId1234, quasiSubstitution)

# Mike Samuel (14 years ago)

2011/6/12 Axel Rauschmayer <axel at rauschma.de>:

harmony:quasis

Quasi-literals look like they could be used for templating (similar to jQuery templating [1]). For example, to turn JSON data into HTML.

Yep. Take a look at js-quasis-libraries-and-repl.googlecode.com/svn/trunk/index.html and try selecting "Safe HTML" from the dropdown at the top-right.

I'm also trying to make sure that the jquery templates spec ( wiki.jqueryui.com/w/page/37898666/Template ) integrates well.

Has that been considered as a use case (e.g. after a compilation/generation step?). But it doesn’t seem like identifier names are accessible to quasi handlers, which would make this difficult.

If the consensus ends up being that more than simple identifiers are allowed in substitutions, then I don't know what it would mean for identifiers to be available.

But more meta-data could be attached to the call site object.

BTW: “Desugaring” at the beginning of “Semantics” seems mangled.

Thanks. Will fix.

# Mike Samuel (14 years ago)

2011/6/13 Gavin Barraclough <barraclough at apple.com>:

On Jun 12, 2011, at 4:35 AM, Axel Rauschmayer wrote:

BTW: “Desugaring” at the beginning of “Semantics” seems mangled.

I was just about to hit send an email saying the same thing! Also, the section on the default quasi tag the name expandedLP is used, where I think this should be unescapedLP? On this topic, rawLP and unescapedLP seem to be poor names.  'LP' seems to be meaningless line noise to developers using the feature who are not familiar with the grammar in the spec (and should not need to be).  Maybe these could just raw & unescaped? - or rawLiterals/rawLiteralPortion/etc for something more verbose?

The names there are my fault. The last meeting decided what to include there, but we never got into names, and I chose bad ones.

Has that been considered as a use case (e.g. after a compilation/generation step?). But it doesn’t seem like identifier names are accessible to quasi handlers, which would make this difficult.

This too!  Given the example for the proposal: msgWelcome to ${siteName}, you are visitor number ${visitorNumber}! It would seem useful if the same quasi could handle both of these formatting tasks: msgWelcome to ${siteName}, you are visitor number ${visitorNumber}! msgWelcome, you are visitor number ${visitorNumber} to ${siteName}! And it seems the identifiers could help.  Presumably the identifier names could be stored in a new frozen array on the call site specific object?

This is quite possible. What exactly is stored might become confusing if the committee's consensus is that SubstitutionBody should allow more than just dotted identifiers.

Might it also be useful to provide a mechanism to override the identifier with something clearer, for example, if the visitor number were being read from the property 'obj().x', msgWelcome to ${siteName}, you are visitor number ${visitorNumber: obj().x}!

I think overloading colon would be a bad idea, and I think the best syntax for expressing property visitorNumber of obj().x is obj().x.visitorNumber.

Allowing overriding is problematic if it means that identifiers in the local scope other than those mentioned in the substitution body can be read.

Also, what is the 'this' object passed to the quasi handler? - might it make sense for 'this' to be the site specific object, rather than the passing this as an argument?

If the quasi handler is a member expression as in foo.barbaz${boo} then "this" is the left operand of the member expression, foo in that example.

this seems to make some logical sense, and makes the

Making the call-site object only available via this would prevent bound methods from using it.

# Gavin Barraclough (14 years ago)

On Jun 13, 2011, at 11:11 PM, Mike Samuel wrote:

Also, what is the 'this' object passed to the quasi handler? - might it make sense for 'this' to be the site specific object, rather than the passing this as an argument?

If the quasi handler is a member expression as in foo.barbaz${boo} then "this" is the left operand of the member expression, foo in that example.

this seems to make some logical sense, and makes the

Making the call-site object only available via this would prevent bound methods from using it.

Ah! - that makes sense. It hadn't been obvious to me that this use case would be possible, and I don't think there are any examples in the proposal showing this - if you have a chance, I think it could be great to add an example to demonstrate this.

# Axel Rauschmayer (14 years ago)

Has that been considered as a use case (e.g. after a compilation/generation step?). But it doesn’t seem like identifier names are accessible to quasi handlers, which would make this difficult.

If the consensus ends up being that more than simple identifiers are allowed in substitutions, then I don't know what it would mean for identifiers to be available.

I didn’t find in the spec how the "substitutions" parameter (that quasi handlers get) is structured, thus I assumed that it was an array of values. It could be an array of (key,value) pairs, then going beyond simple identifiers would be easy. I am assuming that order matters, if not, then there is something fundamental about handler parameters that I haven’t understood.

Using jQuery templates to validate template support: good choice, I really like this templating engine.

Axel

# Mark S. Miller (14 years ago)

On Wed, Jun 15, 2011 at 2:43 AM, Axel Rauschmayer <axel at rauschma.de> wrote:

Has that been considered as a use case (e.g. after a compilation/generation step?). But it doesn’t seem like identifier names are accessible to quasi handlers, which would make this difficult.

If the consensus ends up being that more than simple identifiers are allowed in substitutions, then I don't know what it would mean for identifiers to be available.

I didn’t find in the spec how the "substitutions" parameter (that quasi handlers get) is structured, thus I assumed that it was an array of values. It could be an array of (key,value) pairs, then going beyond simple identifiers would be easy. I am assuming that order matters, if not, then there is something fundamental about handler parameters that I haven’t understood.

Doing this by default violates alpha rename-ability.

To ease lexing worries, we simplified the quasis proposal at the last minute from accepting (informally)

  Identifier `....${ Expression }....`

to

  Identifier `....${ IdentifierDottedPathExpression }....`

Having now tried to write quasis within this restriction, I believe we chose too strong a restriction at this last minute and need to find some way to relax it. More on that later.

If we relax this position back towards Expression, then, if you want to pass such pairs, you could do so:

foo`...${ {bar} }...${ {baz} }...`

Recall that {bar} will be syntactic sugar for {bar: bar}. So this would expand to

foo(literalPartsRecord, {bar: bar}, {baz: baz})
# Brendan Eich (14 years ago)

On Jun 15, 2011, at 2:43 AM, Axel Rauschmayer wrote:

Has that been considered as a use case (e.g. after a compilation/generation step?). But it doesn’t seem like identifier names are accessible to quasi handlers, which would make this difficult.

If the consensus ends up being that more than simple identifiers are allowed in substitutions, then I don't know what it would mean for identifiers to be available.

I didn’t find in the spec how the "substitutions" parameter (that quasi handlers get) is structured, thus I assumed that it was an array of values.

That's right, it is the trailing arguments after the "callSiteId". A rest parameter can be used to capture these as an array. The examples probably should use rest params instead of |arguments|.

The point is that the callSiteId captures all the literal/constant/loop-invariant parts of the quasi, both raw and cooked ("expandedLP"). The remaining quasi handler parameters are the necessarily variable, evaluated-at-runtime substitution expression results.

The proposal needs to say this a bit more explicitly, and up front.

It could be an array of (key,value) pairs, then going beyond simple identifiers would be easy.

No, the proposal desugars, so whatever is after the $ and possibly in {} is an expression evaluated without change, and its result value goes is passed to the quasi handler.

The reason for grammatical restrictions on the substitutions, to be identifiers or perhaps dotted identifier chains (a restricted MemberExpression) is so that the spec and users' models of it are simple and we don't end up with hellish lexical rules for finding that closing }.

I am assuming that order matters, if not, then there is something fundamental about handler parameters that I haven’t understood.

Yes, order matters.

# Axel Rauschmayer (14 years ago)

If we relax this position back towards Expression, then, if you want to pass such pairs, you could do so:

foo`...${ {bar} }...${ {baz} }...`

Recall that {bar} will be syntactic sugar for {bar: bar}. So this would expand to

foo(literalPartsRecord, {bar: bar}, {baz: baz})

Ah, nice.

However, wouldn’t a different data structure be better for handlers? Zipping arrays together feels like unnecessary work.

For example: quasiHandlerNamequasiLiteralPart1 ${quasiSubstitution} quasiLiteralPart2 => quasiHandlerName([ { raw: "quasiLiteralPart1 ", escaped: "quasiLiteralPart1 " }, { value: ... }, { raw: " quasiLiteralPart2", escaped: " quasiLiteralPart2" }, ]);

The above example would work as follows (right?). This keeps the values as they are, instead of turning them into strings ("{bar: bar}" and "{baz: baz}" seem to be strings). var bar = "abc"; var baz = 123; foo${{bar}}${{baz}} => foo([ { raw: "", escaped: "" }, // not really necessary, any more { value: "abc", key: "bar" }, { raw: "", escaped: "" }, // not really necessary, any more { value: 123, key: "baz" }, { raw: "", escaped: "" } // not really necessary, any more ]);

Alternatively, one could have two different types, one for literal parts, one for substitutions.

# Axel Rauschmayer (14 years ago)

The point is that the callSiteId captures all the literal/constant/loop-invariant parts of the quasi, both raw and cooked ("expandedLP"). The remaining quasi handler parameters are the necessarily variable, evaluated-at-runtime substitution expression results.

That makes sense. The ID then allows one to do caching. But I don’t see how the same quasi-literal can be “invoked” multiple times.

# Mark S. Miller (14 years ago)

On Wed, Jun 15, 2011 at 8:36 AM, Axel Rauschmayer <axel at rauschma.de> wrote:

The point is that the callSiteId captures all the literal/constant/loop-invariant parts of the quasi, both raw and cooked ("expandedLP"). The remaining quasi handler parameters are the necessarily variable, evaluated-at-runtime substitution expression results.

That makes sense. The ID then allows one to do caching. But I don’t see how the same quasi-literal can be “invoked” multiple times.

At harmony:quasis#nesting the inner

safehtml`<td>${cell}</td>`

is invoked multiple times. The safehtml quasiparser can parse the html literal text fragments once and cache something that will accept and plug in data in the remaining hole positions. When the literal text fragments represent code in a substantial DSL, this caching matters.

# Brendan Eich (14 years ago)

On Jun 15, 2011, at 8:36 AM, Axel Rauschmayer wrote:

The point is that the callSiteId captures all the literal/constant/loop-invariant parts of the quasi, both raw and cooked ("expandedLP"). The remaining quasi handler parameters are the necessarily variable, evaluated-at-runtime substitution expression results.

That makes sense. The ID then allows one to do caching. But I don’t see how the same quasi-literal can be “invoked” multiple times.

In a loop:

for (let i = 0; i < N; i++) { let sub0 = i * 2, sub1 = i * i;

results[i] = re_match`lp0${sub0}lp1${sub1}lp2`(targets[i]);

}

Remember the quasi handler is just a function, here named re_match, found as usual by lexical scope. The invariant parts (raw and cooked) are collected and hoisted, given a unique name behind the scenes so they can be passed as an object, as the first actual parameter. The remaining arguments are sub0 and sub1evaluation results, which definitely vary per iteration.

# Axel Rauschmayer (14 years ago)

Right, I feel stupid: Even though the literal does not really exist as a reified construct (separate from code), the corresponding code can be executed multiple times.

# Mike Samuel (14 years ago)

2011/6/15 Brendan Eich <brendan at mozilla.com>:

The point is that the callSiteId captures all the literal/constant/loop-invariant parts of the quasi, both raw and cooked ("expandedLP"). The remaining quasi handler parameters are the necessarily variable, evaluated-at-runtime substitution expression results.

The proposal needs to say this a bit more explicitly, and up front.

Added harmony:quasis#callsiteid and renamed rawLP and unescapedLP.

# Axel Rauschmayer (14 years ago)

Quick feedback: I haven’t seen SVE (substitution value expression?) defined anywhere and can’t find a description of using ${{var}} as syntactic sugar for "{var:${var}}".

# Mark S. Miller (14 years ago)

On Wed, Jun 15, 2011 at 4:16 PM, Axel Rauschmayer <axel at rauschma.de> wrote:

Quick feedback: I haven’t seen SVE (substitution value expression?) defined anywhere and can’t find a description of using ${{var}} as syntactic sugar for "{var:${var}}".

At harmony:destructuring#issues:

The shorthand {x, y} for {x: x, y: y} allowed on the left-hand side of assignment should be allowed on the right too ...

This shorthand has nothing to do with quasis per se. It's just a convenient expression syntax. So if we allow more expression syntaxes to appear between ...${ and }... and if this "more" includes this specific syntax, then we can compose then to give the requested information conveniently without violating alpha-rename-ability.

# Bob Nystrom (14 years ago)

On Wed, Jun 15, 2011 at 4:31 PM, Mark S. Miller <erights at google.com> wrote:

The shorthand {x, y} for {x: x, y: y} allowed on the left-hand side of assignment should be allowed on the right too ...

This shorthand has nothing to do with quasis per se. It's just a convenient expression syntax. So if we allow more expression syntaxes to appear between ...${ and }... and if this "more" includes this specific syntax, then we can compose then to give the requested information conveniently without violating alpha-rename-ability.

That shorthand does, I think, beg for a slight change to the quasi syntax. Within a quasi, curly braces are currently used to mean grouping instead of an object literal. So if you want an argument that is an object literal, you have to double curly it: "${{foo}}".

Instead, how about:

// old new $foo $foo ${a + b} $(a + b) ${{x}} ${x}

So, within a quasi, a $ can be followed by an identifier, an expression grouped using parentheses (like they usually are) or an object literal (in curlies like it usually is). Using curlies as a grouping character in quasis always seemed a bit strange to me anyway, since they aren't used that way elsewhere in the language.

# Axel Rauschmayer (14 years ago)

Ah, very clever. It’d be nice to have an example of using this to compile templates (like jQuery templates). Maybe not even the implementation, just a quasi literal and why it works.

var myTmpl = tmplDear ${{first}} ${{last}}; // (*) alert(myTempl.render({ first: "Jane", last: "Doe" }));

The handler (at (*)) is called like this: tmpl(callSiteId73654, { first: undefined }, { last: undefined });

BTW: won’t it be a problem (for this use case) if the variables first and last don’t exist?

# Mark S. Miller (14 years ago)

On Wed, Jun 15, 2011 at 4:42 PM, Axel Rauschmayer <axel at rauschma.de> wrote:

Ah, very clever. It’d be nice to have an example of using this to compile templates (like jQuery templates). Maybe not even the implementation, just a quasi literal and why it works.

var myTmpl = tmplDear ${{first}} ${{last}}; // (*) alert(myTempl.render({ first: "Jane", last: "Doe" }));

The handler (at (*)) is called like this: tmpl(callSiteId73654, { first: undefined }, { last: undefined });

BTW: won’t it be a problem (for this use case) if the variables first and last don’t exist?

It would indeed be a problem. How much does this matter?

# Axel Rauschmayer (14 years ago)

var myTmpl = tmplDear ${{first}} ${{last}}; // (*) alert(myTempl.render({ first: "Jane", last: "Doe" }));

The handler (at (*)) is called like this: tmpl(callSiteId73654, { first: undefined }, { last: undefined });

BTW: won’t it be a problem (for this use case) if the variables first and last don’t exist?

It would indeed be a problem. How much does this matter?

It only matters if template generation (jQuery templates etc.) works as sketched above. However tests quasis for their ability to support jQuery templates must have a solution in mind that works.

I would expect templating (however it is implemented) to be a frequent use case, given how many templating engines are out there and given how elegant a solution quasi-literals are.

# Allen Wirfs-Brock (14 years ago)

On Jun 15, 2011, at 4:43 PM, Mark S. Miller wrote:

On Wed, Jun 15, 2011 at 4:42 PM, Axel Rauschmayer <axel at rauschma.de> wrote: Ah, very clever. It’d be nice to have an example of using this to compile templates (like jQuery templates). Maybe not even the implementation, just a quasi literal and why it works.

var myTmpl = tmplDear ${{first}} ${{last}}; // (*) alert(myTempl.render({ first: "Jane", last: "Doe" }));

The handler (at (*)) is called like this: tmpl(callSiteId73654, { first: undefined }, { last: undefined });

BTW: won’t it be a problem (for this use case) if the variables first and last don’t exist?

It would indeed be a problem. How much does this matter?

In ES.next unresolved resolved references to lexical bindings are early errors. {first} is equivalent to {first: first} so the RHS reference to first is a lexically bound unresolved reference that would produce an early error before the code ever ran.

# Mark S. Miller (14 years ago)

On Wed, Jun 15, 2011 at 5:36 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Jun 15, 2011, at 4:43 PM, Mark S. Miller wrote:

On Wed, Jun 15, 2011 at 4:42 PM, Axel Rauschmayer <axel at rauschma.de>wrote:

Ah, very clever. It’d be nice to have an example of using this to compile templates (like jQuery templates). Maybe not even the implementation, just a quasi literal and why it works.

var myTmpl = tmplDear ${{first}} ${{last}}; // (*) alert(myTempl.render({ first: "Jane", last: "Doe" }));

The handler (at (*)) is called like this: tmpl(callSiteId73654, { first: undefined }, { last: undefined });

BTW: won’t it be a problem (for this use case) if the variables first and last don’t exist?

It would indeed be a problem. How much does this matter?

In ES.next unresolved resolved references to lexical bindings are early errors. {first} is equivalent to {first: first} so the RHS reference to first is a lexically bound unresolved reference that would produce an early error before the code ever ran.

I understand that. I'm asking how much of a problem is it if, in order to use the pattern in question, you had to define the variables first?

# Mike Samuel (14 years ago)

2011/6/15 Axel Rauschmayer <axel at rauschma.de>:

Quick feedback: I haven’t seen SVE (substitution value expression?) defined anywhere and can’t find a description of using ${{var}} as syntactic sugar for "{var:${var}}".

SVE is defined in harmony:quasis#substitution_body_syntax reproduced below:

SVE

Production Result QuasiLiteral :: QuasiTagLiteralPortion QuasiLiteralTail SVE(QuasiLiteralTail) QuasiLiteralTail :: Substitution LiteralPortion QuasiLiteralTail array-concat(single-element-array(SVE(Substitution)), SVE(QuasiLiteralTail)) QuasiLiteralTail :: an empty array Substitution :: $Identifier PrimaryExpression : Identifier Substitution :: ${SubstitutionModifier Identifier} PrimaryExpression SubstitutionBody :: Identifier IdentifierPathTail MemberExpression : str-concat(SV(Identifier), SV(IdentifierPathTail)) IdentifierPathTail :: .IdentifierName IdentifierPathTail str-concat(“.”, SV(IdentifierName), SV(IdentifierPathTail)) IdentifierPathTail :: ε the empty string, ““

The SVE of a substitution is an expression that is evaluated in the scope in which the quasiliteral appears. The SVE of the quasi literal is the array of the SVE for each substitution.

E.g. the SVE of quasitagliteralPortion0 $x literalPortion1 $y.z literalPortion2 is [x, y.z].

# Axel Rauschmayer (14 years ago)

Sorry, I just meant the acronym: what does it stand for?

# Gavin Barraclough (14 years ago)

I think there may be a bug in the desugaring. My understanding is that the only arguments to the quasi tag are the call site id & the SVE, and the SVE contains only the substitution values, but in the desugaring example literal portions are being passed to the quasiTag too:

quasiTag(unguessableId1234, "literalPortion\0 ", x, " literalPortion1"])

I think this should be:

quasiTag(unguessableId1234, x)
# Axel Rauschmayer (14 years ago)

With a let-block it’s probably not a deal breaker, but it seems like a shame that this is necessary for such a frequent use case.

It might be worth it to ask someone who has written a templating engine.

# Mike Samuel (14 years ago)

2011/6/15 Axel Rauschmayer <axel at rauschma.de>:

Sorry, I just meant the acronym: what does it stand for?

Sorry. SVE: Substitution Value Expressions

# Mike Samuel (14 years ago)

2011/6/15 Gavin Barraclough <barraclough at apple.com>:

Hi Mike,

I think there may be a bug in the desugaring.  My understanding is that the only arguments to the quasi tag are the call site id & the SVE, and the SVE contains only the substitution values, but in the desugaring example literal portions are being passed to the quasiTag too:

quasiTag(unguessableId1234, "literalPortion\0 ", x, " literalPortion1"])

I think this should be:

quasiTag(unguessableId1234, x)

Thanks. Fixed.

# Brendan Eich (14 years ago)

My two cents: Ruby (with #{...} in its interpolated quasi-literal strings) and other languages allow pretty arbitrary expressions. The lexical issues are not insuperable. We are not really playing it "safe" by making narrow restrictions (such as Identifier expression only, or dotted identifier member expression only), so much as simplifying our spec work. Not even the drafting, which IIRC Mike already did. Rather, the validation that we would need to do.

I think we should step up, validate MIke's work, and formalize it fully. I thereby summon Waldemar. ;-)

# Brendan Eich (14 years ago)

On Jun 15, 2011, at 9:51 PM, Brendan Eich wrote:

My two cents: Ruby (with #{...} in its interpolated quasi-literal strings) and other languages allow pretty arbitrary expressions. The lexical issues are not insuperable. We are not really playing it "safe" by making narrow restrictions (such as Identifier expression only, or dotted identifier member expression only), so much as simplifying our spec work. Not even the drafting, which IIRC Mike already did. Rather, the validation that we would need to do.

I think we should step up, validate MIke's work, and formalize it fully. I thereby summon Waldemar. ;-)

Forgot to say that if we don't, then JS hackers can use let to hoist expressions into initializers of local bindings whose identifiers are used as substitutions, but they will rightly curse us. The needs of the many outweigh the needs of the few

# Mike Samuel (14 years ago)

2011/6/15 Brendan Eich <brendan at mozilla.com>:

On Jun 15, 2011, at 9:51 PM, Brendan Eich wrote:

My two cents: Ruby (with #{...} in its interpolated quasi-literal strings) and other languages allow pretty arbitrary expressions. The lexical issues are not insuperable. We are not really playing it "safe" by making narrow restrictions (such as Identifier expression only, or dotted identifier member expression only), so much as simplifying our spec work. Not even the drafting, which IIRC Mike already did. Rather, the validation that we would need to do. I think we should step up, validate MIke's work, and formalize it fully. I thereby summon Waldemar. ;-)

What can I do to make this easier?

# Brendan Eich (14 years ago)

On Jun 15, 2011, at 10:25 PM, Mike Samuel wrote:

2011/6/15 Brendan Eich <brendan at mozilla.com>:

On Jun 15, 2011, at 9:51 PM, Brendan Eich wrote:

My two cents: Ruby (with #{...} in its interpolated quasi-literal strings) and other languages allow pretty arbitrary expressions. The lexical issues are not insuperable. We are not really playing it "safe" by making narrow restrictions (such as Identifier expression only, or dotted identifier member expression only), so much as simplifying our spec work. Not even the drafting, which IIRC Mike already did. Rather, the validation that we would need to do. I think we should step up, validate MIke's work, and formalize it fully. I thereby summon Waldemar. ;-)

What can I do to make this easier?

Not clear. Validate that you do not get confused by any } in a quoted context in ES5.1, somehow. Convince Waldemar that your validation is sound. I'll buy you a Vegas vacation (kidding, Google can pay :-P).

# Axel Rauschmayer (14 years ago)

If I may, let me try again and make the case for a more robust data structure for callSiteId. The reason of my insistence is that I've used a similar array encoding in the past (for message templates) and it has bitten me: people always got indices wrong (in a small library that students of mine used). A list of tokens was a more robust solution and might work for quasis, if an indirection is introduced. Many people will use these data structures, so they should be as fool-proof as possible.

########## Proposal ##########

handler`aaa${foo}\nbbb`

desugars to:

const callSiteId1234 = {
    tokens: [
        { raw: "aaa", cooked: "aaa" },
        { key: "foo", valueIndex: 0 },
        { raw: "\\nbbb", cooked: "\nbbb" },
    ]
};
handler(callSiteId1234, [foo]);

########## Using the proposal for the default impl. ##########

// default quasi tag impl. with data structures as shown above function (callSiteId, substArray) { var out = []; callSiteId.tokens.forEach(function(token) { out.push(token.raw !== undefined ? token.raw : substArray[token.valueIndex]); }); return out.join(""); }

// For comparison: default quasi tag impl. from spec function (callSiteId /* , ...substitutions */) { var rawStrs = callSiteId.expandedLP; var out = []; var i = 0, k = -1, n = rawStrs.length - 1; while (i < n) { out[++k] = rawStrs[i]; out[++k] = arguments[++i]; } out[++k] = rawStrs[n]; // As per the original Array.prototype.slice and Array.prototype.join. return out.join(""); }

########## Alternative: types for tokens ##########

One could also introduce types for tokens. Then the iteration body becomes:

out.push(token instanceof LiteralSection ? token.raw : substArray[token.valueIndex]);

This is bike-shedding, but I’d also prefer "uninterpreted" and "interpreted" instead of "raw" and "cooked".