tc39 / proposal-string-cooked

ECMAScript proposal for String.cooked built-in template tag

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Prior discussion

bathos opened this issue · comments

@bakkot provided a link to discussion thread from a few years ago. While it isn’t single-subject, many of the comments remain directly relevant here, so this issue serves to furnish the link to that discussion to visitors and to highlight/summarize some of the key points/contentions that arose in it.

String Identity Template Tag on ES Discuss

My summaries below don’t cover everything discussed there and are not unbiased; I describe the rationale in some cases for the choices currently proposed here where the same choices were up in the air in that thread. Any of these items (or others which I’ve not accounted for from the thread) could be spun off into new issues here.

Name bikeshedding (Moved to #5)

The name currently proposed here (String.cooked) also appears in this prior discussion, where it was suggested by @claudepache and appeared to have some support from others (@erights and @isiahmeadows). Another suggestion there which stands out to me is String.interpolate (from @allenwb).

Disagreement about appropriate API surface (Moved to #6)

There were some comments suggesting a view that the API surface maybe shouldn’t be that of a template tag function, but instead could be informed by the specific use case of being delegated to from other functions that are template tag functions. This point about primary utility was also used to advocate for verb-name (method) vs adjective-name (tag). There were arguments for both scattered throughout the thread, but these these two paraphrased quotes though I think capture the core of those positions well:

[The] primary use of this function is not using it as a tag function, but rather as a normal function. [...] The only use case I could see for using it as a tag function is if you were selecting from several tag functions at runtime and needed a "no-op" option. (@tjcrowder)

I agree that [it would] typically be called explicitly rather than used syntactically as a tag. However [...] if the job it is doing is equivalent to that done by a tag function, and if there are similar existing tags that can be called as a function to do a similar job, I think it would be better for them to have a similar signature and be used in an API compatible way. (@erights)

For what it’s worth, I emphatically agree with @erights here (and would not advocate for this proposal in another form). Still, it seems very notable that there was a disagreement about whether to address the problem space with a tag-contract API at all.

Template tag functions aren’t very composition friendly, but that problem is not made better by designating some of them unusable as tags, it just makes them inconsistent. As long as they remain consistent, the business of composing them remains possible to handle with generic helpers (though for most use cases, this would not be worthwhile).

An optional mapping function

One reason departure from a tag-based API was suggested was to facilitate a map function argument (which would not fit otherwise) for substitution values. I would point out that while mapping substitution values is a common pattern, it seems a bit overspecific — mapping template strings (likely differently) is also not uncommon and in any case, value mapping might be sensitive to the template string values (e.g. for contextual escaping in RegExp, etc).

It is impossible to propose any new built-in method without the “...what about a mapping argument” question coming up, so to me this doesn’t seem quite as notable as the prior item that questions API surface goals overall — but it still stands out for having seen repeated mentions there.

Iterator methods as alternative alleviation

It was pointed out that new iterator methods (which are now a somewhat advanced proposal) could address some of the pain that exists here today. I tend to think this is not quite true: String.raw would remain the obvious and attractive delegation target, whether used correctly (but confusingly) or incorrectly. I think String.cooked has value in its own right even in a world with robust iterator methods, but it would still be worthwhile to check out what things could look like with the iterator methods proposal as it stands today for some proper side-by-side comparisons.

I could create a separate issue to discuss an optional mapping flag, but I don't see a huge point in it over just calling .map() over the values before they are passed to String.cooked. If someone wants to make a stronger case for it please create an issue

@jamiebuilds It's useful for performance, and you could see it as analogous to the iterator callback in Array.from. That's also similarly unnecessary in theory, but it's also similarly extremely common to want to immediately .map it (though actually this one's even more common than that).

@bathos Could you give an example code of "Iterator methods as alternative alleviation"?

@hax You could imagine Iterator#interleave/zip and Iterator#join methods that could be combined to make it easier to merge the strings and values arrays provided to template tags:

function myTag(strings, ...values) {
  return strings.values().interleave(values).join("")
}

An equivalent in Rust:

use itertools::Itertools;

fn main() {
    let strings = ["a", "b", "c"].iter();
    let values = ["1", "2"].iter();
    let result = strings.interleave(values).join("");    
    dbg!(res); // res = "a1b2c"
}

In addition to @jamiebuilds's solid illustration there were some examples in and around esdiscuss thread, message 30. I'm not certain if the current iterator prototype methods proposal expects to include methods with the right interleaving behavior for this use case out-of-the-box.