I've also used them for DSLs, i.e. the following grammar compiler (where the actions are literal functions defined in the grammar string as follows), sadly never released:
It's just a tiny language feature, removing the parenthesis is not the point of the template literal. FWIW, I like the way it looks for styled-components:
I also thought this looked crazy the first time I saw it.
I feel like ES6+ has jumped the shark in a bunch of ways. Adding new language features can be nice, and shorthand syntax is sometimes convenient, but JS now has a bunch of inconsistent rules about what you can and can’t do syntactically.
These make for neato script tricks to impress your programmmer friends (“wait you can do THAT?!) but I think it has mostly just made the language harder to read.
But don’t worry we have build tools to fix it that will pick a single syntax for your team! /s
This is honestly my least favorite part of js and the reason I use ts. ES6 has so many syntax tricks that sometimes I delve into a new codebase and it's almost like reading a different language. Code golf is fun, I get it, but sometimes you gotta code for the next developer that'll be maintaining your shit.
tl;dr it's just abusing ES6's template string functions
> const kid = sq.from`person`.where`age < 13`
Which just looks like a clever way to confuse people of what's going on. Try explaining why this works to someone new to JavaScript. Not to mention the maintenance nightmare this creates. Don't do this.
EDIT:
After others mentioned it, I understand now that for SQORN it is being used to escape SQL parameters and this does seem valid.
However, the OP brings it up as a novelty to drop parenthesis from functions, and for that I definitely think it's abuse without any value. This is not like Ruby's optional parenthesis: it can only be used for template literals and it makes your function implementation confusing.
It's a nice feature when used appropriately. Article says at the bottom in bold: "It’s a nice-to-know feature, good for impressing others, but my suggestion is to use this only if it’s the clearest way to express your concepts."
You're framing this as a question of syntax preference, but actually the whole point of template tags is to cater to a very specific need: the ability to sanitize an interpolated value.
In this specific example, let's say you have:
sql.from`book`.return`distinct ${field}`
You don't want a sql injection to occur if somehow `field = 'author'; drop table book; --` or similar.
With a plain function call, the library would have no way of knowing what to sanitize.
fixture `Getting Started`// declare the fixture
.page `https://devexpress.github.io/testcafe/example`; // specify the start page
//then create a test and place your code there
test('My first test', async t => {
await t
.typeText('#developer-name', 'John Smith')
.click('#submit-button')
// ...
});
I spent ages going through their docs trying to find out why they specify their two main functions differently (`fixture` using tagged templates and `test` as a regular functions). In the docs it mentions "doesn't matter which way you do it". It's just there to confuse / impress.
and it will be safe from SQL injection, because `where` is probably defined something like:
function where(strings, ...interpolations) {
var result = "";
for (var i = 0; i < interpolations.length; i++) {
result += strings[i] + this.sql_quote(interpolations[i]);
}
result += strings[interpolations.length];
return this.add_to_query(result);
}
Interestingly, func`123 ${abc}` is not syntactic sugar for func(`123 ${123}`) .
The latter interpolates the values into the template and passes the final string to the function as a single argument.
The former acts as shown in the article, passing the string pieces as an array and the value pieces as additional arguments. This allows SQORN to prevent SQL injection while building queries. See first FAQ on https://sqorn.org/docs/faq.html
Is that not a dangerous way to prevent SQL injection? What happens if someone calls it with parentheses? Will that throw an error or will it bypass the SQL injection prevention?
That’s actually the point, when you omit the parentheses the behavior changes, instead of interpolating the string and passing that to the function, the parts of the string get passed in instead.
I was ready to hate on it too, until I learned (in the comments here, NOT the article) that this is a special syntax for doing something like a C var-args function - first a template parameter, then the variable list of values.
The function invoked does NOT simply receive the evaluated string, it gets the template and can decide what to do with it, or not, including how to encode/“escape” insertion values or other such issues.
Sincere question: doesn't the use of template strings increase the possibility of injection where none would theoretically be possible in the non-string version of the API? Not super familiar with the intended purpose of sqorn, hence the real question, but I think part of the goal of query builders can be to very specifically constrain the possible generated output statically.
So, for example in your example above, if I had tied the age parameter to some input box, it is now theoretically up to the caller to sanitize age and make sure the user doesn't type "10 || 1 = 1" or something. This as opposed to say, doing .where(less_than(identifier("age"), input)), where you can absolutely know that if input is not an int you can safely throw. I am basically making the same injection question that's existed forever which is that if you deal at the coarseness of string parsing, you lose the information of what is user generated and what is programmer generated.
Putting a function in front of a backtick string replaces normal string introplation: instead of the default behavior, the function before the backtick string gets passed the literal pieces of the template string and ${} values explicitly and separately. Instead of returning an interpolated string, the function can return whatever it wants. In sqorn's case, it returns an Object with separate keys for sql and and values. When the time comes to actually execute a query, sqorn passes these objects to database backends with positional placeholders in the SQL ($1, $2, $3, etc.) and the values as positional bound parameters. This is a safe way to prevent SQL injection.
Thanks for mentioning about Sqorn. When I read about this feature a few weeks back, I was thinking hard for a good use case of tagged template literals. Now, I get it.
I agree with your closing statement completely.
> It’s a nice-to-know feature, good for impressing others, but my suggestion is to use this only if it’s the clearest way to express your concepts.
Personally, I often use parentheses in languages like Ruby where avoiding them is in vogue. Perhaps it's just a personal preference, but I feel it communicates better (which is what source code is for)
I'm currently learning Elixir at a new job, and I really, really dislike the (what I think of as) the ruby-style where `return` statements and function call parentheses are optional.
I don't know Ruby or Elixir's way of doing this, but I assume it's not too dissimilar to in Haskell and I'd argue there isn't much ambiguity -- the spaces are enough to discern arguments once you get used to it, and if your call gets too large maybe it's time to consider binding the arguments so it's clearer what's being passed to the function anyway.
Same thing for implicit return statements, when they're implicit it just takes getting used to the fact that the last statement in the block returns -- I've never really had trouble reading code because of the lack of function call parentheses or explicit return statements but I guess in a language where it's optional it can be a bit more unexpected versus a language like Haskell where this style is encouraged more strictly.
There's a good deal of ambiguity in Haskell, but people tend to resolve it with operators like '$'. But I'd argue that it works very well in Haskell due to the fact that whitespace as function application and automatic currying go together well to make an elegant syntax. Lacking those features, it might be better to stick to the standard parenthetical notation.
A colleague convinced me to look at this a different way (although for Ruby as opposed to Elixir). Functions always return a value. This is different than some languages where there is a distinction between a procedure and a function. In Ruby, if you don't call "return", then the function returns the return value from the last statement. We get fooled into thinking that we are returning "nothing", but it's still just returning the value from the last statement -- which is hopefully nil.
To be consistent, we could choose to always add "return" to the last line of every function -- because that's what it's doing. But this is a completely redundant bit of typing once you know what the language is doing. Having that "return" on the last line of every single function is not helpful in the least. Instead we can define "return" to be a statement that you only use when you are returning early from a function.
Since he pointed that out, languages that force me to type "return" at the bottom of functions bug me :-) As for parentheses, I'm mostly in agreement. Omitting parentheses is mostly useful when you are writing a DSL where you want to obfuscate that what you are doing is actually a function call. In Ruby land, Rails uses this effectively. However, it's a double edged sword because I have seen junior developers who do not understand that this is normal Ruby and a function call. They just think it is "Rails Magic". I'm of mixed feelings on the matter. I use it occasionally when I feel that it helps express the intent of my DSL rather than the plumbing, but I always feel a bit uneasy when I do it.
There’s a principle of OOP called the “uniform access principle” (articulated by Bertrand Meyer, among others) that holds that a class should not leak whether a value is computed or is just a field on the object. That way, you don’t need getters/setters because, if you want to change the storage of a class member, you can just change it from being a data member to a method of vice versa. This helps avoid the explosion of getters and setters that characterize a certain sort of Java project.
Another way to call a function without parentheses is with an ES5 getter (or ES6 proxy):
// The function we want to call
function foo () { console.log('Hello!') }
// ES5 getter:
x = {}
Object.defineProperty(x, 'foo', {get: foo})
// Call it:
x.foo // Outputs 'Hello!'
What if we want to say "foo" instead of "x.foo"? Put a getter on window:
This should ideally trigger XHR back to the server (preferably async, with no error checking or retry) which then invokes a SELECT for nonexistant rows on a schema with a bunch of query triggers attached that store the queried-for data after first performing an HTTP request (from inside the DB, in a stored procedure) to translate the data to another language. (The translation service does not provide an API; the stored procedure extracts the response out of the HTML using a combination of 13 regular expressions and 2 substr() calls.) The original un-translated data should not be stored.
One of the worst syntactical aspects of Scala is that you can define methods which cannot be called with invocation parentheses. Why anyone would ever want this is beyond my understanding.
What about methods that have arguments and no side-effects, like a special getter that accesses a value in a Map of Maps? or functions that flush state to I/O while doing other stuff and returning a value?
Why only make a special weird distinction for arg-less methods? And why enable different ways of defining the method that can optionally require you to omit parentheses rather than choose to do it only when you choose to write a function that embodies that convention?
If you want a way to indicate side effects, make it a part of the type system like in Haskell. Such a silly convention is unhelpful and I think fails even at the intended goal because it confuses people thinking it’s not involving the overhead of a function call.
Doesn't seem that different from the C# concept of properties, except with a bit less language support. To me it doesn't seem that worth worrying about the overhead of trivial function calls in the general case.
But they often aren’t trivial, e.g. like breeze conversions to Scala data structures that appear like simple attribute accesses and don’t indicate at all that really they are complex function calls with important performance considerations.
I’m well aware. It’s just a bad concept. Knowing that something behaves as a function invocation is extremely important. The idea that this knowledge should not have consequences for the mental model of the caller is a bad idea. Setting up a design that achieves this (where, from the caller’s perspective, attributes vs side-effectful argumentless methods appear the same) results in a bad design.
I see this a lot with Named Principles and Design Patterns. They have Capitalized Names and Entries on Wikipedia so people believe they are Pragmatically Successful and Deserve Respect.
But they are usually just junk. Things like Liskov, all kinds of nominal design patterns, mischaracterizations of least astonishment, etc.
A good heuristic is to ignore all that garbage and just use common sense.
I'd like to point out that Relay also uses this template literal approach to write graphql queries. Then there is styled-components which lets users write css inside js and bind it to components using template literals.
It's not a "niche" usage, rather one of the raison d'etre of the feature. Even the MDN docs point this out quite early in the text.
That being said, when you say that no-parenthesis are the least important feature of templated strings, they seem to be mandatory for "deconstructing" them via function arguments:
So it's not so much that "you can call functions without parents in some cases" or "they're not that nuts of a feature", rather than "if you want to deconstruct templated strings, you must call your function without parenthesis"
Which means that sometimes, there's just no other way around.
The first example is doing a normal string interpolation, and then passing the result to a function. The latter is doing template literals, which is a whole other feature. You're not really "running a function without parenthesis".
So you're not running a function when I write "f`some ${foo} lols`" ? What's f then ? This is HN and I'm a self taught noob, so probabilistically I'm wrong and you're right. But I'm having such a hard time believing it.
To me, it's still running the sequence of instructions defined by f in both cases. Just that the arguments passed to f are different whether it's called with parenthesis or not. And as far as I understand, that's the feature that comes with string literals. But you're still calling f, and f is still a function. It's defined as a function and executed as a function.
"If there is an expression preceding the template literal (tag here), this is called a "tagged template". In that case, the tag expression (usually a function) gets called with the processed template literal, which you can then manipulate before outputting." - [1]
Now when i open my console (FF) and I type:
a = 12
a`hello`
I get "TypeError: a is not a function". But if I do
b = () => "yo"
b`hello`
I get, as expected, "yo" and no TypeError.
Furthermore, I didn't know this feature, the article called it "functions without using parenthesis" so i called it like that.
N.B. I'd like to know what the interpreter / JIT is doing now ;p
Eventually, f is called as a function, but in the syntax tree, it's not a direct "function call". The obvious way to see that is, as you can see in your example, "hello" isn't passed as an argument. Instead, there's an intermediate form internally that does some magic, and calls the function with some other parameters.
Maybe I was being a little pedantic, but yeah, f`test` and f(`test`) are two very different operations under the hood.
That's correct. But the article is making a subtly different comparison, which the parent is responding to. Specifically, with one string-literal argument, f`hi` and f(`hi`) are nearly identical from the perspective of the caller, as long as you deconstruct the extra array in the function body.
This is cute, but since you're limited to literal strings, you can't push this very far before using a regular parenthesized function is better again.
I found out about this feature while using GraphQL. When going through Apollo's docs I saw a bunch of gql`templateString`, which is used for defining typeDefs. The first time I saw that, I was so confused. Personally, I think it would be more clear if they just did gql(templateString)
As a non-Ruby programmer, this sounds like it makes it non-obvious whether you're calling a class' method or attribute. Do you end up relying on your editor to help you out ?
I have no idea if I just invoked alpha(beta, gamma) and put the result into a single-member array, or if I've got an array with two elements - alpha(beta) and gamma.
Agreed. It is the "idiomatic" way of writing code in Ruby. I love Java but writing lambda expressions in Java with deeply nested parenthesis makes the code really ugly and less readable esp with chaining.
As far as I understand, Ruby style is writing the code in the most elegant way to increase readability and (hopefully) reduce chances of bugs.
Every langauge with native support for property setters and getters has the same problem. C# has decent IDE support, in JS we pretend it never happens, Ruby is at least upfront
Python has the `@property` decorator, which effectively disguises method calls as attribute access. To me, that's as bad, if not worse, than the ruby code posted above.
Oh thank god, we saved ourselves six characters! And the only thing we sacrificed is knowing for sure whether we're accessing an object's property, or calling a method!
Well, lack of parenthesis is just horrid (flashbacks to Visual Basic), but kinda very happy to see one of the most useful things in php (and Delphi I think?) finally arrive in a current language!
I've always thought I only wanted it in a high level language, but now... I think I want it in c++ too... (just for debug of course, any pro knows user facing strings stay outside for localisation)
114 comments
[ 2.5 ms ] story [ 171 ms ] threadhttps://www.styled-components.com maybe?
https://polymer.github.io/lit-html
https://github.com/WebReflection/hyperHTML
You can do almost anything with Nim, for function calls! I personally like UFCS (Uniform Function Call Syntax), aka:
https://facebook.github.io/relay/docs/en/query-renderer.html
I've also used them for DSLs, i.e. the following grammar compiler (where the actions are literal functions defined in the grammar string as follows), sadly never released:
https://gist.github.com/bdowning/4dca9f006f230bc7e8c18a2b9c9...
Apart from the other examples, I've also seen them be used for internationalization. http://i18n-tag.kolmer.net/
var query = sql`select * from user where id=${id}`
Similar for html, preventing injection. You just need to implement the corresponding functions.
For example https://github.com/felixfbecker/node-sql-template-strings
var query = sql(`select * from user where id=${id}`);
I feel like ES6+ has jumped the shark in a bunch of ways. Adding new language features can be nice, and shorthand syntax is sometimes convenient, but JS now has a bunch of inconsistent rules about what you can and can’t do syntactically.
These make for neato script tricks to impress your programmmer friends (“wait you can do THAT?!) but I think it has mostly just made the language harder to read.
But don’t worry we have build tools to fix it that will pick a single syntax for your team! /s
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
> const kid = sq.from`person`.where`age < 13`
Which just looks like a clever way to confuse people of what's going on. Try explaining why this works to someone new to JavaScript. Not to mention the maintenance nightmare this creates. Don't do this.
EDIT:
After others mentioned it, I understand now that for SQORN it is being used to escape SQL parameters and this does seem valid.
However, the OP brings it up as a novelty to drop parenthesis from functions, and for that I definitely think it's abuse without any value. This is not like Ruby's optional parenthesis: it can only be used for template literals and it makes your function implementation confusing.
"Why do some functions require parenthesis and some don't?" "When do I need to use parenthesis?"
It's just unnecessarily confusing.
In this specific example, let's say you have:
You don't want a sql injection to occur if somehow `field = 'author'; drop table book; --` or similar.With a plain function call, the library would have no way of knowing what to sanitize.
And without template tags, the API would arguably look more complex, and require the user to discover/learn an ad-hoc interpolation DSL: You can still target the template tag's raw API requirements without the syntax (though you'd lose readability with multiple interpolations):> const kid = sq.from`person`.where`age < ${kids_age}`
and it will be safe from SQL injection, because `where` is probably defined something like:
EDIT: I agree with your EDIT.The latter interpolates the values into the template and passes the final string to the function as a single argument.
The former acts as shown in the article, passing the string pieces as an array and the value pieces as additional arguments. This allows SQORN to prevent SQL injection while building queries. See first FAQ on https://sqorn.org/docs/faq.html
You can also do some interesting stuff with the special ".raw" property of the string array passed. See here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
For example:
> 'string'.split('').reverse().join('')
becomes
> 'string'.split``.reverse().join``
The function invoked does NOT simply receive the evaluated string, it gets the template and can decide what to do with it, or not, including how to encode/“escape” insertion values or other such issues.
Sqorn doesn't use tagged template literals to be cool. It uses them to make writing SQL flexible, secure and intuitive.
You can write:
const boy = sq.from`person`.where`age < ${7} and gender = ${'male}`
to produce the query:
{ text: 'select * from person where age < $1 and gender = $2', args: [7, 'male'] }.
All methods have both template and non-template string forms you can choose between. For example, the query above could also be written as:
sq.from('person').where({ maxAge: sq.l`age < ${7}`, gender: 'male' })
or even as:
sq`person``age < ${7} and gender = ${'male}`
or for those who like plain old SQL:
sq.l`select * from person where age < ${7} and gender = ${'male'}`
Read the tutorial at https://sqorn.org/docs/tutorial.html to learn about the possibilities.
So, for example in your example above, if I had tied the age parameter to some input box, it is now theoretically up to the caller to sanitize age and make sure the user doesn't type "10 || 1 = 1" or something. This as opposed to say, doing .where(less_than(identifier("age"), input)), where you can absolutely know that if input is not an int you can safely throw. I am basically making the same injection question that's existed forever which is that if you deal at the coarseness of string parsing, you lose the information of what is user generated and what is programmer generated.
I agree with your closing statement completely.
> It’s a nice-to-know feature, good for impressing others, but my suggestion is to use this only if it’s the clearest way to express your concepts.
https://en.m.wikipedia.org/wiki/Elvis_operator
I'm currently learning Elixir at a new job, and I really, really dislike the (what I think of as) the ruby-style where `return` statements and function call parentheses are optional.
Same thing for implicit return statements, when they're implicit it just takes getting used to the fact that the last statement in the block returns -- I've never really had trouble reading code because of the lack of function call parentheses or explicit return statements but I guess in a language where it's optional it can be a bit more unexpected versus a language like Haskell where this style is encouraged more strictly.
Obviously not.
To be consistent, we could choose to always add "return" to the last line of every function -- because that's what it's doing. But this is a completely redundant bit of typing once you know what the language is doing. Having that "return" on the last line of every single function is not helpful in the least. Instead we can define "return" to be a statement that you only use when you are returning early from a function.
Since he pointed that out, languages that force me to type "return" at the bottom of functions bug me :-) As for parentheses, I'm mostly in agreement. Omitting parentheses is mostly useful when you are writing a DSL where you want to obfuscate that what you are doing is actually a function call. In Ruby land, Rails uses this effectively. However, it's a double edged sword because I have seen junior developers who do not understand that this is normal Ruby and a function call. They just think it is "Rails Magic". I'm of mixed feelings on the matter. I use it occasionally when I feel that it helps express the intent of my DSL rather than the plumbing, but I always feel a bit uneasy when I do it.
Why only make a special weird distinction for arg-less methods? And why enable different ways of defining the method that can optionally require you to omit parentheses rather than choose to do it only when you choose to write a function that embodies that convention?
If you want a way to indicate side effects, make it a part of the type system like in Haskell. Such a silly convention is unhelpful and I think fails even at the intended goal because it confuses people thinking it’s not involving the overhead of a function call.
I see this a lot with Named Principles and Design Patterns. They have Capitalized Names and Entries on Wikipedia so people believe they are Pragmatically Successful and Deserve Respect.
But they are usually just junk. Things like Liskov, all kinds of nominal design patterns, mischaracterizations of least astonishment, etc.
A good heuristic is to ignore all that garbage and just use common sense.
It's not a "niche" usage, rather one of the raison d'etre of the feature. Even the MDN docs point this out quite early in the text.
The key features of tagged template literals are:
* The tag function receives the string literals parts and values separately, before interpolation.
* The tag function can return anything, not just a string.
* The string literals are passed as a single argument (a TemplateObject) which is immutable and identical across multiple calls to the tag.
The last one is the kicker. It means that you can do something like this:
And now, no matter how many times you call header(), the work to create the template is only done once because the strings is stable as a cache key.Which is exactly what lit-html does to implement fast HTML templating : https://github.com/Polymer/lit-html
That being said, when you say that no-parenthesis are the least important feature of templated strings, they seem to be mandatory for "deconstructing" them via function arguments:
So it's not so much that "you can call functions without parents in some cases" or "they're not that nuts of a feature", rather than "if you want to deconstruct templated strings, you must call your function without parenthesis"Which means that sometimes, there's just no other way around.
Cheers !
The first example is doing a normal string interpolation, and then passing the result to a function. The latter is doing template literals, which is a whole other feature. You're not really "running a function without parenthesis".
To me, it's still running the sequence of instructions defined by f in both cases. Just that the arguments passed to f are different whether it's called with parenthesis or not. And as far as I understand, that's the feature that comes with string literals. But you're still calling f, and f is still a function. It's defined as a function and executed as a function.
"If there is an expression preceding the template literal (tag here), this is called a "tagged template". In that case, the tag expression (usually a function) gets called with the processed template literal, which you can then manipulate before outputting." - [1]
Now when i open my console (FF) and I type:
I get "TypeError: a is not a function". But if I do I get, as expected, "yo" and no TypeError.Furthermore, I didn't know this feature, the article called it "functions without using parenthesis" so i called it like that.
N.B. I'd like to know what the interpreter / JIT is doing now ;p
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Maybe I was being a little pedantic, but yeah, f`test` and f(`test`) are two very different operations under the hood.
This is cute, but since you're limited to literal strings, you can't push this very far before using a regular parenthesized function is better again.
And semicolons are just noise :-)
window.toString = window.alert;
"" + window // alert popped
e.g.
When used correctly you can write IMHO really clean and concise OOP code where (cascading) changes are easily made.
As far as I understand, Ruby style is writing the code in the most elegant way to increase readability and (hopefully) reduce chances of bugs.
Or is my example something that a good Ruby developer never write at all?
“Attributes” in Ruby are just methods that do attribute-like things, so there is no ambiguity: it is always a method.
I've always thought I only wanted it in a high level language, but now... I think I want it in c++ too... (just for debug of course, any pro knows user facing strings stay outside for localisation)