2

Tagged template functions aren't normal functions in the same way that constructor functions aren't normal functions. They should never be called apart from in a special way. Constructors, with a new, tagging functions with an interpolated string literal.

We have a convention that constructors must be in Pascal Case so it is clear to the caller that it's a constructor.

  1. Is there a similar convention for tagging functions?
  2. If not, would enforcing that they have a trailing underscore be ambiguous to any other convention?

function tagger_(s, name, food) {  
  return `${s[0]}${name}${s[1]}${food}${s[2]}`
}

const name = 'Dave';
const food = 'ice-cream'
const message = tagger_`Hello World!  My name is ${name} and I like ${food} for dinner`;

console.log(message);

The argument for this not being a normal function is that the first argument isn't a string.

const log = console.log;

function tagger_(s) {
    console.log(`Has raw property: ${!!s.raw}`);
}

tagger_(`Hello World!`);
tagger_`Hello World!`

Likewise the s.raw property contains the string, unescaped. There would be no practical way to spoof this. Even if one could, it would be very bad practice.

function tagger_(s) {
    console.log(`s.raw: ${s.raw}`);
}

tagger_`This\nhas\tspecial characters`

The Duck Typing Problem

Whilst it may be possible to create a duck-typed object to pass into the method such that it won't fail immediately, the interface of the engune-generated object might extent (e.g. maybe an escapedRaw property is added). This would mean anything calling the method as a normal function would break.

When we write a normal function were totally in control of it, this isn't the case here because the s parameter type isn't controlled by the writer if the function.

BanksySan
  • 27,362
  • 33
  • 117
  • 216
  • I don't think tagging functions are special. They have a distinctive signature, yes, but otherwise they really are normal functions. You can call them like any other function, you just have to pass the right arguments. – Bergi Mar 11 '19 at 21:51
  • @Bergi They're special because the first argument is set to a special type. – BanksySan Mar 11 '19 at 22:00
  • @Bergi I've added a demo to highlight why I think it should be a special function. – BanksySan Mar 11 '19 at 22:10
  • @BanksySan — I think you're mistaken about the "non-string" argument. `'foo' instanceof String` also returns false. – Ben Blank Mar 11 '19 at 22:18
  • @BenBlank You're right. I've removed it. – BanksySan Mar 11 '19 at 22:20
  • @BanksySan They need to be called with an array as the first argument, that's all. Maybe the array should be frozen and have a `.raw` property, but that's still normally achievable. (Unlike constructors, which need `new`/`super()` or `Reflect.construct`) – Bergi Mar 11 '19 at 22:24
  • @Bergi that still doesn't solve the problem. `s.raw` is the string, unespaced. I've added another demo. – BanksySan Mar 11 '19 at 22:33
  • @BanksySan Actually `s.raw` is an array, but regardless: I don't see the problem that you are talking about. Nothing prevents you from passing such values (made-up or not) to a tag function through a normal function call. Sure, passing invalid data would be a bad practice, but so is passing invalid values to any other function. – Bergi Mar 11 '19 at 22:36
  • @BanksySan Consider `String.raw({raw:["This\\nhas\\tspecial characters"]})`. – Bergi Mar 11 '19 at 22:39
  • @Bergi how would you construct `raw` in a non-horrible way? Even it it's possible, wouldn't it be a horrible monkey patch? – BanksySan Mar 11 '19 at 22:39
  • @BanksySan What do you mean by "non-horrible way"? The raw value might come from a string literal, from a `String.raw` string, from a file reader or anywhere else. – Bergi Mar 11 '19 at 22:40
  • @Bergi I mean that we can't guarantee that there won't me a change made in the language that could expose another, special features that's use. Monkeying the object and calling it like a normal function would break if the tagging function used the new feature. – BanksySan Mar 11 '19 at 22:45
  • @BanksySan Well I guess that could be said about all functions expecting special objects :-) Finally, consider ``function tag(...args) { return String.raw(...args); } tag`…`;`` where `String.raw` is called like a normal function in a totally valid way. – Bergi Mar 11 '19 at 22:55
  • @Bergi I've described the problem with duck-tying the object above. – BanksySan Mar 12 '19 at 09:05

0 Answers0