11 ms·
Javascript: call functions without using parentheses
- sondh 8y ago> Infact, apart from SQORN, I’ve never seen this syntax used elsewhere. https://www.styled-components.com https://www.styled-components.com maybe?
- alephnan 8y agoAlso, https://polymer.github.io/lit-html https://polymer.github.io/lit-html https://github.com/WebReflection/hyperHTML https://github.com/WebReflection/hyperHTML
- chris_mc 8y agoNim: https://forum.nim-lang.org/t/3046 https://forum.nim-lang.org/t/3046 You can do almost anything with Nim, for function calls! I personally like UFCS (Uniform Function Call Syntax), aka: let something: int = item.function(arg2, arg3,...).function.function(vararg).etc
- noonat 8y agohttps://observablehq.com https://observablehq.com also uses this to great effect.
- kamac 8y agofacebook's relay uses it to find and compile graphql queries https://facebook.github.io/relay/docs/en/query-renderer.html https://facebook.github.io/relay/docs/en/query-renderer.html
- phs2501 8y agohttps://github.com/bdowning/sql-assassin https://github.com/bdowning/sql-assassin is something I wrote for myself that is very similar to SQORN and uses templates for the same reason. 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/4dca9f006f230bc7e8c18a2b9c95ff8e https://gist.github.com/bdowning/4dca9f006f230bc7e8c18a2b9c9...
- gyrgtyn 8y agohttps://github.com/choojs/nanohtml https://github.com/choojs/nanohtml
- abecedarius 8y agohttps://github.com/erights/quasiParserGenerator https://github.com/erights/quasiParserGenerator
- wongarsu 8y agohttps://github.com/apollographql/graphql-tag https://github.com/apollographql/graphql-tag
- brockers 8y agoCan someone tell me what possible value this has?
- deleted 8y ago[deleted]
- blktiger 8y agoLook at what this does with the feature - https://polymer.github.io/lit-html https://polymer.github.io/lit-html
- rhacker 8y agoI saw that a while back, now I know how they did that!
- robmaister 8y agoJob security
- doomslice 8y agoThey're called tagged templates: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... Apart from the other examples, I've also seen them be used for internationalization. http://i18n-tag.kolmer.net/ http://i18n-tag.kolmer.net/
- toast0 8y agoSomeone wandered in from perl5? (I say that as a person who enjoys perl)
- naranha 8y agoIt can be used for escaping values for example 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 https://github.com/felixfbecker/node-sql-template-strings
- burlesona 8y agoI 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
- phiresky 8y agoThe template strings feature is not a just "shorthand syntax", it allows you to do things that were not possible before (see [1]) [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Raw_strings https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- camelCaseOfBeer 8y agoThis 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.
- rhinoceraptor 8y agoI use tagged templates for ensuring URL paths are URI safe, like so: const path = esc`/my/api/v1/${possiblyURIUnsafeVariable}`
- jameslk 8y agotl;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.
- jolmg 8y agoI don't think it's abuse, but rather a very appropriate use of ES6 template string functions. I think the point is that you can do: > const kid = sq.from`person`.where`age < ${kids_age}` 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); } EDIT: I agree with your EDIT.
- AustinG08 8y agoIt'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."
- jameslk 8y agoIt's a nice feature, when used for template strings. Looking at the library, it just looks awkward: sq.from`book` .return`distinct author` .where({ genre: 'Fantasy' }) .where({ language: 'French' }) I can already hear the questions: "Why do some functions require parenthesis and some don't?" "When do I need to use parenthesis?" It's just unnecessarily confusing.
- DecoPerson 8y agoInterestingly, 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 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/Reference/Template_literals#Raw_strings https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- femto113 8y agoWorth noting also that there's nothing magical about the backtick being adjacent to the function, you can have whitespace inbetween, e.g func `123 ${abc}` or even func `123 ${abc}` Coupled with the vagueness around ; I expect this will make for some awesomely obfuscated JavaScript in the coming years.
- femto113 8y agoEven more nonsense possible since the tag function doesn't have to return a string, it can return a function, meaning you could chain these, e.g. function tag2(string, ...keys) { console.log("tag2", string, ...keys); return function tag1(string, ...keys) { console.log("tag1", string, ...keys); return "tag1-return-value" } } console.log(tag2`input to tag2``input to tag1`); yields tag2 [ 'input to tag2' ] tag1 [ 'input to tag1' ] tag1-return-value
- mcv 8y agoIs 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?
- LiterallyDoge 8y agoThis guy uses double quotes to make his example look more impressive and it tilts me.
- Kiro 8y agoWhy doesn't console.log `foobar` work as imagined? It gives me an array.
- fahrradflucht 8y agoTry adding interpolations to your example to make sense of this. console.log`foo${123}baz` You get an array so that you later can interleaf the parts with the interpolations you get as additional arguments.
- burlesona 8y agoThat’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.
- _xgw 8y agoThis is pretty useful for saving 2 characters in codegolfing challenges. For example: > 'string'.split('').reverse().join('') becomes > 'string'.split``.reverse().join``
- Roboprog 8y agoI 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.
- eejdoowad 8y agoSqorn 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 https://sqorn.org/docs/tutorial.html to learn about the possibilities.
- tolmasky 8y agoSincere 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.
- olooney 8y agoPutting 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.
- ct520 8y agoBarf, no.
- scriptnull 8y agoThanks 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.
- zackify 8y agoYou could call functions by accessing methods like object.test if you simply used es6 proxies. Same with extending the prototype, hardly a new concept
- losvedir 8y agoparenthesEEEEEEEEEs!
- PeakHackerNews 8y agoAll 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 https://en.m.wikipedia.org/wiki/Elvis_operator
- bdcravens 8y agoPersonally, 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)
- pavel_lishin 8y agoI agree. Ambiguity kills software readability. 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.
- Shoue 8y agoI 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.
- Retra 8y agoThere'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.
- msla 8y ago> There's a good deal of ambiguity in Haskell Obviously not.
- toomim 8y agoAnother 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.
- Me1000 8y agoYou can also invoke the function by doing foo["bar"]: https://runkit.com/me1000/5ba283ad8bb2580012c875e8 https://runkit.com/me1000/5ba283ad8bb2580012c875e8
- SwellJoe 8y ago// Call it: hello = 'michael' // Outputs "Hello michael!' This makes me uncomfortable.
- AzMoo_ 8y agoWith great power comes great responsibility. This is a situation in which the old axiom, "don't write stupid code", comes into play.
- shawn 8y agoBut stupid code is the most fun.
- exikyut 8y agoThis 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.
- mlthoughts2018 8y agoOne 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.
- emodendroket 8y agoYou're supposed to distinguish between functions with side effects and those with none.
- mlthoughts2018 8y agoWhat 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.
- emodendroket 8y agoDoesn'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.
- mlthoughts2018 8y agoBut 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.
- kumarharsh 8y agoI'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.
- gmac 8y agoA nice use for this (tagged template literals) is bringing extended RegExps to JS. https://github.com/jawj/js-xre https://github.com/jawj/js-xre
- spankalee 8y agoCalling 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 https://github.com/Polymer/lit-html
- globuous 8y agoThanks for your comment, very interesting! 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: const bar = "bar" f = (s, ...v) => console.log(s, v); f(`foo ${bar} baz`); // => foo bar baz ; [] f`foo ${bar} baz`; // => ["foo ", " baz"] ; ["bar"] 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 !
- ehsankia 8y agoI think you're confusing what's happening here? 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".
- fersho311 8y agoI 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)
- burlesona 8y agoUsing the parenthesis actually gives different behavior. That’s, I suppose, why this syntax exists at all.
- seattle_spring 8y agoThis is almost as scary as all of the people that insist on not using semicolons in JS.
- Roboprog 8y agoThe back ticks function as parens, effectively. The function receives multiple arguments, NOT a single string. Think “sprintf”. And semicolons are just noise :-)
- mmis1000 8y agoyou can call without parentheses already for those methods without parameters window.toString = window.alert; "" + window // alert popped
- iamrohitbanga 8y agoI 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()
- globuous 8y agoAs 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 ?
- techazard 8y agoIt's actually nice that it does not matter if it's a function or attribute that your calling in ruby. Check out the Uniform access principle (https://en.wikipedia.org/wiki/Uniform_access_principle https://en.wikipedia.org/wiki/Uniform_access_principle). When used correctly you can write IMHO really clean and concise OOP code where (cascading) changes are easily made.
- pavel_lishin 8y agoWhere this kills me is ambiguous commas. something = [ alpha beta, gamma ] 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.
- dragonwriter 8y agoIt's not actually ambiguous; though it is a bit of language-specific syntax to learn.
- iamrohitbanga 8y agoAgreed. 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.
- soylentgraham 8y agoWell, 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)
- galaxyLogic 8y agoAs 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?
- some1else 8y agoThe 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--Make-Macros-Instead https://jlongster.com/Stop-Writing-JavaScript-Compilers--Mak... - Babel-plugin-macros: https://github.com/kentcdodds/babel-plugin-macros https://github.com/kentcdodds/babel-plugin-macros
- portal_narlish 8y agoThis 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.
- jaunkst 8y agographql-tags does this also
- oh-kumudo 8y agoWhy? This doesn't really have too much value other than making the code more confusing.
- carc1n0gen 8y agoI think another example of a library using template strings this way is styled-components for react https://www.styled-components.com https://www.styled-components.com
- jkoudys 8y agostage 2 proposal for pipeline operator will also let you call functions without parens. 'hello world' |> console.log
- raxxorrax 8y agoAs 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...