-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Name bikeshed #5
Comments
+1 for On reflection, I also like |
Pro for I quite like
Not a fan of |
Since you're looking to match the behaviour of an untagged template, why not something like In a recent refactoring of TV and TRV, we chose to refer to the cooked properties as the "indexed components" of the template (since the only reference to the term "cooked" currently is in alias names), in contrast to the "raw components". Though I think I am fine with either of |
|
I had no idea we even used the term "cooked" internally in the spec. It just sounds like a nice analogue, and lightly conveys the precise meaning intended here - the string pieces have been allowed to "mature" and change to their final forms. |
One downside for names like |
Also to mention, I've seen a number of compilers in different languages use the term "cooked" to distinguish it from "raw". In Rome we use it like this: jsTemplateElement.create({
cooked: "",
raw: "",
}) |
"cooked" its not exactly common nomenclature in JS. I'd suggest |
FWIW, MDN refers to "cooked" strings within their template literal documentation. Excerpt:
|
Could it be |
@GreLI that's an interesting angle. It might be a bit of a squatting hazard given JS does already have Number.parseFloat - which parses a regular number token (afaik; not 100% sure it's free of quirks) and analogous companions (Boolean.parse, String.parse) don't strike me as too far fetched to consider leaving space for. Tags can only address raw/cooked string values rather than string tokens - the former are products of parsing, the latter is input to parsing (w/ the quotation marks, etc). OTOH, "parsed" is not "parse", and I can see how that could make the difference. Speaking of that distinction: I think parse v parsed is a great example of why nouny/adjectival naming for tags may be preferable over verb names in general - it seems particularly clear when contrasting these that the first would be "off". Raw is adjectival, too, and while a precedent of one may not count for much, (anecdotally) it seems like the convention of tag names communicating something the string is rather than what's being done with it is pretty widespread (e.g. "html", not "createTemplate"). So (for example) I'd suggest "interpolated" is better than the previously discussed "interpolate". |
At first glance, the past-tense In a more general sense, "parsing" to me implies syntactic analysis of a string or the transformation between data types, neither of which feel terribly relevant here. Looking around for alternatives I found that Golang's grammar uses "raw" vs "interpreted". I have similar reservations as I do |
I wanted to use this function in a template tag the other day in the way suggested in the readme: function myTag(strings, ...exprs) {
let transformedStrings = strings.map(someTransformation);
return String.cooked(transformedStrings, ...exprs);
} This made me like That is, I've already processed the strings at the point at which I want to call this function. So any name which suggests that it is processing the strings themselves, rather than just mashing them together, is a bad name. (For example, So my vote is now for (This usage also makes it clear, I think, that |
I could also see a case for splitting out the two use cases: have a |
How about |
I like |
I think |
I think I might have mentioned this in a TC39 plenary meeting at one point, but I couldn't find it in the notes with a quick look. In general I think using puns to name things can be confusing for developers (numbering likely in the millions or tens of millions worldwide) who aren't fluent in English. |
I suppose with general purpose translation tools "interpolated" would do better but with docs in an editor or a search it would be quick to find the meaning. As for I might have changed my choice to |
Tag names should be descriptive/declarative, not verbs. I’m not married to a particular name, but I think that factor is important. Old meeting notes suggest this reasoning was used for A name like |
It's only technically a tag name, though. It doesn't need to be a particularly good example of what to call a userspace tag name IMO. I do agree that they aren't imperative verbs like a function. I think the past participle would work. So I suppose Maybe that can be considered descriptive and declarative even though it's just changing the tense. |
Yes, I'm pretty sure that's the intended original meaning. It returns the raw source code string. I would argue that the opposite term is |
Hi ! Definition here: Why It is used to kinda "get what would be the result of untagged string" but in an actual string tag function... |
|
Yes exaclty ! String.merge`Hello ${who} !` === 'Hello ' + who + ' !' |
Interesting proposals, I do love the symmetry of
Over all, I sort of think the problem is that most of these are verbs (or adjectival uses of things that are also verbs). Only As an alternative, might I present: String.tag You might being thinking "that doesn't even mean anything!" and you would be right! That's the point. Because Furthermore, if you used it as many people likely will: const html = String.tag;
const css = String.tag;
const sql = String.tag; It becomes extremely clear that you are just making these identifiers string tags. This also has the advantage of being short, and even happens to be the same number of characters as It means nothing, it does nothing. It's just there for other tags to build upon. That being said, I think of the other proposed terms "cooked" is the one I like best purely because "raw" already exists. I also think that its lack of contextual meaning at least means it probably won't be misunderstood, the advantage of a potentially non-obvious name is that few people will assume they know what it means and will look it up. This is as opposed to "interpolate", "identity", "parsed", and similar which someone could easily misinterpret without even stopping to think "do I actually know what that means?" |
function safePath(strings, ...subs) {
return String.fromTemplate(strings, ...subs.map(sub => encodeURIComponent(sub)));
}
let id = 'spottie?';
safePath`/cats/${ id }`;
// → "/cats/spottie%3F" It follows the existing "$Type.from$Thing" pattern of |
I like I do think it's slightly more redundant though, just as |
I may also purpose:
|
Usually a static
I do not agree with this point: |
|
We generally don't see abbreviations in the language, and I think that's a good thing. While My point about "not doing anything" is that template literals already do this, they already interpolate/merge/zip/concatenate inherently. If I write this: const content = "Inner HTML!";
element.innerHTML = `<div>${content}</div>`; That already does all of the functionality, that will merge the template together and product a string that will be assigned to Using this
My example expanded: const html = String.tag;
const content = "Inner HTML!";
element.innerHTML = html`<div>${content}</div>`; Note that the The other use is as the basis for tags that do actually do something, but don't modify the templating. For instance, I might implement an actual
The result is obvious, the implication is not. A template already creates a string, the result of my first example with no tag is a string. Using a template doesn't produce some specialized object, it produces a string, as such: String.fromTemplate`<div>${content}</div>`; Is arguably redundant (though not as bad as many of the others). This statement doesn't create a string from a template, it actually just gives you the result of the template - hence why The muddy water comes from using the method in order to build a template tag such as @gibson042 wrote:
That tag does actually modify the way that the template is interpolated. This is why the naming is so difficult, because in this use case there is a good argument for a name like Still, I think Still, I think As for collage, concat, alternate, add, glued, etc. These all have the same issue as with interpolate or parsed: They only make sense in the context of implementing another tag function, they only make sense when called with the array and strings. String.glued`<div>${content}</div>`; ...it doesn't really make much sense, and at best implies that the template requires a tag in order to function (which it does not). And if you do Edit: Mixing markdown and templates is... not great. |
+1 for this argument, I also use this VSCode extension, but for your information it work also with a comment: /*html*/`<div>${content}</div>` ;) I almost agree with all the rest of your last arguments, except I really don't see the point of using this "noop" function as a tag directly, it is the same as not using a tag... I feel this usage would be rather rare ! |
|
If this is something that we expect people to use as both a function and a tag, then the name needs to suggest that the signature is what you'd get when using it as a tag, because that signature is not what you'd normally expect for something you call as a function. Which means |
What is the point of using it as tag ? It only makes your source code bigger, and adds useless virtual machine's rountrips decreasing performance ! BTW the tag's signature is indeed needed to be able to pass arguments around easily : const myTag = ( ...args )=> {
const sanitized = utils.sanitizeTemplateLiteral( ...args )
let result = String.cooked( ...sanitized )
result = result.replace( /foo/g, 'bar' )
...
} |
For example: function renderIntro(name, needsSanitation) {
let tag = needsSanitation ? sanitize : String.fromTemplate;
return tag`Hello ${name}`!
}
No. If we expect this not to be used as a tag, then the obvious signature is to take two lists. You can just write |
function renderIntro(name, needsSanitation) {
return needsSanitation
? sanitize`Hello ${name}!`
: `Hello ${name}!`
} function renderIntro(name, needsSanitation) {
let name = needsSanitation ? sanitize(name) : name
return `Hello ${name}`!
} |
Please imagine a more complex template expression, such that you wouldn't want to repeat the whole thing or sanitize each individual expression, as was obviously my intent. |
You would be unlikely to author code with it used directly as a tag rather than "renaming" it by assigning it to some other identifier. However picking a name under the assumption that "nobody will ever use it like that" isn't great, and at the very least there will be examples written on sites like MDN that absolutely will show it used in that way in order to demonstrate what it does/doesn't do. And as @bakkot pointed out, whether or not anyone uses it directly as a tag, the signature of the function has to match the signature of a tag function in order for this sort of "renaming" to work in the first place. As such, the function will be a valid tag function even if using it as one directly is seemingly pointless. Though I'm not really convinced that it's truly pointless, I just haven't come up with a good reason to use it without a rename yet.
Yeah, unfortunately the comment way doesn't always work correctly and it's needlessly verbose. Plus it's a little weird to use both methods. For instance in a project that actually uses Lit, the Another neat thing to do with these, is have a debug-only behavior. For instance: function debugTemplate(strings, ...values) {
const string = String.cooked(strings, ...values);
console.log(string);
return string;
}
const sql = DEBUG ? debugTemplate : String.cooked; SQL in particular I commonly need this because SQL errors are almost always totally unhelpful, and being able to copy-paste the fully interpolated query into another tool to check it over is invaluable. |
@bakkot Still you can process the strings part if you want or need but the case fall back to the non tag usage because you will need the strings array therefore your function to be a tag... |
@zeel01 For your SQL exemple: const query = ` blah ${table} blah ...`
DEBUG && console.log( query ) Or if a sql tag is needed to do actual jobs: const sql = ( ...args )=> {
const query = String.{choose a name in this thread} ( ...args )
DEBUG && console.log( query )
return MySQL.exec( query )
}
const rows = sql`SELECT ...` |
Examples are intentionally trivial, refactoring a trivial example doesn't really add anything to the conversation. Imagine I have a module with 30 SQL queries in it. I don't want to assign every query to a temp variable, then log that variable, then run the query. Instead, I want to simply pass the query into the SQL library I'm using. The tag function lets me sneak a function call in between creating the string and sending it to the query - one that doesn't add more than 3 characters for each use, and as a bonus makes the IDE switch syntax modes. As such I can write all the queries in a normal natural way, and enabling debugging is done elsewhere with only a couple of lines rather than debugging logic being mixed into every single query. |
In a way, tag functions are very similar to Decorators, they essentially decorate a string rather than a class or function. They allow you to encapsulate some logic behind a simple declarative syntax, and the particulars can be worked out elsewhere. |
I agree that this is a bit off topic... |
The point is that whether or not anyone will actually use it like: const myString = String.cooked`Some ${templated} string`; The signature of the function must be that of a template tag for some of the most desirable use cases. That means it is a valid tag, and as such should make sense of for some reason someone did use it as one. And at the same time, it needs to make sense when delegated to within a custom tag, whether the custom tag is just getting the cooked string and doing something else with it, or actually modifying the interpolation behavior. That is, regardless of how the function is used, it should not have a confusing name. |
The appropriate way to sanitize each part may depend on the context of the string. You can't generally take something which operates on a template literal and transform it into something which operates only on a single string - the surrounding context is an important part of the logic. (Consider sanitizing HTML, for example: the rules for escaping attribute values are not the same as the rules for escaping element contents.) Sanitizing each part individually, without the surrounding context, isn't generally possible. So, yes, the function does in fact really need to be used as a tag directly. |
@zeel01 has you said earlier, the difficult task is to concile 2 usages that are so different... It is why I argued on the side: It wont be used as a tag so the name should reflect that... That being said, I don't want to spam this thread anymore, I apologize for this BTW ! |
A last philosophic comment: We, as developpers, and here, as specifiers are responsible of the consequences of choices we take. More that final users who will use what is available. If the simple choice of naming a future fonction would lead to a usage that kills performances for a matter of lazyness, we would have be responsible of more worldwide energy comsomption therefore more CO2 emissions in a world where global climate change matters, and where sobriety should be the path to take... |
I would hope bundlers/optimizers would remove completely no-op uses. Heck, I would hope that runtimes would be smart and realize a tag doesn't do anything and just not call it. I wouldn't worry about a hypothetical extra function call, there are many ways to avoid any runtime performance penalty without the developer needing to jump through as many hoops authoring the code. |
Unfortunately this optimism isn't justified in the current ecosystem. Very few people are running tools which will eliminate this sort of thing before shipping to production. And engines can't avoid the call because they can't know for sure that But the overhead of this is very small. Just like anything else, allowing developers to more directly express their intent is often worth the cost of this tiny amount of overhead. And there's not a better way of writing my example snippet above: even if you were willing to duplicate the whole template, that extra code has real costs as well. |
Nope! It make sense to use a name that goes in both usage, if it make sense to use it at both usage... You know that any function that takes a single string can be used as a tag right? (because an array of 1 string const sym = Symbol`foo`
const num = Number`42`
const elem = document.createElement`div`
const items = document.querySelectorAll`#my-list > li`
const $ = document.querySelectorAll // $`...` So if this spec's function as the tag signature, this is not obvious it as to reflect this usage. The fact that somebody may use it like that is not an argument for its naming. It makes sense only if we name it to be so...
function renderIntro(name, classes, needsSanitation) {
let { attr, text } = sanitizers.forHTML
if( !needsSanitation )
attr = text = o=>o
return `<div class="greetings ${attr(classes)}">Hello ${text(name)} !</div>`
} There is no more a need for a general sanitizer that have to parse the context of string to determine if it is an attribute or a text element (so less instructions, memory used etc), the template is not duplicated (no memory waste), the noop function is used (and defined in memory) only if condition tells so... |
That requires discipline on the part of every user of the sanitization APIs to confirm that they are used in the correct context every time. The whole point of tagged template literals - literally the reason they exist - is to allow you to build sanitization and similar APIs which can take into account the surrounding context, so that the author of the tag can exercise that discipline instead of requiring every single user to get it right in every single instance. |
// framework code
const html = (...args)=> (new DOMParser).parseFromString( String.cooked(...args), 'text/html' ) // not a tag usage
const sanitizedHtml = (...args)=> html( ...sanitizer(...args) ) // sanitizer has still a tag signature
// userland code
function renderIntro(name, classes, needsSanitation) {
let tag = needsSanitation ? sanitizedHtml : html
return tag`<div class="greetings ${classes}">Hello ${name} !</div>`
} PS: String.cooked = ( oneOrMoreStrings, ...props )=>
[].concat( oneOrMoreStrings )
.map( (s,i)=> s+( i in props ? props[i] : '') )
.join('') |
Yes, of course it is possible to wrap any signature so that the result is usable as a tag, and thereby not need to call it as a tag directly. That does not mean that it would not be useful to be able to call this function as a tag directly. I don't think this line of inquiry is useful so I'm going to stop engaging with it now. |
Splitting this out from #1 cause this tends to be a big topic.
options:
String.cooked
The primary appeal of this is to contrast it against
String.raw
. A potential downside to that name is that it doesn't really match the intended use case of this function: To make it easier to implement "value pre-processing" tagged template functions.String.interpolate
This function aligns a bit more with the intended usage of this function, and is overall a bit more literal about what it does internally.
String.identity
This name has been brought up before, it refers to the fact that these two are equivalent:
The downside is that using this function as the direct tag of a tagged template is not really its intended use case, and calling it within another tag makes it a somewhat weird name.
Other names
Feel free to suggest other names, but if you do, please explain what that name accomplishes and what it loses out on from the other names already suggested.
The text was updated successfully, but these errors were encountered: