[proposal] Function calls, syntax sugar
I like the idea, maybe we could do the following:
foo(posArg1, posArg2, name1: x, name2: y)
as syntactic sugar for:
foo(posArg1, posArg2, { name1: x, name2: y })
Axel
foo {one:1,two:2 };
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
Given:
function foo(){ return 1; }
This is a valid function call:
foo
()
// 1;
But this is valid, too (though, not a function call):
foo <-- identifier
{} <-- empty block
// function foo() { return 1; }
Also, I could be wrong, but wouldn't there need to be a way to disambiguate UnaryExpression?
typeof foo { one: 1, two: 2 }
Maybe this is not an issue, or easily dealt with?
We can add even more generic form of function call:
foo 1,2,"three";
The equivalent to:
foo(1, 2, bar("a", "b"))
is...
foo 1, 2, bar "a", "b"
?
CoffeeScript has this problem, too.
Andrew:
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
Existing grammar:
CallExpression Arguments
Arguments :
( )
( ArgumentList )
To add ObjectLiteral
, at very least the grammar would need to have a
NoLineTerminator
between CallExpression
and Arguments
, which breaks extant
code.
A slightly less ambitious suggestion:
consider f() as syntax for the implicit arguments array
(which, as of ES6, can be considered deprecated), then
make the parens in this syntax optional
In other words, you could write
f 1 // single parameter
f(1,2) // single parameter, implicit arguments pseudo-array
f [1,2] // single parameter, explicit array
Things get more interesting when you consider currying (functions returning functions):
f(1)(2) // conventional, implicit arguments pseudo-arrays
f 1 2 // paren-free, single parameters, no arrays
For your nested call example, you'd have the choice between
foo(1, 2, bar("a", "b")) //uncurried, implicit pseudo-arrays
foo[1, 2, bar["a", "b"]] // uncurried, explicit arrays
foo 1 2 (bar "a" "b") // curried, single parameters
In the latter variant, () are used for grouping, consistent with their use in the rest of the language.
Nice as this would be, I don't know whether this can be fitted into ES grammar and ASI... (probably not?).
Claus
PS. could es-discuss-owner please check their mailbox (and update the mailing list info page)?
On Fri, Jul 12, 2013 at 11:07 AM, Claus Reinke <claus.reinke at talk21.com>wrote:
A slightly less ambitious suggestion:
consider f() as syntax for the implicit arguments array (which, as of ES6, can be considered deprecated), then make the parens in this syntax optional
(snip)
In other words, you could write
f 1 // single parameter f(1,2) // single parameter, implicit arguments pseudo-array
This breaks CallExpression Arguments...
Given:
function f(a, b) {
return [a, b];
}
Currently:
f(1, 2); // [1, 2]
Whereas...
// single parameter, implicit arguments pseudo-array:
f(1, 2);
a
would be magically be treated like a ...rest param that wasn't really
an array, but instead a implicit arguments pseudo-array?
// [[1, 2], undefined]
f [1,2] // single parameter, explicit array
(snip)
For your nested call example, you'd have the choice between
foo(1, 2, bar("a", "b")) //uncurried, implicit pseudo-arrays foo[1, 2, bar["a", "b"]] // uncurried, explicit arrays
Optional parens for CallExpression Arguments or MemberExpression Arguments results in convoluted argument lists. The solutions shown above using [] also create ambiguity with:
- MemberExpression[ Expression ]
- CallExpression[ Expression ]
Given:
function foo(value) {
return value;
}
foo.prop = "Some data";
Currently:
foo("prop"); // "prop"
foo["prop"]; // "Some data"
Whereas...
foo[1, 2, bar["a", "b"]] // uncurried, explicit arrays
foo["prop"] // What does this return?
function f(a, b) { return [a, b]; }
Currently:
f(1, 2); // [1, 2]
Whereas...
// single parameter, implicit arguments pseudo-array: f(1, 2);
|a| would be magically be treated like a ...rest param that wasn't really an array, but instead a implicit arguments pseudo-array?
// [[1, 2], undefined]
No, just another way to describe the current situation, where
function f() {return [...arguments]} // pseudo code
f(1,2) // [1,2]
or, if we make the arguments explicit
function f(...arguments) {return [...arguments]} // pseudo code
f(1,2) // [1,2]
and explicit formal parameters would be destructured from arguments, so
function f(a,b) {return [a,b]}
f(1,2) // [1,2]
The solutions shown above using [] also create ambiguity with:
- MemberExpression[ Expression ]
- CallExpression[ Expression ]
Given:
function foo(value) { return value; } foo.prop = "Some data";
Currently:
foo("prop"); // "prop"
foo["prop"]; // "Some data"
ah, yes, I knew there had to be a serious flaw somewhere... sigh
Claus
On Fri, Jul 12, 2013 at 6:45 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Fri, Jul 12, 2013 at 12:22 AM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Quite often I see constructions like this:
foo({one:1,two:2});
so call of function with single parameter - object literal. Idiom named "Poor man named arguments passing"
Idea is to extend existing JS/ES syntax calls to support simple form of the call above:
foo {one:1,two:2 };
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
Given:
function foo(){ return 1; }
This is a valid function call:
foo ()
// 1;
But this is valid, too (though, not a function call):
foo <-- identifier {} <-- empty block
// function foo() { return 1; }
This construction
foo {};
is an equivalent of:
foo({});
but not
foo();
To call function with empty param list you still need empty '('')' brackets.
Also, I could be wrong, but wouldn't there need to be a way to disambiguate UnaryExpression?
typeof foo { one: 1, two: 2 }
Maybe this is not an issue, or easily dealt with?
This is not an issue. Parsed exactly the same as: typeof foo({ one: 1, two: 2 })
-- Andrew Fedoniouk.
On Fri, Jul 12, 2013 at 7:02 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Fri, Jul 12, 2013 at 12:22 AM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Quite often I see constructions like this:
foo({one:1,two:2});
so call of function with single parameter - object literal. Idiom named "Poor man named arguments passing"
Idea is to extend existing JS/ES syntax calls to support simple form of the call above:
foo {one:1,two:2 };
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
Existing grammar:
CallExpression Arguments
Arguments : ( ) ( ArgumentList )
To add ObjectLiteral, at very least the grammar would need to have a NoLineTerminator between CallExpression and Arguments, which breaks extant code.
Yes, NoLineTerminator is required for non-strict mode (that ugly semicolon elision strikes again)
Even in non-strict mode this code:
function foo(obj) { return 1; }
var c = foo { one:1 };
produces parsing error before the '{' - "semicolon required".
The same kind of error is in this case too:
function foo(p1,p2) { return 1; }
var c = foo 1,2;
So that syntax change will be backward compatible - it will not change semantic of existing valid code.
-- Andrew Fedoniouk.
On Thu, Jul 11, 2013 at 9:22 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Quite often I see constructions like this:
foo({one:1,two:2});
so call of function with single parameter - object literal. Idiom named "Poor man named arguments passing"
Idea is to extend existing JS/ES syntax calls to support simple form of the call above:
foo {one:1,two:2 };
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
This syntax extension will not break correct pre-ES6 code as far as I can tell. But I am not sure about new ES6 arrivals.
In fact we can add even more generic form of function call:
foo 1,2,"three";
that is an equivalent of foo(1,2,"three");
In this case grammar may look like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <parameter-list> // proposal
Where <parameter-list> is comma ',' separated list of expressions.
Just to reduce that bracketing noise in syntax.
I'm not a fan of this, because it only gets us part of the way toward the benefits of real named parameter support, and makes it even less likely that we'll actually get to the end. Python has it good - every argument can be given either by position or by name, and you can collect both extra positional arguments and extra named arguments. I'd prefer figuring out a syntax for argument lists that gives us the same argument-list power as Python.
On Fri, Jul 12, 2013 at 11:20 AM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
On Thu, Jul 11, 2013 at 9:22 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Quite often I see constructions like this:
foo({one:1,two:2});
so call of function with single parameter - object literal. Idiom named "Poor man named arguments passing"
Idea is to extend existing JS/ES syntax calls to support simple form of the call above:
foo {one:1,two:2 };
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
This syntax extension will not break correct pre-ES6 code as far as I can tell. But I am not sure about new ES6 arrivals.
....
Just to reduce that bracketing noise in syntax.
I'm not a fan of this, because it only gets us part of the way toward the benefits of real named parameter support, and makes it even less likely that we'll actually get to the end. Python has it good - every argument can be given either by position or by name, and you can collect both extra positional arguments and extra named arguments. I'd prefer figuring out a syntax for argument lists that gives us the same argument-list power as Python.
Adding Python'ic way of handling parameters requires substantial change of existing runtime architecture ('arguments' and around).
In contrary proposed
foo {one:1,two:2 };
requires just syntax change .
-- Andrew Fedoniouk.
On Fri, Jul 12, 2013 at 1:42 PM, Andrew Fedoniouk <news at terrainformatica.com
wrote:
On Fri, Jul 12, 2013 at 6:45 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Fri, Jul 12, 2013 at 12:22 AM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Quite often I see constructions like this:
foo({one:1,two:2});
so call of function with single parameter - object literal. Idiom named "Poor man named arguments passing"
Idea is to extend existing JS/ES syntax calls to support simple form of the call above:
foo {one:1,two:2 };
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
Given:
function foo(){ return 1; }
This is a valid function call:
foo ()
// 1;
But this is valid, too (though, not a function call):
foo <-- identifier {} <-- empty block
// function foo() { return 1; }
This construction
foo {};
is an equivalent of:
foo({});
but not
foo();
Right, I get that... but what I'm telling you is that your proposal doesn't work:
foo {}
Same as
foo({})
So, that means:
foo {}
should be the same as
foo ({})
...Because this is a valid function call.
But it's not the same and cannot be defined as the same, because ASI put a semi colon at the end of |foo| and {} is a valid empty block. Changing this would surely be web breaking.
Rick
To call function with empty param list you still need empty '('')' brackets.
Also, I could be wrong, but wouldn't there need to be a way to disambiguate UnaryExpression?
typeof foo { one: 1, two: 2 }
Maybe this is not an issue, or easily dealt with?
This is not an issue. Parsed exactly the same as: typeof foo({ one: 1, two: 2 })
Right, but again:
function foo() {return 1;}
typeof foo ({})
"number"
...Because this is the same as
typeof foo({})
So you need a [no LineTerminator here], which unnecessarily complicates:
CallExpression Arguments CallExpression[no LineTerminator here]ObjectLiteral
On Fri, Jul 12, 2013 at 2:08 PM, Andrew Fedoniouk <news at terrainformatica.com
wrote:
On Fri, Jul 12, 2013 at 7:02 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Fri, Jul 12, 2013 at 12:22 AM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Quite often I see constructions like this:
foo({one:1,two:2});
so call of function with single parameter - object literal. Idiom named "Poor man named arguments passing"
Idea is to extend existing JS/ES syntax calls to support simple form of the call above:
foo {one:1,two:2 };
Semi-formally that syntax looks like:
function-call: <name-token> '(' <parameter-list> ')' // existing form <name-token> <object-literal> // proposal
Existing grammar:
CallExpression Arguments
Arguments : ( ) ( ArgumentList )
To add ObjectLiteral, at very least the grammar would need to have a NoLineTerminator between CallExpression and Arguments, which breaks extant code.
Yes, NoLineTerminator is required for non-strict mode (that ugly semicolon elision strikes again)
Even in non-strict mode this code:
function foo(obj) { return 1; }
var c = foo { one:1 };
produces parsing error before the '{' - "semicolon required".
Of course it does, but again, you're ignoring my point about ASI. Invocation parens (and arguments list) are allowed to be on the a following line:
function foo(value) { return value; }
foo
("hi!")
// "hi!";
And again, the opening and closing curly brace synatx that you think is an ObjectLiteral becomes a valid Block on the next line (because invocation parens and argument lists CAN be on the next line):
function foo(obj) { return 1; }
var c = foo { one:1 }; <-- valid block, containing a LabelStatement // 1
c; // function foo(obj) { return 1; }
The same kind of error is in this case too:
function foo(p1,p2) { return 1; }
var c = foo 1,2;
Still the same reason I've said over several responses.
function foo(p1,p2) { return 1; }
var c = foo 1,2;
// 2 c; // function foo(obj) { return 1; }
So that syntax change will be backward compatible - it will not change semantic of existing valid code.
Contrary to the evidence I've provided several times in several messages?
On Fri, Jul 12, 2013 at 2:06 PM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Fri, Jul 12, 2013 at 1:42 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
...
This construction
foo {};
is an equivalent of:
foo({});
but not
foo();
Right, I get that... but what I'm telling you is that your proposal doesn't work:
foo {}
Same as
foo({})
So, that means:
foo {}
should be the same as
foo ({})
...Because this is a valid function call.
But it's not the same and cannot be defined as the same, because ASI put a semi colon at the end of |foo| and {} is a valid empty block. Changing this would surely be web breaking.
Seems like I am not getting that famous ASI thing.
I do not understand why here:
foo (exp);
there is no semicolon injected. It rather should be this:
foo; (exp);
if that ASI thing has any traces of logic behind.
BTW: what about "use strict" ? Does it govern ASI parsing rules? If not then why?
On Fri, Jul 12, 2013 at 3:55 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Seems like I am not getting that famous ASI thing.
I do not understand why here:
foo (exp);
there is no semicolon injected. It rather should be this:
foo; (exp);
if that ASI thing has any traces of logic behind.
It has a very simple logic, just not the one you're assuming.
You're probably thinking that the rule is "if a line doesn't end in a semicolon, and you can insert one without causing this line or the next to have a parse error, do so". The actual rule is "if a line doesn't end in a semicolon, attempt to join it with the following line. If that causes a syntax error, insert a semicolon and try again".
In other words, ASI only happens when a semicolon is required, not when one is possible.
BTW: what about "use strict" ? Does it govern ASI parsing rules? If not then why?
No, strict mode has no effect on ASI.
On Fri, Jul 12, 2013 at 4:17 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
On Fri, Jul 12, 2013 at 3:55 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Seems like I am not getting that famous ASI thing.
I do not understand why here:
foo (exp);
there is no semicolon injected. It rather should be this:
foo; (exp);
if that ASI thing has any traces of logic behind.
It has a very simple logic, just not the one you're assuming.
You're probably thinking that the rule is "if a line doesn't end in a semicolon, and you can insert one without causing this line or the next to have a parse error, do so". The actual rule is "if a line doesn't end in a semicolon, attempt to join it with the following line. If that causes a syntax error, insert a semicolon and try again".
In other words, ASI only happens when a semicolon is required, not when one is possible.
Your hypothesis would be true if not this case:
return { a:1 };
Why it injects ';' after the return? This
return { a:1 };
is perfectly valid construction.
BTW: what about "use strict" ? Does it govern ASI parsing rules? If not then why?
No, strict mode has no effect on ASI.
Too bad IMO.
-- Andrew Fedoniouk.
On Jul 12, 2013, at 5:09 PM, Andrew Fedoniouk wrote:
Your hypothesis would be true if not this case:
return { a:1 };
Why it injects ';' after the return? This
Because, the actual ECMAScript grammar says a new line can't occur between the 'return' keyword and the optional return expression. www.ecma-international.org/ecma-262/5.1/#sec-12.9
You really need to read and understand the relevant portions of the specification before engaging in this sort of discussion. www.ecma-international.org/ecma-262/5.1/#sec-7.9
On Fri, Jul 12, 2013 at 8:09 PM, Andrew Fedoniouk <news at terrainformatica.com
wrote:
On Fri, Jul 12, 2013 at 4:17 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:
On Fri, Jul 12, 2013 at 3:55 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
Seems like I am not getting that famous ASI thing.
I do not understand why here:
foo (exp);
there is no semicolon injected. It rather should be this:
foo; (exp);
if that ASI thing has any traces of logic behind.
It has a very simple logic, just not the one you're assuming.
You're probably thinking that the rule is "if a line doesn't end in a semicolon, and you can insert one without causing this line or the next to have a parse error, do so". The actual rule is "if a line doesn't end in a semicolon, attempt to join it with the following line. If that causes a syntax error, insert a semicolon and try again".
In other words, ASI only happens when a semicolon is required, not when one is possible.
Your hypothesis would be true if not this case:
Tab's response is not a hypothesis, it's a generalization of the specification. ASI rules are defined, unambiguously, in the ECMAScript spec.
return { a:1 };
Why it injects ';' after the return? This
return { a:1 };
is perfectly valid construction.
For the same reason I've mentioned several times in this thread: a [no LineTerminator here], which exists between return and the optional Expression:
return [no LineTerminator here] Expression (optional);
On Sat, Jul 13, 2013 at 11:16 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
On Fri, Jul 12, 2013 at 8:09 PM, Andrew Fedoniouk <news at terrainformatica.com> wrote:
On Fri, Jul 12, 2013 at 4:17 PM, Tab Atkins Jr. <jackalmage at gmail.com> wrote: ....
In other words, ASI only happens when a semicolon is required, not when one is possible.
Your hypothesis would be true if not this case:
Tab's response is not a hypothesis, it's a generalization of the specification. ASI rules are defined, unambiguously, in the ECMAScript spec.
return { a:1 };
Why it injects ';' after the return? This
return { a:1 };
is perfectly valid construction.
For the same reason I've mentioned several times in this thread: a [no LineTerminator here], which exists between return and the optional Expression:
return [no LineTerminator here] Expression (optional);
OK, what about this definition then
[name] [no LineTerminator here] [literal object declaration];
So this:
foo.bar { one:1, two:1 };
will be parse as a method call: foo.bar {one:1,two:1};
And this:
foo.bar { one:1, two:1 };
as in case of 'return' will be parsed with ASI in effect:
foo.bar; { one:1, two:1, };
Any other discussions about attempts to transform Java/C/C++/D grammar (where '\n' is just a space) to FORTRAN/BASIC-derived line oriented grammars (Python/Ruby/Lua/etc) I'll left for off-list conversations.
On Sat, Jul 13, 2013 at 10:49 PM, Andrew Fedoniouk < news at terrainformatica.com> wrote:
OK, what about this definition then
[name] [no LineTerminator here] [literal object declaration];
So this:
foo.bar { one:1, two:1 };
will be parse as a method call: foo.bar {one:1,two:1};
And this:
foo.bar { one:1, two:1 };
as in case of 'return' will be parsed with ASI in effect:
foo.bar; { one:1, two:1, };
This is very different to the return
case in terms of cognitive load.
Associating return
with "ah, no ASI here" is much simpler than having to
be on the lookup for ASI exceptions almost everywhere.
Also, return
at least returns, no matter what. Your proposal would make
the newline relevant in an entirely new way: by making it the difference
between having a statement (the foo.bar
) that's effect-less in most cases
(i.e., if foo.bar
isn't an effect-ful getter) followed by a block, and a
function call with the block as an argument.
Any other discussions about attempts to transform Java/C/C++/D grammar
(where '\n' is just a space) to FORTRAN/BASIC-derived line oriented grammars (Python/Ruby/Lua/etc) I'll left for off-list conversations.
Nobody's attempting to do anything of the sort - the ASI rules have been unchanged for a long time.
Quite often I see constructions like this:
foo({one:1,two:2});
so call of function with single parameter - object literal. Idiom named "Poor man named arguments passing"
Idea is to extend existing JS/ES syntax calls to support simple form of the call above:
foo {one:1,two:2 };
Semi-formally that syntax looks like:
This syntax extension will not break correct pre-ES6 code as far as I can tell. But I am not sure about new ES6 arrivals.
In fact we can add even more generic form of function call:
foo 1,2,"three";
that is an equivalent of
foo(1,2,"three");
In this case grammar may look like:
Where
<parameter-list>
is comma,
separated list of expressions.Just to reduce that bracketing noise in syntax.