Proposal: Symbol.templateTag

# Alexander Jones (7 years ago)

discuss!

Template tags are a great feature with many novel applications, IMO approaching macro-level expressiveness. But due to their nature in this context, it's helpful if tags are named quite tersely. Unfortunately, if you have existing API, this can be challenging.

Let's say I have a DSL (domain-specific language), and I may, unsafely, generate typed snippets of code containing that DSL like so:

function dsl(code: string) {
    // `code` MUST be well-formed
}

const someStatement = dsl("val foo = " + dslEncode(foo));

Let's say I'd like to add support in my library for ES6 clients such that they can do:

const someStatement = dsl`val foo = ${foo}`;

where the dsl template tag would be implemented such that dslEncode is used on each interpolated expression.

Unfortunately, the dsl function's arguments are already defined and can't be changed. I'd need a new name, like dsl.tag, which would end up being a fair bit more verbose.

Would it be possible, at this point, to introduce a new behaviour into ES such that instead of merely calling the tag object, the implementation first checks for a Symbol.templateTag property on the tag object, and if it exists, it is invoked? I'd guess this can't Break The Web because no-one can realistically be using an existing function as a template tag if it was not already designed for it.

const someStatement = dsl`val foo = ${foo}`;
// desugars to, approximately
const someStatement = (dsl[Symbol.templateTag] || dsl)(["val foo =", ""],
foo);

To be honest, I am kind of surprised it wasn't already implemented like this, but maybe there were performance concerns with the branching. Interested in your thoughts.

Thanks

Alex

# Mark S. Miller (7 years ago)

I have run into this myself. It is a real problem, and I like the nature of your proposed solution. But I don't understand how it might break the web. If Symbol.templateTag is a new symbol, guaranteed unequal to any prior value, how can introducing it, and new behavior conditional on it, change any existing behavior?

# Alexander Jones (7 years ago)

OK, I was thinking about this incorrectly and I think you're right - it doesn't Break The Web. It's exactly the same as when @@isConcatSpreadable and @@toStringTag were introduced, as far as I can tell.

If no-one has any obvious objections to save me the effort, I'll try to write up a stage 0 this weekend.

# Andrea Giammarchi (7 years ago)

I'm not fully sure I understand ... instead of:

const dsl = (chunks, ...rest) => chunks.reduce((s, c, i) => s + (i ?

rest[i-1] : '') + c);

you are looking for something similar ?

const dsl = (chunks, ...rest) => chunks.reduce((s, c, i) => s + (
  i ? (dsl[Symbol.templateTag] || String)(rest[i-1]) : ''
) + c);

if that's the case, is Symbol.templateTag really needed ?

It looks to me it's possible to define your own behavior through your own transformer already and this proposal easily brings parsing-method clashing in the plate.

Or maybe not ... like I've said, I haven't fully understood the issue.

Thanks for any sort of extra clarification.

# Andrea Giammarchi (7 years ago)

fix: forgot of course i would never be 0

`const dsl = (chunks, ...rest) => chunks.reduce((s, c, i) => s + rest[i-1]

  • c);`

VS

const dsl = (chunks, ...rest) => chunks.reduce((s, c, i) => s + (dsl[Symbol.templateTag] || String)(rest[i-1]) + c);

# Alexander Jones (7 years ago)

As promised (to myself!) I've written a stage-0.

alex-weej/es-symbol-template-tag

Your feedback is greatly appreciated.