Javascript: call functions without using parentheses

musikele | 308 points

Calling a function without parens is probably the _least_ interesting part of tagged template literals.

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:

    const templateCache = new WeakMap();
    
    const html = (strings, ...values) => {
      let template = templateCache.get(strings);
      if (template === undefined) {
        template = makeTemplate(strings); // this might be expensive
        templateCache.set(strings, template);
      }
      return makeDOM(template, values);
    };
    
    const header = (title) => html`<h1>${title}</h1>`;
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

spankalee | 6 years ago

Sqorn author here.

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.

eejdoowad | 6 years ago

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:

    Object.defineProperty(window, 'bar', {get: foo})

    // Call it:
    bar                 // Outputs "Hello!"
To pass arguments, use a setter instead:

    Object.defineProperty(window, 'hello', {set:
        function (x) {console.log('Hello '+x+'!')}
    })

    // Call it:
    hello = 'michael'   // Outputs "Hello michael!'
To support multiple arguments, pass an array or hash.
toomim | 6 years ago

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

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...

DecoPerson | 6 years ago

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

burlesona | 6 years ago

> Infact, apart from SQORN, I’ve never seen this syntax used elsewhere.

https://www.styled-components.com maybe?

sondh | 6 years ago

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)

bdcravens | 6 years ago

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.

kumarharsh | 6 years ago

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.

scriptnull | 6 years ago

Can someone tell me what possible value this has?

brockers | 6 years ago

This is pretty useful for saving 2 characters in codegolfing challenges.

For example:

> 'string'.split('').reverse().join('')

becomes

> 'string'.split``.reverse().join``

_xgw | 6 years ago

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.

mlthoughts2018 | 6 years ago

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)

soylentgraham | 6 years ago

I feel where Ruby's optional parenthesis syntax really shines is in method call chaining.

e.g.

    employees.flat_map(&:addresses).map(&:city).uniq.sort
    # in place of
    employees().flat_map(&:addresses).map(&:city).uniq().sort()
iamrohitbanga | 6 years ago

The pitfall being that this approach only works with string arguments. Having some Ruby experience, I don't like any of the JavaScript DSLs using template strings for function invocation so far (like styled-components and graphql), and would rather just use macros:

- Sweet.js: https://jlongster.com/Stop-Writing-JavaScript-Compilers--Mak...

- Babel-plugin-macros: https://github.com/kentcdodds/babel-plugin-macros

some1else | 6 years ago

This is a common paradigm in object-functional programming, as it further emphasizes the duality of functions and the data they return. Scala allows one to do this, however one could ostensibly pack a bunch of side effects into a method and call it this way. Stylistically, these situations are supposed to be differentiated. myObject.method() should be reserved for effectful functions, and myObject.method for pure functions.

portal_narlish | 6 years ago

You could call functions by accessing methods like object.test if you simply used es6 proxies. Same with extending the prototype, hardly a new concept

zackify | 6 years ago

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)

fersho311 | 6 years ago

This is almost as scary as all of the people that insist on not using semicolons in JS.

seattle_spring | 6 years ago

I think another example of a library using template strings this way is styled-components for react https://www.styled-components.com

carc1n0gen | 6 years ago

As noted somewhere the template functions can return anything not just strings. They can return functions.

Now my question: Isn't this the same as ability to define macros in other high-level languages like Lisp for instance?

galaxyLogic | 6 years ago

Why doesn't console.log `foobar` work as imagined? It gives me an array.

Kiro | 6 years ago

Why?

This doesn't really have too much value other than making the code more confusing.

oh-kumudo | 6 years ago

As someone with a german key layout, who is already used to pain when it comes to parenthesis, this back-tick stuff is on a whole new level...

raxxorrax | 6 years ago

A nice use for this (tagged template literals) is bringing extended RegExps to JS. https://github.com/jawj/js-xre

gmac | 6 years ago

stage 2 proposal for pipeline operator will also let you call functions without parens.

    'hello world' |> console.log
jkoudys | 6 years ago

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.

jameslk | 6 years ago

you can call without parentheses already for those methods without parameters

window.toString = window.alert;

"" + window // alert popped

mmis1000 | 6 years ago

graphql-tags does this also

jaunkst | 6 years ago

All of this syntactic fluff and Tower of Babelification, and not a soul has thought to implement useful syntax tricks like the Elvis operator.

https://en.m.wikipedia.org/wiki/Elvis_operator

PeakHackerNews | 6 years ago

parenthesEEEEEEEEEs!

losvedir | 6 years ago

Barf, no.

ct520 | 6 years ago

This guy uses double quotes to make his example look more impressive and it tilts me.

LiterallyDoge | 6 years ago