Block lambda is cool, its syntax isn't
On Thu, Jan 12, 2012 at 4:26 PM, Thaddee Tyl <thaddee.tyl at gmail.com> wrote:
I have heard many fellow programmers say that, while block lambdas would be welcome, Ruby's |a, b| syntax is ugly. I thought I was the only one feeling this way, but since I am not, I would like to ask why the parentheses cannot be used there.
{ (a, b) a + b }
cannot conflict with any existing program; at least I don't see how it can.
And if people choose to use newlines in their code?
{(a, b) a + b}
... already has a meaning.
On Jan 12, 2012, at 1:26 PM, Thaddee Tyl wrote:
I have heard many fellow programmers say that, while block lambdas would be welcome, Ruby's |a, b| syntax is ugly. I thought I was the only one feeling this way, but since I am not, I would like to ask why the parentheses cannot be used there.
{ (a, b) a + b }
cannot conflict with any existing program; at least I don't see how it can.
but it is ambiguous with other currently valid statement blocks such as:
{ (a, b) + b}
or
{ (a,b) a+b }
Am I wrong if I say there not a bigger issue with block lambda than with the current object notation on the matter?
I mean, does that code mean anything useful?
function() {
{|a,b| a+b};
}
If not (as it seems to me), it means that a block lambda will not be used as a statement by itself. If it's the case, it should defined as an Expression only, where there's no anonymous block to conflict the syntax. That solution has been chosen for object notation in the past. That way,
function() {
{
(a, b)
a.add(b)
}
}
would still be an anonymous block where
function() {
asyncAction(..., { (a, b) a.add(b); } }
}
would be a block lambda as an argument of an async function. No semantic change for an identical syntax, in to strict ES5.
The case where you would like to use a block lambda as a stament can be resolved by adding parenthesis, like with the current object notation. And since I still continue to hope we'll ditch the unprefixed anonymous block in some future revision of ES, that very small edge case could vanish at the same time.
Does it seems possible/acceptable?
-----Message d'origine---
On Thu, Jan 12, 2012 at 11:23 PM, François REMY <fremycompany_pub at yahoo.fr> wrote:
Am I wrong if I say there not a bigger issue with block lambda than with the current object notation on the matter?
I mean, does that code mean anything useful?
function() { {|a,b| a+b}; }
If not (as it seems to me), it means that a block lambda will not be used as a statement by itself. If it's the case, it should defined as an Expression only, where there's no anonymous block to conflict the syntax. That solution has been chosen for object notation in the past. That way,
function() { { (a, b) a.add(b) } }
would still be an anonymous block where
function() { asyncAction(..., { (a, b) a.add(b); } } }
would be a block lambda as an argument of an async function. No semantic change for an identical syntax, in to strict ES5.
The case where you would like to use a block lambda as a stament can be resolved by adding parenthesis, like with the current object notation. And since I still continue to hope we'll ditch the unprefixed anonymous block in some future revision of ES, that very small edge case could vanish at the same time.
Does it seems possible/acceptable?
Looks like a great idea to me!
François REMY <mailto:fremycompany_pub at yahoo.fr> January 12, 2012 2:23 PM Am I wrong if I say there not a bigger issue with block lambda than with the current object notation on the matter?
Please continue :-).
I mean, does that code mean anything useful?
function() { {|a,b| a+b}; }
(You need a name for that function if it is a declaration, and from the context you show, it is.)
Does this perfectly valid JS mean anything useful?
function f() { (function (a, b) { return a + b; }); }
Nevertheless, it is legal. JS follows C (not Java) in allowing seemingly useless expression-statements. This can be a source of bugs. It is also required in some cases, namely when the function expression (in parentheses) is the completion value of a Program. In such a case that value could be the wanted result of eval or an eval-like host API.
If not (as it seems to me), it means that a block lambda will not be used as a statement by itself. If it's the case, it should defined as an Expression only,
Expression is already a kind of statement, via ExpressionStatement. Furthermore, an ExpressionStatement already cannot start with a left curly brace. Please read the grammar:
12.4 Expression Statement
Syntax
ExpressionStatement : [lookahead ? {{, function}] Expression ;
NOTE An ExpressionStatement cannot start with an opening curly brace because that might make it ambiguous with a Block. Also, an ExpressionStatement cannot start with the function keyword because that might make it ambiguous with a FunctionDeclaration.
where there's no anonymous block to conflict the syntax.
This is the conflict you're looking for.
That solution has been chosen for object notation in the past. That way,
function() { { (a, b) a.add(b) } }
That is already valid ES1-6.
Others have already replied, but the problem with redefining ( after { without untenable newline sensitivity is it is a backward-incompatible change.
On Jan 12, 2012, at 2:23 PM, François REMY wrote:
Am I wrong if I say there not a bigger issue with block lambda than with the current object notation on the matter?
I think you're correct. An ExpressionStatment can not begin with a { so,without changing that rule, there should be no potential for ambiguity between Block and BlockLambda. The only issue is distinguishing ObjectLiteral and BlockLambda and from that perspective a {( is as good as a {| as neither is ambiguous with existing ObjectLiteral syntax,
At that point, it becomes a matter of taste. Which of {|x| x.foo()} or {(x) x.foo()} looks better to you? I find the | | more pleasant and easer for my eyes to pick out, but I'm undoubtably influenced by many years of reading and writing Smalltalk code.
On Thu, Jan 12, 2012 at 5:52 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
On Jan 12, 2012, at 2:23 PM, François REMY wrote:
Am I wrong if I say there not a bigger issue with block lambda than with the current object notation on the matter?
I think you're correct. An ExpressionStatment can not begin with a { so,without changing that rule, there should be no potential for ambiguity between Block and BlockLambda. The only issue is distinguishing ObjectLiteral and BlockLambda and from that perspective a {( is as good as a {| as neither is ambiguous with existing ObjectLiteral syntax,
An additional consideration: for a block lambda's completion value to be another block lambda, the {( syntax would require parenthesis, while the {| syntax would not:
let succ = {(x) {(s) {(z) s(x(s)(z)) }}};
With the grammar François is suggesting, this would be a syntax error (I think), since the body of a block lambda is a StatementList_opt. The {| syntax, on the other hand, can accommodate block lambdas as expression statements, though, given another token of lookahead (again, I think).
On Jan 12, 2012, at 3:41 PM, Jon Zeppieri wrote:
An additional consideration: for a block lambda's completion value to be another block lambda, the {( syntax would require parenthesis, while the {| syntax would not:
No, since a Statement can not begin with a { character parenthesis would be required in using either syntax. Also note that they would also be required if completion value of the BlockLambda is provided by an object literal:
{| a,b | ({x: a,y: b})} //syntax error if parenthesis around object literal are removed
...
With the grammar François is suggesting, this would be a syntax error (I think), since the body of a block lambda is a StatementList_opt. The {| syntax, on the other hand, can accommodate block lambdas as expression statements, though, given another token of lookahead (again, I think).
With two token look-ahead a | BlockLambda could be allowed at the head of a statement, but it still would not enable unparenthesized object literals at the head of a statement. It seems like a cognitive burden to allow one but not the other. A simple "{ at the start of an expression statement must be parenthesized" rules seems easiest to learn and remember.
On Thu, Jan 12, 2012 at 7:11 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
On Jan 12, 2012, at 3:41 PM, Jon Zeppieri wrote: ...
With the grammar François is suggesting, this would be a syntax error (I think), since the body of a block lambda is a StatementList_opt. The {| syntax, on the other hand, can accommodate block lambdas as expression statements, though, given another token of lookahead (again, I think).
With two token look-ahead a | BlockLambda could be allowed at the head of a statement, but it still would not enable unparenthesized object literals at the head of a statement. It seems like a cognitive burden to allow one but not the other. A simple "{ at the start of an expression statement must be parenthesized" rules seems easiest to learn and remember.
Allen
That's a good point, but I think that unparenthesized block lambdas might be worth this complication. I hadn't considered object literals, at all, while writing the previous message, and I don't think that was simply an oversight. Object literals and blocks have a similar syntax but dissimilar meaning. Block lambdas, on the other hand, are semantically kinda-sorta like blocks. You don't need to parenthesize nested blocks in JS code. (In fact, you can't.) That leads me to expect unparenthesized block lambdas.
I guess it depends, to some extent, whether you think block lambdas and object literals have more in common with each other (both being first-class values) than block lambdas and blocks do (both being lists of statements).
... continuing with my sent-too-soon message:
function() {
asyncAction(..., { (a, b) a.add(b); } }
}
You're right, this could be done. Allen's right that aesthetics matter, and so (aesthetic sensibilities in various people were formed by these) do older languages. Ruby uses || not (). Smalltalk uses | but differently, and of course does not use {}.
Apart from aesthetics, I argue that || are better because they are different from the overloaded () pair, which mean expression grouping in the nearby context of the body of the block-lambda, e.g. a.add()b; above. () also are used around formal parameter lists. And of course they are used for control structure heads, e.g. if (foo).
Rather than overloading () yet again, for a different formal parameter list context (block-lambda parameter list after {), which is immediately adjacent to an expression-statement contexts (the block-lambda body), I believe we should use ||.
The Ruby precedent is another reason, and a particular reason to use | instead of some other candidate. But I'm mainly trying to persuade you here that () is not the best choice just because it brackets formal parameter lists for functions.
On Thu, Jan 12, 2012 at 4:26 PM, Thaddee Tyl <thaddee.tyl at gmail.com> wrote:
I have heard many fellow programmers say that, while block lambdas would be welcome, Ruby's |a, b| syntax is ugly. I thought I was the only one feeling this way, but since I am not, I would like to ask why the parentheses cannot be used there.
{ (a, b) a + b }
cannot conflict with any existing program; at least I don't see how it can.
I agree. While Ruby and Smalltalk are useful to continue mining for ideas, they shouldn't be a source of syntax, as they're not super-popular. Using || to denote an argument list looks bizarre to my eyes, and I suspect also to the eyes of every single person who's used a C-like language but not Ruby.
I'm not wedded to {(a,b) a + b} for blocks either, but it's at least an improvement over {|a,b| a + b}. I wouldn't mind something simple like "block(a,b){a+b}" or even "do(a,b){a+b}", though those probably run into ambiguity issues.
On Jan 12, 2012, at 2:23 PM, François REMY wrote:
If not (as it seems to me), it means that a block lambda will not be used as a statement by itself. If it's the case, it should defined as an Expression only, where there's no anonymous block to conflict the syntax. That solution has been chosen for object notation in the past. That way,
function() { { (a, b) a.add(b) } }
would still be an anonymous block where
function() { asyncAction(..., { (a, b) a.add(b); } } }
would be a block lambda as an argument of an async function. No semantic change for an identical syntax, in to strict ES5.
So simple, and so brilliant. I love it!
I must say, the parens look much prettier to me, and they're of course more consistent with JS precedent.
It does help to write a reasonable chunk of code to compare syntaxes for uses of a feature "in the wild." Does some enterprising es-discusser wanna take a big JS program with a bunch of little anonymous functions, swap them out with block lambdas, and make two gists with the two different syntaxes? That'd be super helpful.
And since I still continue to hope we'll ditch the unprefixed anonymous block in some future revision of ES, that very small edge case could vanish at the same time.
I don't understand what you're referring to here -- what do you mean by "unprefixed anonymous block?"
Does it seems possible/acceptable?
Lovely.
From the perspective of someone who writes JS every day, the parens look
and feel "right".
Dave, you asked for some example code, this is actually from task.js:
This one is from ecto/duino/blob/master/lib/board.js and has more "real world" uses
Sorry, thought about it more and I'm raining on your (and dherman's) parade :-(.
In an expression but not statement context,
... { (x) e; }
is already a valid JS program if e begins with (, [, +, or - (the last two intended as unary operators but becoming binary).
Note that | bracketing avoids this problem. In no case can JS of any extant version have a legal sequence {|.
But because {( is already allowed, what comes after the closing ) can be the continuation of a parenthesized expression.
Even if you don't buy my "better to look different because not function" argument, this tilts the balance.
Sigh. Never mind, I'm thinking of statement not expression context
The correspondence principle is a big change for the body, so even if we could reserve block or abuse do, making the special form look like function (params) { body } is a mistake. 'return' does not return from the block, it returns from the enclosing function.
I'm not in love with Ruby syntax but {(a, b) a + b} isn't as distinctive and clearly a lambda-like thing. You're right about that, but turning back to function-based syntax with a different introductory keyword goes the wrong direction. This is why I keep coming back to "different syntax is a virtue". The correspondence-principle-based semantics are different enough.
We have three coarse-grained alternatives:
- ? (params) { body }
1a. One problem is what should we use for ?? 'lambda', 'block', etc. are not reserved the syntax. Whatever the identifier (including Greek lambda: ?), this is an incompatible change.
1b. Another problem: this form looks like a function expression with a different introductory keyword, but Tennent's Correspondence Principle makes the meaning of body elements including |this| and return/break/continue radically different. Different-enough semantics should have different syntax.
- { (params) body }
Putting the parameter list in parentheses is more consistent with function syntax, while putting the parameters on the inside stresses the block-ness over function-ness. By block-ness I mean (ignoring let, const, and function in Harmony) how code and {code} are equivalent.
Of course this cuts against the syntax too: block statements are not first-class callables whose code bodies are evaluated only if invoked, they are statements evaluated eagerly when reached. This syntax is arguably too block-like.
A point that I remember having come up in past TC39 meetings: if we ever want structuring forms (object literals or destructuring patterns) that use () for grouping property names, this syntax is future hostile.
- { |params| body }
This is new-to-JS, idiomatic block-like -- but not too block-statement-like -- syntax for novel body semantics.
The bars will drive some people to distraction. Others will grok, or already do via Ruby. This is not to favor Ruby, just to build on the belief that some precedentin language design is better than none.
We could reject (3) in favor of (2) if we had an overriding non-aesthetic reason. I don't see one.
Aesthetics vary. The more conventional look of (2) seems like a usability hazard to me, which trumps aesthetics. Again I'd rather have something more different-looking to signal the novel application of TCP.
I'm not in love with Ruby syntax but {(a, b) a + b} isn't as distinctive and clearly a lambda-like thing. You're right about that, but turning back to function-based syntax with a different introductory keyword goes the wrong direction. This is why I keep coming back to "different syntax is a virtue". The correspondence-principle-based semantics are different enough.
Two more pro-pipe points: block lambdas (lambda blocks?) will often be arguments of functions: foo( {|x,y| x + y } ) foo( {(x,y) x + y } )
With parens, you are starting to look a bit like Lisp... ;-)
I also find that pipes stick out better as something alien. Parens feel too much like someone has forgotten to add a semicolon, inside a normal block: gist.github.com/d2c8fcbab5a3da4c4dae (Rick Waldron’s Gist comparing parens and pipes)
But man does that code profit from block lambdas – really nice to look at. In some cases, things would fit into a single line where you were forced to split into multiple lines without BLs.
Axel Rauschmayer <mailto:axel at rauschma.de> January 12, 2012 10:23 PM
Two more pro-pipe points: block lambdas (lambda blocks?) will often be arguments of functions: foo( {|x,y| x + y } ) foo( {(x,y) x + y } )
With parens, you are starting to look a bit like Lisp... ;-)
Agreed, although note that you can go paren-free in the call:
foo {|x,y| x+y}
But there will be paren-ful call cases.
I also find that pipes stick out better as something alien. Parens feel too much like someone has forgotten to add a semicolon, inside a normal block: gist.github.com/d2c8fcbab5a3da4c4dae (Rick Waldron’s Gist comparing parens and pipes)
Yup.
But man does that code profit from block lambdas – really nice to look at. In some cases, things would fit into a single line where you were forced to split into multiple lines without BLs.
It's not just the line savings -- the lack of 'function'deadwood and 'return' just to have a result value win too. (I didn't see any TCP-based control effects.)
I tend to prefer the parentheses because pipes are not "logical" for arguments and, worse, they are very difficult to type on many keyboard layouts. On mine it's AltGr+1. I tend to use it very fewly.
But I'm not saying my personal taste is universal.
On Fri, Jan 13, 2012 at 12:20 AM, Brendan Eich <brendan at mozilla.org> wrote:
We have three coarse-grained alternatives:
If you are going to enumerate all potential alternatives (including ones that you need to be rejected for one reason or another), then a CoffeeScript inspired syntax should be included:
(params) -> {body}
- Sam Ruby
I have an obvious bias towards 1 because I think Unicode symbols describe much better the underlying intentions and semantics than their ASCII alternatives (usually), however not many people creates mappings for these symbols on their .XCompose (or even have a compose key handy).
I remember you mentioning that the Haskell-ish alternative \x, y { body }' or
.\x, y { body }' was not practical, because \ is already a
valid escaping character in identifiers (?)
Given those above, despite my usual disliking of Ruby syntax, I prefer
the pipes over parenthesis, given the latter has an already too
overloaded semantics, such that { (foo, bar) (2) }' would look pretty confusing, at least to me. However, what about
{x, y: body}'? Granted
we don't allow labels immediately following a lambda block (and I'm
not sure labels are used much in the language, I could be wrong
however).
2012/1/13 Brendan Eich <brendan at mozilla.org>:
hello world!
On 01/12/2012 09:09 PM, David Herman wrote:
It does help to write a reasonable chunk of code to compare syntaxes for uses of a feature "in the wild." Does some enterprising es-discusser wanna take a big JS program with a bunch of little anonymous functions, swap them out with block lambdas, and make two gists with the two different syntaxes? That'd be super helpful.
i think this would be very helpful too [although, sadly, since i am overcommitted as it is, it will not be me].
2012/1/13 Quildreen Motta <quildreen at gmail.com>:
I have an obvious bias towards 1 because I think Unicode symbols describe much better the underlying intentions and semantics than their ASCII alternatives (usually), however not many people creates mappings for these symbols on their .XCompose (or even have a compose key handy).
I remember you mentioning that the Haskell-ish alternative
\x, y { body }' or
.\x, y { body }' was not practical, because \ is already a valid escaping character in identifiers (?)Given those above, despite my usual disliking of Ruby syntax, I prefer the pipes over parenthesis, given the latter has an already too overloaded semantics, such that
{ (foo, bar) (2) }' would look pretty confusing, at least to me. However, what about
{x, y: body}'? Granted we don't allow labels immediately following a lambda block (and I'm not sure labels are used much in the language, I could be wrong however).
That would mean that a block lambda with no parameters is
{: body}
Don't you think it looks odd?
Plus, parentheses are what JS uses for parameters.
Arrows are not a live proposal because TC39 wants to keep LR(1) parsing for grammar validation (checking for ambiguities). We do not have consensus to change and there's no champion working on a credible alternative.
I could have dredged them up but they really are not happening, whereas we have recurring and live proposals for the three body-plans I listed.
Quildreen Motta <mailto:quildreen at gmail.com> January 13, 2012 5:30 AM Given those above, despite my usual disliking of Ruby syntax, I prefer the pipes over parenthesis, given the latter has an already too overloaded semantics, such that
{ (foo, bar) (2) }' would look pretty confusing, at least to me. However, what about
{x, y: body}'?
Parameters have optional guards (in the future), default value initialisers, destructuring patterns and so on. Using : is no good because, while we might manage to keep the grammar unambiguous, it is too visually light and is not a close-bracket. It's already used in ?: and labels.
Granted we don't allow labels immediately following a lambda block (and I'm not sure labels are used much in the language, I could be wrong however).
Consider { x, y=a?b:c : body }.
On Jan 13, 2012, at 6:09 AM, john saylor wrote:
hello world!
On 01/12/2012 09:09 PM, David Herman wrote:
It does help to write a reasonable chunk of code to compare syntaxes for uses of a feature "in the wild." Does some enterprising es-discusser wanna take a big JS program with a bunch of little anonymous functions, swap them out with block lambdas, and make two gists with the two different syntaxes? That'd be super helpful.
i think this would be very helpful too [although, sadly, since i am overcommitted as it is, it will not be me].
I've updated a version of my ES.next Smalltalk collections implementation experiment to use block lambdas. This version is allenwb/ESnext-experiments/blob/master/ST80collections-exp0-blp.js The one that you should compare it to is allenwb/ESnext-experiments/blob/master/ST80collections-exp0.js
As you might expect, this new version is even closer in form to the original Smalltalk code. In particularly, I was able to eliminate all the artifacts that needed to be introduced because of function expressions don't have Tennent's Correspondence. In the source, I've tagged the lines where TCP is using /TCP/
FWIW, After working through the example gists that I created last night, my opinion changed about the look of Block Lambda's with pipes. As others on the list have said previously and is now very clear to me, pipes provide a very distinct visual cue "this is a block lambda, expect X to happen here" where the {(args) expr } gets lost; these visual cues are important when you're reading and writing "real world" programs in JavaScript.
On 01/13/2012 03:36 PM, Rick Waldron wrote:
As others on the list have said previously and is now very clear to me, pipes provide a very distinct visual cue "this is a block lambda, expect X to happen here" where the {(args) expr } gets lost; these visual cues are important when you're reading and writing "real world" programs in JavaScript.
yes, i had the same impression [thx to those who supplied code examples].
one problem i came across in this domain is that a block lambda with no arguments is visually identical to a boolean or ['||']. context, i believe, will clarify this; but from a purely visual perspective, two adjacent pipe characters are almost archetypal for the boolean or operator [if i can speak about archetypes in computer programming].
The { and } around the || make it pretty clear the || is not a boolean. Also you can put a space in {| |}. But mainly, as with any alternative bracketing system that uses an infix operator, we are relying on the leading { to make it clear that the immediately following delimiter, | in the proposal, cannot represent its usual infix role (| or ||).
Just a thought, has a prefix ^ been considered?
A lambda that takes no arguments could be: ^{ /.../ } And a lambda with some arguments: ^(x,y){ /.../ }
I just find this really readable, the syntax for lambdas that take no arguments is delightfully minimal (just one extra character!), my brain is well trained to expect argument lists to be delimited by parentheses so I find this much easier to understand, and I find the ^ character pleasingly evocative of λ. :-)
On Wed, Jan 18, 2012 at 10:28 AM, Gavin Barraclough <barraclough at apple.com>wrote:
Just a thought, has a prefix ^ been considered?
A lambda that takes no arguments could be: ^{ /.../ } And a lambda with some arguments: ^(x,y){ /.../ }
I just find this really readable, the syntax for lambdas that take no arguments is delightfully minimal (just one extra character!), my brain is well trained to expect argument lists to be delimited by parentheses so I find this much easier to understand, and I find the ^ character pleasingly evocative of λ. :-)
This is really a matter of taste.
{ (a, b) ... }
^(a, b) { ... }
{ |a, b| ... }
Of course, some characters are easier to type than others, especially in non-qwerty keyboards (I'm thinking about the pipe "|").
But what matters most is readability, since code is more often read than written. So far, people seem to think that pipes are more readable.
Yes, we've been over many prefixes, from Greek lowercase lambda to \ to ^. Two problems, one specific to ^, the other general to the body plan you propose:
-
^ is taken as the bitwise XOR operator. So there's an "ASI didn't happen but the user expected it" hazard motivating us to restrict any operand (vs. operator) context overloading of ^ to start a lambda to have [no LineTerminator here] on the left of the operand-^. Otherwise valid JS of the form
x = y ^{z: w}
is too easily confused for an assignment expression statement (terminated by an automatically inserted semicolon, the mistaken reader thinks -- there could be comments and lots of blank lines between the two lines shown above) followed by a lambda.
Even with the above being forbidden by [no LineTerminator here] to the left of the lambda' productions right hand side, allowing x ^ ^{y} smells bad. But perhaps not as bad as pipes on the inside -- YMMV.
- The TCP conformance makes anything like function (params) {body} an anti-pattern. Changing function to ^ does not avoid this problem. We want block-lambdas to look different from functions.
(2) is an overriding objection in my view.
On Jan 18, 2012, at 1:28 AM, Gavin Barraclough wrote:
Just a thought, has a prefix ^ been considered?
A lambda that takes no arguments could be: ^{ /.../ } And a lambda with some arguments: ^(x,y){ /.../ }
Alas ^ is syntactically ambiguous in the non-argument case: foo ^{...}
A person could have a legitimate reason to xor a variable with an object.
:-/
Note that this in no way indicates an endorsement of block lambdas, which i still believe to be Of The Devil :D
On Jan 18, 2012, at 2:09 AM, Brendan Eich wrote:
x = y ^{z: w}
On Jan 18, 2012, at 9:05 AM, Oliver Hunt wrote:
Alas ^ is syntactically ambiguous in the non-argument case: foo ^{...}
The ambiguity here doesn't seem particularly troubling. Under ASI this parses in a sensible fashion. This seems unlikely to cause any confusion in real usage, since it only effects an object literal as an operand to a bitwise operator.
On the other hand, reuse of | seems somewhat more awkward. Based on the block lambda revival proposal, I believe: {|x = (a&b)| x} {|x = (a&&b)| x} {|x = a&b| x} Are all valid block lambdas, but: {|x = a&&b| x} Isn't. Allowing an arbitrary subset of infix operators in initializer expressions seems an unfortunate wart, and potentially confusing to users of the language.
On Jan 18, 2012, at 2:09 AM, Brendan Eich wrote:
- The TCP conformance makes anything like function (params) {body} an anti-pattern. Changing function to ^ does not avoid this problem. We want block-lambdas to look different from functions.
(2) is an overriding objection in my view.
That's a fair point. For my tastes reusing | as delimiters to the parameter lists is a step too far away from function-like syntax, particularly if it restricts an arbitrary subset of infix operators from initializers.
I'd suggest changing the terminator for the parameter list to something other than | ... {|: foo() } {|x,y: foo(x,y) } But I'd hate myself if I did. ;-)
G.
On Jan 18, 2012, at 10:36 AM, Gavin Barraclough wrote:
On Jan 18, 2012, at 2:09 AM, Brendan Eich wrote:
x = y ^{z: w}
On Jan 18, 2012, at 9:05 AM, Oliver Hunt wrote:
Alas ^ is syntactically ambiguous in the non-argument case: foo ^{...}
The ambiguity here doesn't seem particularly troubling. Under ASI this parses in a sensible fashion.
It doesn't
a ^ b
does not get any ASI, and is parsed (correctly) as a^b
This seems unlikely to cause any confusion in real usage, since it only effects an object literal as an operand to a bitwise operator.
I dislike the | mode quite a lot, the only obvious reason for that proposal existing is some peoples current love of ruby :) Why not use the C++ lambda syntax? or the MSVC lambda syntax? or the haskell syntax? etc, etc (Obj-C uses the ^ that we've already demonstrated is ambiguous :D )
Oliver Hunt <mailto:oliver at apple.com> January 18, 2012 11:37 AM
On Jan 18, 2012, at 10:36 AM, Gavin Barraclough wrote:
This seems unlikely to cause any confusion in real usage, since it only effects an object literal as an operand to a bitwise operator.
I dislike the | mode quite a lot, the only obvious reason for that proposal existing is some peoples current love of ruby :)
Not so -- I do not love Ruby. Also, this is essentially an ad hominem argument.
Why not use the C++ lambda syntax?
Because we cannot use (params) { body } without a restricted production and the result looking too much like a function, or run-together expression and block-statement. We've been over this.
Block-lambdas have significantly different semantics due to TCP conformance. They ought to look different.
I dislike the | mode quite a lot, the only obvious reason for that proposal existing is some peoples current love of ruby :) Why not use the C++ lambda syntax? or the MSVC lambda syntax? or the haskell syntax? etc, etc (Obj-C uses the ^ that we've already demonstrated is ambiguous :D )
Sounds like material for an FAQ: syntaxes that have been rejected + rationales.
On Jan 18, 2012, at 11:41 AM, Brendan Eich wrote:
Oliver Hunt <mailto:oliver at apple.com> January 18, 2012 11:37 AM
On Jan 18, 2012, at 10:36 AM, Gavin Barraclough wrote:
This seems unlikely to cause any confusion in real usage, since it only effects an object literal as an operand to a bitwise operator.
I dislike the | mode quite a lot, the only obvious reason for that proposal existing is some peoples current love of ruby :)
Not so -- I do not love Ruby. Also, this is essentially an ad hominem argument.
Many apologies, the ':)' was meant to imply that a knew that that wasn't a valid argument
Why not use the C++ lambda syntax?
Because we cannot use (params) { body } without a restricted production and the result looking too much like a function, or run-together expression and block-statement. We've been over this.
Block-lambdas have significantly different semantics due to TCP conformance. They ought to look different.
I guess. Maybe the problem here is that I don't like the block-lambda concept itself. I'll mull on i, and harass you at whatever magical location where meeting at tomorrow :D
Maybe the problem here is that I don't like the block-lambda concept itself.
I’m curious: What do you dislike?
Oliver Hunt <mailto:oliver at apple.com> January 18, 2012 11:45 AM On Jan 18, 2012, at 11:41 AM, Brendan Eich wrote:
Not so -- I do not love Ruby. Also, this is essentially an ad hominem argument.
Many apologies, the ':)' was meant to imply that a knew that that wasn't a valid argument
Sure, I saw that -- just trying to stick up for myself as a non-lover of Ruby.
I think TCP is worth including in JS in a new special form. We've been talking about this since 2008 at least:
esdiscuss/2008-November/008216
Anything we on this front is a new special form. We won't make a migration barrier or dialect/version fork and inflict TCP on functions. So then the issue is what concrete syntax might be best.
Looking at other languages is one way to hedge against the proven ability of any language designer to mistake the original for the good. Ruby's syntax has some basis in Smalltalk, but not much. Ruby is known to some JS developers but not all. There's some win here, but not decisive win.
Independent of Ruby, we've argued about other syntaxes, most recently {(params) body}, and everyone I saw champion that syntax came back to pipes to better distinguish the new special form from both functions and juxtaposed parenthesized expressions and block statements.
The fact that block-lambdas use the same {|params| body} syntax as Ruby in what I'm proposing is thus not an argument from authority (Matz's or other Rubyist's). The proposed syntax does not have anything to do with "love of Ruby" as in "willing the good of Ruby" (to use a sound definition of "love"). It has everything to do with loving JS assuming we want a TCP-conforming lambda special form.
To be fair, after a long reflection, I have been concluding that no other proposal beat {|| ...} for block lambda at this time. Also, I’ve found some use cases where block-lambda are actually useful (something I was really curious about before thinking about it). In the future, they may even be used a lot by transpilers to emulate new language constructs in a ES-friendly way.
The main point I dislike with the current proposal is that it makes block-lambda used at places where I think they should not. It’s not because they are useful to solve some problems they are useful to fix all. Most of the time, when we want a lambda, we don’t want TCP. We want a new function. Just and only that. Since we want a new function, we are requesting function-like semantics. It’s logical.
An exemple:
function A() {
let ret = {|x| return x; };
let inc = {|x| x+1 };
let j = 0;
while (true) {
if (j > 3) ret(j);
j = inc(j);
}
}
I don’t think “inc” should be a block-lambda. It can be, if someone do that, I would not bother modifying the code. But it’s not the natural way to think. What we need here is not an “extension” of the current function (which is a block lambda is), we need a new function.
The problem is that the current syntax is too long :
let inc = function(x) { return x+1; }
I think (but I don’t say it’s what should be done, it’s up to debate) that alongside block-lambda, we should introduce a ‘canonical’ lambda proposal, that would just be syntaxic sugar for plain old functions.
The syntax I propose would be:
‘@(‘ + <argument-list> + ‘)’ + <statement>;
// or if too complex
‘@(‘ + <argument-list> + ‘)’ + ‘{‘ + <statement> + ‘}’;
That would desugar to:
(function lambda_lineX_colY_fileZ(<argument-list>) {
return do {
<statement>
};
}).bind(this);
That would allow the following samples :
let inc = @(x) x+1;
array.map(@(x) x+1);
array.map(@(x) {
if(x.hasKey(‘a’)) {
x.get(‘a’)
} else {
undefined
}
});
and the more complex ones :
array.map(@(x) {
while(x.parentNode) {
x=x.parentNode;
if(x.tagName==’DIV’) { return x; }
}
console.log(‘An element that has no parent DIV has been found.’);
return undefined;
});
Please note that the last one seems very complex to write using a block-lambda :
array.map({|x| let returnValue;
while(x.parentNode) {
x=x.parentNode;
if(x.tagName==’DIV’) { returnValue=x; break; }
}
if(returnValue) {
returnValue;
} else {
console.log(‘An element that has no parent DIV has been found.’);
undefined;
}
});
That proposal does offer some middle ground between
- ‘functions’ (classical functions)
- ‘lambdas’ which are local functions that are not meant to go outside the scope of an action
- ‘block-lambdas’ which are internal functions that are not meant to go outside the scope of a function scope.
Anyone can use what’s the most convenient for him, I think there’s room for all those things. François
From: Axel Rauschmayer Sent: Wednesday, January 18, 2012 8:48 PM To: Oliver Hunt Cc: Brendan Eich ; es-discuss Steen Subject: Re: Block lambda is cool, its syntax isn't Maybe the problem here is that I don't like the block-lambda concept itself.
I’m curious: What do you dislike?
François REMY <mailto:fremycompany_pub at yahoo.fr> January 18, 2012 12:54 PM To be fair, after a long reflection, I have been concluding that no other proposal beat {|| ...} for block lambda at this time. Also, I’ve found some use cases where block-lambda are actually useful (something I was really curious about before thinking about it). In the future, they may even be used a lot by transpilers to emulate new language constructs in a ES-friendly way.
Indeed -- and it's good to read this paragraph.
The main point I dislike with the current proposal is that it makes block-lambda used at places where I think they should not. It’s not because they are useful to solve some problems they are useful to fix all. Most of the time, when we want a lambda, we don’t want TCP. We want a new function.
I'm not sure about "most", but you are right that we don't want one hammer for all nails. We have function already. It wants shorter syntax (some say). Providing block-lambdas does not help there, and may tempt misuse of block-lambda where function is better, just because of block-lambda's conciseness.
Just and only that. Since we want a new function, we are requesting function-like semantics. It’s logical. An exemple: function A() { let ret = {|x| return x; }; let inc = {|x| x+1 }; let j = 0; while (true) { if (j > 3) ret(j); j = inc(j); } } I don’t think “inc” should be a block-lambda. It can be, if someone do that, I would not bother modifying the code. But it’s not the natural way to think.
The TCP does help here, IMHO -- no 'return'.
What we need here is not an “extension” of the current function (which is a block lambda is), we need a new function.
Yet strawman:arrow_function_syntax is in decline. But more below on that.
The problem is that the current syntax is too long : let inc = function(x) { return x+1; }
It's not just the length -- the 'return' and arguably even the braces are too much for an expression-bodied function.
I think (/but I don’t say it’s what should be done, it’s up to debate/) that alongside block-lambda, we should introduce a ‘canonical’ lambda proposal, that would just be syntaxic sugar for plain old functions.
Let's try not to use "lambda" two different ways, one of them not TCP. Lambda calculus and LISP fans want TCP.
The syntax I propose would be: ‘@(‘ + <argument-list> + ‘)’ + <statement>;
Two problems:
- The semicolon can't be required, and if it isn't, then an Expression or AssignmentExpression statement as body inverts precedence fatally, because a function expression is a PrimaryExpression (ES6 fix, ES1-5 had it as MemberExpression, unobservably different and still high precedence). See
Adapting to your proposal:
function f() {return "f"} var x = 3; g = -> (a) a ? f : x++(1);
To fix (1), we want not simply a shorthand for high-precedence FunctionExpression, rather a new low-precedence production, as in arrow_function_syntax:
AssignmentExpression : ArrowFunctionExpression ...
Parenthesization will be required to use this kind of expression with higher precedence operators than , and = but those are rare operators with which to join any kind of function expression to other operators, with the possible exception of ( Arguments ) to invoke immediately. Immediate invocation would require parenthesizing the callee.
- @ is wanted for private names. The precedent in Ruby and CoffeeScript for instance variables is strong.
// or if too complex ‘@(‘ + <argument-list> + ‘)’ + ‘{‘ + <statement> + ‘}’;
Definitely no ; at end here -- but this one is ok because the braces avoid precedence inversion.
To fix (2), how about reviving arrows and avoiding the non-LR(1) parsing for grammar validation problem?
ArrowFunctionExpression : -> ArrowFormalParameters_opt AssignmentExpression -> ArrowFormalParameters_opt ArrowBodyBlock_opt
ArrowFormalParameters : ( FormalParameterList_opt ) ( this Initialiser_opt ) ( this Initialiser_opt , FormalParameterList )
Someone with nick satyr mentioned this to me on irc.mozilla.org #jslang just recently, referencing Ruby 1.9 and Perl6.
If this looks good I can edit strawman:arrow_function_syntax and remove the words in it that set it up as a mutually exclusive alternative to strawman:block_lambda_revival.
Please note that the last one seems very complex to write using a block-lambda : array.map({|x|let returnValue; while(x.parentNode) { x=x.parentNode; if(x.tagName==’DIV’) { returnValue=x; break; } } if(returnValue) { returnValue; } else { console.log(‘An element that has no parent DIV has been found.’); undefined; } });
Good point. Function wins due to early return, we've talked about extensions to block-lambdas to help but they still lose IMHO.
That proposal does offer some middle ground between
- ‘functions’ (classical functions)
- ‘lambdas’ which are local functions that are not meant to go outside the scope of an action
Again, 'lambda' is a misnomer, both for losing TCP and because you are simply providing shorter syntax (a function-rose by any other name would smell as sweet).
What's more, you are distinguishing syntax not semantics: the two bullets above are semantically equivalent. One from syntax in the language today, the second induced by shorter syntax (I propose revived arrow-in-front function syntax).
- ‘block-lambdas’ which are internal functions that are not meant to go outside the scope of a function scope.
Generally downward funarg only, yes. Still as you say some uses such as 'inc' might get away with escaping and be called later, without error.
Anyone can use what’s the most convenient for him, I think there’s room for all those things.
I agree, since the shorter function syntax is "just syntax".
What do you think of prefixing -> to revive arrow function syntax?
On Jan 19, 2012, at 8:27 , Brendan Eich wrote:
To fix (2), how about reviving arrows and avoiding the non-LR(1) parsing for grammar validation problem?
Couldn’t one always freeze such a function shorthand and then get syntax such as:
#(x, y) { x + y }
Axel Rauschmayer <mailto:axel at rauschma.de> January 19, 2012 12:06 AM
Couldn’t one always freeze such a function shorthand and then get syntax such as:
#(x, y) { x + y }
I'm not sure what you mean. I proposed this a while ago ("Harmony of My Dreams") but we don't want frozen by design, and without the # the result is ambiguous without restricted productions, and hazardous on that account.
The idea that any grawlixy preifx will do is false. Hash is wanted for consistent freeze/seal prefixing. Arrow is better and putting it at the front solves the grammar problems with arrow function syntax as current drafted.
Couldn’t one always freeze such a function shorthand and then get syntax such as:
#(x, y) { x + y }
I'm not sure what you mean. I proposed this a while ago ("Harmony of My Dreams") but we don't want frozen by design, and without the # the result is ambiguous without restricted productions, and hazardous on that account.
The idea that any grawlixy preifx will do is false. Hash is wanted for consistent freeze/seal prefixing. Arrow is better and putting it at the front solves the grammar problems with arrow function syntax as current drafted.
Rationale: wouldn’t freezing by default be OK for 98% of the cases? If you want anything else, you can use a traditional function. Then the above syntax as the only function shorthand would be OK.
On 19 January 2012 18:31, Axel Rauschmayer <axel at rauschma.de> wrote:
Rationale: wouldn’t freezing by default be OK for 98% of the cases?
Especially since the cases where you care most about short syntax are throw-away functions.
The only sane reason I have seen for mutating a function is to set its prototype property. But who wants to write constructors as short lambdas? Another use case is modelling C-style static variables as properties, but you cannot do that with an anonymous function anyway (I also don't regard it as particularly sane, but who am I to judge).
Are there other relevant use cases?
Andreas Rossberg wrote:
On 19 January 2012 18:31, Axel Rauschmayer<axel at rauschma.de> wrote:
Rationale: wouldn’t freezing by default be OK for 98% of the cases?
Especially since the cases where you care most about short syntax are throw-away functions.
The only sane reason I have seen for mutating a function is to set its prototype property. But who wants to write constructors as short lambdas? Another use case is modelling C-style static variables as properties, but you cannot do that with an anonymous function anyway (I also don't regard it as particularly sane, but who am I to judge).
I'd say +1 for frozen shortcut functions. I also don't see the use case where mutating the_function itself would be needed in such cases. It seems to me it is ok for big percentage of uses. If you need mutable one, use function.
Only fearful factor is when frozen one is used and in the runtime mutation is needed for something... but I could not find such use... wrapping such function is still possible, that is probably most possibility you need in runtime when monkey-patching or something.
Correct me if I am wrong.
Axel Rauschmayer <mailto:axel at rauschma.de>
January 19, 2012 9:31 AM
Rationale: wouldn’t freezing by default be OK for 98% of the cases? If you want anything else, you can use a traditional function. Then the above syntax as the only function shorthand would be OK.
First, #(params) { body } was proposed by Arv and Alex:
strawman:shorter_function_syntax
Arv and Alex feel strongly that the shorter function syntax (anything shortening 'function' syntax) must not freeze by default.
There was lack of clarity about whether completion value as implicit return value was part of the proposal. If so, controvery, since there is a completion value leak hazard. TC39 seems to agree the solution there is something with different look & feel, such as block-lambdas.
But, making a one-char grawlix shorthand for 'function' while still requiring 'return' is not considered enough of a shorthand. A possible cure here is to support an alternative body syntax: #(params) expr. However, this inverts precedence if done naively. It also runs into trouble trying to prefer an object literal over a block statement. I've worked on both of these in the context of
strawman:arrow_function_syntax
This superseded shorter_function_syntax, but ran into grammatical issues that have vexed it.
But notice that throughout this, no one advancing a proposal advocated freezing by default. JS developers use function objects as mutable objects. Not just to set .prototype, also to decorate with ad-hoc and meta-data properties. Freezing is not wanted by default.
I agree that for block-lambdas it's easier to say "freeze by default". For merely "shorter function syntax", no. Functions are mutable objects by default in JS. This matters for minifiers, which may not be able to see all the mutations but would love to use shorter syntax for 'function' syntax, blindly.
Brendan Eich wrote:
Axel Rauschmayer <mailto:axel at rauschma.de> January 19, 2012 9:31 AM
Rationale: wouldn’t freezing by default be OK for 98% of the cases? If you want anything else, you can use a traditional function. Then the above syntax as the only function shorthand would be OK.
First, #(params) { body } was proposed by Arv and Alex:
strawman:shorter_function_syntax
Arv and Alex feel strongly that the shorter function syntax (anything shortening 'function' syntax) must not freeze by default.
Well, if #(x, y) {x+y} is not good because of # is used for freezing and freezing is not good (it is not, metadata is good argument for that), and if something-like-lambda-block is ok, some variation of lambda-block could be the shorter replacement for functions. These come to mind:
{a, b a+b} {/a, b/ a+b} {^a, b^ a+b} {%a, b% a+b}
and there is still more infix operators...
maybe even {a, b
a+b} if it does not create conflict in this context.
It may be just a personnal taste, but I've to agree the current proposal (#() ...) seems very appealing to me. I did not respond to your mail proposing to use the Arrow syntax because it seems obscure to me. The distinction between "normal" and "fat" arrow is thin, does not make sense. You either need a function-object (which doesn't have 'this' mapping, has expandos) or a local-function (which has 'this' mapping, just need to be a [Call] target). If you need the first, you need a traditionnal function since you need something not-frozen that can be added to objects as a property at a later time. If you want 'this' mapping, you need something that only makes sense in a local context.
Additionnaly, the arrow syntax is illogical. You usually say "I want a function of (X,Y) which returns X+Y" or "I want to transform X in X.toString", not "From X, I want to get X.toString()".
Freezing a local function seems as acceptable to me as it seemed to others. A LF should only be used in a controlled context where the function itself is just used as a function, not an object. But if it's not acceptable to other members, I'm not against a @(x) syntax that does not offer frozen functions (but I think it's a missed optimization opportunity). The point about Conditionnal Compiling in IE is not valid anymore since Microsoft has deleted HTML Conditionnal Comments because they were not standards (even if they were used a lot), so I don't think the obscure and fewly used JScript CC are meant to stay, especially if it hurts another proposal.
In my view of the thing, a local function should be used as a function in the mathematical sense of the term: you give a parameter, it returns its image by the function.
The cases we are trying to solve:
var inc=#(x) x+1;
array.map(#(x) x.toString());
array.filter(#(x) isValid(x));
array.map(#(x) {
while(x.previousSibling) x=x.previousSibling;
return x;
});
For example, I don't see this as a good use-case of a LocalFunction :
...
refreshLayout: function(e) {
...
requestAnimationFrame(#(e) this.refreshLayout(e));
}
...
It should be a block lambda instead, because it's meant to 'continue' the current function in a sort of async while(true) loop.
...
refreshLayout: function(e) {
...
requestAnimationFrame({|e| this.refreshLayout(e) });
}
...
For all of the use cases where a "mathematical function" is requied, you just need some valid [Call]-able item. You will never add expandos on an function you don't know (ie that you received as a parameter). You'll wrap it before, if you really need that. If you want the full flexibility of a function-as-an-object, it means you need a 'true function'; LF are not meant to replace functions in the long run, they are made to serve the case where you want a short, action-related, contextual function. That means 'this' binding, if needed, just like it's in languages like dotNET.
However, I would like to hear more about the specific reasons that led Arv and Alex think a LF should not be frozen.
, François
PS: The synax I speak about for LocalFunctions would be:
<LocalFunctionExpression>: '#(' <argument-list> ')' <expression> or '#(' <argument-list> ') {' <statements>* '}'
They would be 'bound-this' if there's a 'this' in their body, but can be left unbounded if there's no since it has no visible effet. If they don't reference variables of a scope, they should not use reference scope and may be reused accross function calls.
-----Message d'origine---
FTR: With block lambdas and object literal extensions, I wouldn’t want/need a function shorthand. I thought I had seen an opportunity for a more compact syntax (if and only if function shorthands are needed), but was wrong. Sorry.
Suggestion: a community-edited page where we collect the rejected syntaxes (=> less running in circles) – simply copying emails (such as yours below) there would probably suffice.
Yet they are cases where a block lambda isn’t suited and where a ‘classic’ function is just too long to type (and would hurt performance as well). Look back in the thread for a sample. (Mainly: cases involving a ‘return’ in a loop or in a nested statement can’t be solved well using block-lambda).
Block lambda is not the solution since it wasn’t written to solve the cases where we traditionnaly use a 'local function’, but to solve new use-cases where we want our function to continue to run inside a function structure, or asynchronously. The old cases where we use “function() { ... { ... return; } ... }” are not covered properly by block lambda, nor are intended to.
From: Axel Rauschmayer Sent: Thursday, January 19, 2012 10:14 PM To: Brendan Eich Cc: Andreas Rossberg ; François REMY ; Oliver Hunt ; es-discuss Steen Subject: Re: Block lambda is cool, its syntax isn't FTR: With block lambdas and object literal extensions, I wouldn’t want/need a function shorthand. I thought I had seen an opportunity for a more compact syntax (if and only if function shorthands are needed), but was wrong. Sorry.
Suggestion: a community-edited page where we collect the rejected syntaxes (=> less running in circles) – simply copying emails (such as yours below) there would probably suffice.
On Jan 19, 2012, at 20:27 , Brendan Eich wrote:
Axel Rauschmayer <mailto:axel at rauschma.de> January 19, 2012 9:31 AM
Rationale: wouldn’t freezing by default be OK for 98% of the cases? If you want anything else, you can use a traditional function. Then the above syntax as the only function shorthand would be OK.
First, #(params) { body } was proposed by Arv and Alex:
strawman:shorter_function_syntax
Arv and Alex feel strongly that the shorter function syntax (anything shortening 'function' syntax) must not freeze by default.
There was lack of clarity about whether completion value as implicit return value was part of the proposal. If so, controvery, since there is a completion value leak hazard. TC39 seems to agree the solution there is something with different look & feel, such as block-lambdas.
But, making a one-char grawlix shorthand for 'function' while still requiring 'return' is not considered enough of a shorthand. A possible cure here is to support an alternative body syntax: #(params) expr. However, this inverts precedence if done naively. It also runs into trouble trying to prefer an object literal over a block statement. I've worked on both of these in the context of
strawman:arrow_function_syntax
This superseded shorter_function_syntax, but ran into grammatical issues that have vexed it.
But notice that throughout this, no one advancing a proposal advocated freezing by default. JS developers use function objects as mutable objects. Not just to set .prototype, also to decorate with ad-hoc and meta-data properties. Freezing is not wanted by default.
I agree that for block-lambdas it's easier to say "freeze by default". For merely "shorter function syntax", no. Functions are mutable objects by default in JS. This matters for minifiers, which may not be able to see all the mutations but would love to use shorter syntax for 'function' syntax, blindly.
/be
Now you are forgetting the argument that TCP-conforming block-lambdas should look quite different from ...(params) {body} functions. This implies that shorter function syntax ought to follow the (params) {body} plan.
François REMY <mailto:fremycompany_pub at yahoo.fr> January 19, 2012 12:19 PM It may be just a personnal taste, but I've to agree the current proposal (#() ...) seems very appealing to me. I did not respond to your mail proposing to use the Arrow syntax because it seems obscure to me. The distinction between "normal" and "fat" arrow is thin, does not make sense.
Agreed, and that is another problem plaguing arrow function syntax as proposed. But if we rescue it by moving the arrow up front, we may solve this problem by dropping fat arrow. "Shorter function syntax" does not have to include "shorter .bind(o) syntax".
You either need a function-object (which doesn't have 'this' mapping, has expandos) or a local-function (which has 'this' mapping, just need to be a [Call] target).
We can't guess and I would not assume a local (nested, you mean?) function wants lexical |this| by default, overridden only by callee-base |this| when/if the nested function is called as a method. Mark Miller showed the problem:
esdiscuss/2011-November/018186
in reply to
esdiscuss/2011-November/018183
If you need the first, you need a traditionnal function since you need something not-frozen that can be added to objects as a property at a later time. If you want 'this' mapping, you need something that only makes sense in a local context.
You mean a block-lambda? I agree.
Additionnaly, the arrow syntax is illogical. You usually say "I want a function of (X,Y) which returns X+Y" or "I want to transform X in X.toString", not "From X, I want to get X.toString()".
I'm not sure what you mean here by "illogical" and your verbal expansions -- are you saying the arrow should come in between params and body? Yes, that is nicer and Jeremy Ashkenas followed that design (used in other languages of course) for that reason. But it makes for grammar trouble for two reasons:
-
(params) looks like a comma expression. We can parse it as such and then validate it using "supplemental syntax" as for destructuring, but it is a bit future-hostile.
-
If we want an expression body alternate syntax that prefers object literals over body blocks, we have to resolve this conflict:
strawman:block_vs_object_literal
Doing so is even more future-hostile in light of the new object literal extensions in ES6.
Freezing a local function seems as acceptable to me as it seemed to others. A LF should only be used in a controlled context where the function itself is just used as a function, not an object. But if it's not acceptable to other members, I'm not against a @(x) syntax that does not offer frozen functions (but I think it's a missed optimization opportunity).
Again, @ is wanted for private names -- all the few unused ASCII punctuators are wanted. Why are you insisting on @ here? It's not particularly associated with functions in other languages, or suggestive of functions.
In my view of the thing, a local function should be used as a function in the mathematical sense of the term: you give a parameter, it returns its image by the function.
JS practice varies wildly. No developer consensus on this point.
The cases we are trying to solve:
var inc=#(x) x+1;
array.map(#(x) x.toString());
array.filter(#(x) isValid(x));
array.map(#(x) { while(x.previousSibling) x=x.previousSibling; return x; });
This case is not like the one you showed earlier, and not so clearly bad for block-lambda. There is only one return, it's at the bottom, so completion value as implicit return value in a block-lambda wins:
array.map({ |x|
while(x.previousSibling) x=x.previousSibling;
x;
});
It's not a requirement either way, of course.
For example, I don't see this as a good use-case of a LocalFunction :
... refreshLayout: function(e) { ... requestAnimationFrame(#(e) this.refreshLayout(e)); } ...
It should be a block lambda instead, because it's meant to 'continue' the current function in a sort of async while(true) loop.
Oh but here is a case where you advocate a block-lamdba for an "async callback". IIRC earlier you wrote that block-lambdas should be used for sync callbacks. I may have misread you though.
I dont believe there's a hard and fast rule. Of course, the block-lambda cannot break/continue/return if the enclosing function activation is no more.
... refreshLayout: function(e) { ... requestAnimationFrame({|e| this.refreshLayout(e) }); } ...
For all of the use cases where a "mathematical function" is requied, you just need some valid [Call]-able item. You will never add expandos on an function you don't know (ie that you received as a parameter).
No, but local functions are decorated with expandos in their local scope.
You'll wrap it before, if you really need that. If you want the full flexibility of a function-as-an-object, it means you need a 'true function'; LF are not meant to replace functions in the long run, they are made to serve the case where you want a short, action-related, contextual function. That means 'this' binding, if needed, just like it's in languages like dotNET.
We don't want a third kind of function (LF?). We just want shorter syntax. If freezing needs shorter syntax, # as prefix composes.
However, I would like to hear more about the specific reasons that led Arv and Alex think a LF should not be frozen.
I already wrote it: this is just proposed shorter syntax for 'function'. Shortening shouldn't freeze by default. That means the use-cases today for mutated functions cannot use the shorter syntax, for no reason other than your aesthetics about "mathematical functions", etc.
, François
PS: The synax I speak about for LocalFunctions would be:
<LocalFunctionExpression>: '#(' <argument-list> ')' <expression> or '#(' <argument-list> ') {' <statements>* '}'
They would be 'bound-this' if there's a 'this' in their body, but can be left unbounded if there's no since it has no visible effet. If they don't reference variables of a scope, they should not use reference scope and may be reused accross function calls.
<expression> doesn't work as I've argued, due to precedence inversion
Playing devil’s advocate: The main benefit of having a function shorthand in addition to block lambdas is that minimizers profit from it, right? For the use case that you showed, I wouldn’t mind at all to type the slightly longer "function". I most mind function expressions as parameters (for, say, Array.prototype.forEach) where lambda blocks are perfect.
But notice that throughout this, no one advancing a proposal advocated freezing by default. JS developers use function objects as mutable objects. Not just to set .prototype, also to decorate with ad-hoc and meta-data properties. Freezing is not wanted by default.
Another example: when defining parser combinators, I've re-defined .toString, to make it easier to identify the parser combination in question during debugging.
This would be less of an issue if core JS operations weren't biased towards mutation. If it was easy to create modified function objects without in-place mutation, freezing functions would be more acceptable.
Claus
On 19/01/2012, at 22:14, Axel Rauschmayer wrote:
Suggestion: a community-edited page where we collect the rejected syntaxes (=> less running in circles) – simply copying emails (such as yours below) there would probably suffice.
Has a backtick/accent grave ever been considered and/or rejected ?
Anonymous function expression:
setTimeout( '(){ ... }, 1e3);
Named function expression:
setTimeout( 'name(){ ... }, 1e3);
Declarations:
`(){ ... } // error: can't declare anonymous functions
`name(){ ... }
I must say you globally got my point (even if, after re-reading it, it wasn't as clear as I intended). I hope others did, too.
To respond to your questions :
(1) On the usage of the arobase (@) symbol The @ is used in some languages as a function introductory keyword. Have a look at the very popular (among engineers) Matlab/Octav, for exemple. To me, it also carry a vlid semantic since "At X corresponds X.toString()" is a nice way to say "@(X) X.toString()".
I've had a look at the current private name proposal, and, since last time I checked, there's still no mention of the @ symbol in it. If I guess correctly, it could replace the # sign used currently on the page. In that case, I don't see where's the problem with using the @() syntax since you can't actuall call the # symbol in the current proposal, you just need to access his ‘properties’.
By the way, I don't say we should use @, just that it's a possible choice (and that if # has a semantic issue that would make it a bad choice, it can be used instead). I’ve been using the @ symbol quite a lot in the past to introduce functions so it feels natural for me (don’t take me responsible if I continue to use it in my mails) but if the comitee choose to use # (or another symbol like %, µ or § (which is the key just after the opening parenthesis on my keyboard BTW)), I would be perfectly happy.
(2) Arrow between args and body Would be complex to parse. I don’t think it’s possible, and even if it is, it could be horribly confusing in some cases :
? array.filter((x)=>x<5) ? array.filter((x)=>(x<5)) ? array.filter((x)=>{return x<5;})
var list = { ... append: (x) => { list.add(x,list.length); } ... }
?
(2) Function, block-lambda semantics
Maybe I wasn’t that clear on the point, but something I hate is non-future-proof/unstable coding. When I write a piece of code, I want it to be as maintainable as possible. By maintainable, I mean that if I need to change the code, a small change should never trigger changes at different locations in my code. Small change, small fix.
The problem with the block-lambda used as a ‘local function’ (sorry, I don’t see a better way to define the concept, ie a function which is only used in an action-related, controlled context) is that a small change could require major rewrite.
Let’s imagine that I wrote that code :
arrary.map({|x| while(x.previousSibling) x=x.previousSibling; });
Then, let’s imagine that I should change my code to include an early return. Using block-lambda, it can be rather complex.
array.map({|x|
let shouldReturn=false;
while(!shouldReturn && x.previousSibling) {
if(isValidStart(x)) { shouldReturn=true; }
else { x=x.previousSibling; }
}
x;
})
Boom, the code has become very complex and is not easily readable anymore. Even if I wanted to modify my code to transform the block-lambda in a function, it would involve a lot of rewrite (changes are highlighted here, but I think if I face the case, I just delete the whole code and restart from scratch) :
array.map(function(x) {
while(x.previousSibling) {
if(isValidStart(x)) return x;
x=x.previousSibling;
}
return x;
});
But, according to me, if a small change can trigger a block-lambda to become a function, it’s because it should not have been a block lambda from start. It should have been a function.
Lets consider the case where we use a short function notation instead, from beginning : array.map(@(x) { while(x.previousSibling) { x=x.previousSibling; } return x; } It’s a big longer but it’s futur-proof. To make my small change, I just have to add one line of code. array.map(@(x) { while(x.previousSibling) { if(isValidStart(x)) return x; x=x.previousSibling; } return x; }); For the first time, the code was stable. A small change only shifted slightly the balance.
I can acomodate with ‘function’, but, let’s face it, in some simpler cases, if I don’t have a short function notation, I’ll use a block-lambda and will face the problem. And be sure I’ll dislike it :-D
Even allowing the @(x) x.toString() syntax is not a problem since if I need to change the expression into a list of statement, there’s just one place to perform the changes (I have to replace x.toString() with a new code), so I can write my code in one continuous typing stream, and I don’t have to use the mouse (or CTRL+Arrows) to move the spot to different places. An expression has to be small anyway so I don’t loose a lot of code even if I choose to scratch & redo. The @ and the arguments are already there. (3) Short function ‘desugaring’ preference If I could choose, I would set the short function syntax to imply automatic binding. But since it makes them unusable for script minizers without checking if ‘this’ is used in the function or not, I guess I can live without that (to the condition that I can do that: @(this=this, x) this.transform(x)).
I hope I’ve been more clear than in my previous mail, François
-----Message d'origine---
Jorge <mailto:jorge at jorgechamorro.com> January 20, 2012 1:22 AM
Has a backtick/accent grave ever been considered and/or rejected ?
Rejected, that is used by wiki.ecmascript.org/doku.php?id=harmony:quasis already.
Just to add weight to my previous mail, you may find it interesting to notice that my proposed syntax match nearly exactly the proposed syntax of the new Mozilla-editored Rust programming language: doc.rust-lang.org/doc/tutorial.html#closure-compatibility
call_twice({|| "I am a stack closure; });
call_twice(fn@() { "I am a boxed closure"; });
It confirms my feeling about {|| ...}: it should only be allowed in a context where the function still exists; it's a way to return the control back to the calling function when you need to call a "callback-able" function for some reason. At every other place, you should use 'normal' closures (the language-agnostic equivalent of ECMAScript functions), for which we should have a simplified syntax (aka syntaxic sugar). I don't bother if it has to start by @, #, %, µ, § or anyting else, but I feel strongly about the fact we need it.
Now, I think everyone got my point, I leave the final discussion to group members. But, at least, my message was sent. ;-)
Best , François
Rust is a static language with many guarantees. It's not really comparable to JS here.
JS extended with block-lambdas has no way to force downward-funarg-only contract on functions called with block-lambda arguments. And indeed, nothing goes wrong if those block-lambdas escape via the heap and are called after the enclosing function's activation, which passed the block-lambda down, has returned. Only if the block-lambda does a control effect that would propagate to the enclosing function's activation is there a dynamic error.
JS is a dynamic language. When you write "it should only be allowed", are you seriously proposing a checkable syntax and static check of some kind? How would that work?
(Aside: I wish I had used "fn" instead of "function" in 1995!)
(Aside: I wish I had used "fn" instead of "function" in 1995!)
How much does the fact that a symbol could be used (in theory) matter in practice? Most regexes I see for valid identifiers are still of the ^[a-zA-Z_$][0-9a-zA-Z_$]*$ variety.
I’d expect λ to be in use in Greece, but how about ƒ (which has been mentioned many times)? It seems very appropriate and is even easy to type on a Mac (easier than square brackets on a German keyboard!).
On Jan 20, 2012, at 5:08 PM, Brendan Eich wrote:
... (Aside: I wish I had used "fn" instead of "function" in 1995!)
At the TC39 meeting we were trying to think of pragma candidates.
It wouldn't surprise me if JS programmer would happily trade one:
use fn;
per file in exchange for being able to use "fn" as a synonym for "function". In terms of character counts, you come out ahead starting with the second function definition.
Allen Wirfs-Brock <mailto:allen at wirfs-brock.com> January 20, 2012 5:41 PM
At the TC39 meeting we were trying to think of pragma candidates.
It wouldn't surprise me if JS programmer would happily trade one:
use fn;
per file in exchange for being able to use "fn" as a synonym for "function". In terms of character counts, you come out ahead starting with the second function definition.
Allen
I have considered this in the past. It always seemed too little, due to return. If it enabled another production:
AssignmentExpression : fn Identifier_opt ( FormalParameterList_opt ) AssignmentExpression
(or we just added this unconditionally, without the pragma -- but the pragma is good too) then I'd be happy, finally.
On 20/01/2012, at 19:17, Brendan Eich wrote:
Jorge <mailto:jorge at jorgechamorro.com> January 20, 2012 1:22 AM
Has a backtick/accent grave ever been considered and/or rejected ?
Anonymous function expression:
setTimeout( `(){ ... }, 1e3);
Named function expression:
setTimeout( `name(){ ... }, 1e3);
Declarations:
`(){ ... } // error: can't declare anonymous functions
`name(){ ... }
Rejected, that is used by wiki.ecmascript.org/doku.php?id=harmony:quasis already.
1.- There are not quasi literals in the language yet 2.- quasi literals could as well use something else (the acute accent ?) instead 3.- a shorter function syntax is almost at the top in the programmers' wish list for ES.next (unlike quasis)
Isn't it -perhaps- too soon to reject ` for functions in favor of quasis ?
Jorge <mailto:jorge at jorgechamorro.com> January 20, 2012 6:46 PM
1.- There are not quasi literals in the language yet
No, quasi-literals are approved for ES6. Already.
2.- quasi literals could as well use something else (the acute accent ?) instead
No, that is used for string literals, since JS1 in 1995!
3.- a shorter function syntax is almost at the top in the programmers' wish list for ES.next (unlike quasis)
So what? No one except you is asking for ` and quasis won. Please adjust your expecations accordingly.
Isn't it -perhaps- too soon to reject ` for functions in favor of quasis ?
No.
On 21/01/2012, at 02:34, Axel Rauschmayer wrote:
how about ƒ (which has been mentioned many times)? It seems very appropriate and is even easy to type on a Mac (easier than square brackets on a German keyboard!).
setTimeout( ƒ () { ... }, 1e3) setTimeout( `() { ... }, 1e3)
setTimeout( ƒ name () { ... }, 1e3) setTimeout( `name () { ... }, 1e3)
ƒ name () { ... } `name () { ... }
On 21/01/2012, at 03:59, Brendan Eich wrote:
2.- quasi literals could as well use something else (the acute accent ?) instead
No, that is used for string literals, since JS1 in 1995!
Sorry, I don't follow, with "that" you mean "something else" or "the acute accent" ?
str = ´agudo´ Error • message: "Invalid character '\u0180'"
Jorge <mailto:jorge at jorgechamorro.com> January 20, 2012 7:15 PM
Sorry, I don't follow, with "that" you mean "something else" or "the acute accent" ?
Oh, not ' but the diacritical on é, you mean?
How do I type that on a US or UK keyboard? We are not going to use non-ASCII characters, so you are still barking up the wrong tree.
Axel Rauschmayer <mailto:axel at rauschma.de> January 20, 2012 5:34 PM
How much does the fact that a symbol could be used (in theory) matter in practice? Most regexes I see for valid identifiers are still of the ^[a-zA-Z_$][0-9a-zA-Z_$]*$ variety.
I’d expect λ to be in use in Greece, but how about ƒ (which has been mentioned many times)? It seems very appropriate and is even easy to type on a Mac (easier than square brackets on a German keyboard!).
It's hard to type λ on many keyboards. ƒ is easier, but neither is backward-compatible:
js> ƒ = 1
1 js> ƒ
1 js> λ = 2
2 js> λ
2
We rejected these already for these reasons.
I think "use fn;" (real pragma syntax), with the low-precedence assignment-expression fn (params) assign-expr production, wins. What do you think?
Various thoughts:
How much does the fact that a symbol could be used (in theory) matter in practice? Most regexes I see for valid identifiers are still of the ^[a-zA-Z_$][0-9a-zA-Z_$]*$ variety.
I’d expect λ to be in use in Greece, but how about ƒ (which has been mentioned many times)? It seems very appropriate and is even easy to type on a Mac (easier than square brackets on a German keyboard!).
It's hard to type λ on many keyboards. ƒ is easier, but neither is backward-compatible:
js> ƒ = 1 1 js> ƒ 1 js> λ = 2 2 js> λ 2
We rejected these already for these reasons.
My thinking was: If no one ever uses λ as an identifier (not likely in Greece) or ƒ as an identifier (likely, given that most people seem to think JS identifiers must adhere to the regexes shown above) then one would be free to make them reserved tokens in ES6. Counter arguments:
-
Hard to type: reading is far more important than typing, it is very easy to adapt tools to help with typing.
-
Not ASCII: I hardy every encounter non-UTF text files, any more. Non-ASCII seems to work well for Fortress.
I think "use fn;" (real pragma syntax), with the low-precedence assignment-expression fn (params) assign-expr production, wins. What do you think?
Having fn would be sweet. For many kinds of pragmas, it would be great if one could configure these per project (or per directory). Then one could put legacy code in one directory and ES6 code in another. And the weight of the pragmas would be negligible. Not sure how to do this, though; reminds me very loosely of CSS (centralized management of style).
Would fn make sense as an ES6 breaking change? I’d expect many people to be strongly in favor, possibly as many strongly opposed.
Possible research (applies to ƒ, too):
- Poll people for their opinion
- Search JS code bases for how often the identifier "fn" is used (how?).
Axel Rauschmayer <mailto:axel at rauschma.de> January 20, 2012 9:24 PM
- Hard to type: reading is far more important than typing, it is very easy to adapt tools to help with typing.
ƒ is not particularly readable IMHO. It's florin, not mathematical notation I've ever seen. Anyway we can't assume the compatibility break is ok since it can occur in operand context in any expression.
- Not ASCII: I hardy every encounter non-UTF text files, any more. Non-ASCII seems to work well for Fortress.
Fortress, please. I invite @samth to comment now. I admire Fortress, but it is not an exemplar or nearby language for JS.
I think "use fn;" (real pragma syntax), with the low-precedence assignment-expression fn (params) assign-expr production, wins. What do you think?
Having fn would be sweet. For many kinds of pragmas, it would be great if one could configure these per project (or per directory). Then one could put legacy code in one directory and ES6 code in another. And the weight of the pragmas would be negligible. Not sure how to do this, though; reminds me very loosely of CSS (centralized management of style).
You make a good point: "use fn;" may be too much to require in every <script> element content.
Would fn make sense as an ES6 breaking change? I’d expect many people to be strongly in favor, possibly as many strongly opposed.
Don't forget that JQuery uses fn as an identifier (and not just following a dot) all over.
Possible research (applies to ƒ, too):
- Poll people for their opinion
These are not decisive. I've been straw-polling audiences for a while. Block-lambdas are doing well, but one must ask "nothing, just 'function'" and usually get a stronger response than any new syntax.
- Search JS code bases for how often the identifier "fn" is used (how?).
See JQuery sources. Unconditionally reserving 'fn' is a non-starter IMHO. Recall also that we are trying to hew to "One JavaScript" -- few to no refactoring woes moving code in and out of a module {...} container or "use strict"; code block.
About 'ƒ': on my keyboard, there's no way to type it. I have to use ALT+its UTF8 keycode, and it doesn't work in all programs. BTW, since the default file format on Windows is not UTF8, expect many developers to use ISO-8859-1 for their JS files. Personnaly, I use Notepad++ which allow me to use UTF8 as default encoding, but I don't expect all devs to do that.
About the 'fn' proposal, I'm happy with it. Asking a 'use' statement just for that feature may prove a little excessive but if it can be merged with other can-be-breaking syntax reforms ('use es6'), why not. But, seriously, what really makes it impossible to use # or @? Those would have no compat issues, would require no header. Even if @ would be used in private-name, it would be in a completely different context. It's like the SQL guys who used '%' as wildchar in LIKE strings because '' was already used as a shortcut in the SELECT operand; those use of the '' symbol were completely orthogonal, and it would have been possible to allow both.
François
-----Message d'origine---
This seems pretty easy to enforce to me.
function a(x) {
let count = 0;
[
(x+1)^(x-1),
(x*x),
(2*x-1)
];
arr.forEach({|x|
if((x&1)==0) count++;
});
return count;
}
-----Message d'origine---
(sorry, last mail was sent by mistake)
This seems pretty easy to enforce to me.
function a(x) {
let count = 0;
let arr = [
(x+1)^(x-1),
(x*x),
(2*x-1)
]
arr.forEach({|x|
if((x&1)==0) count++;
});
return count;
}
can be compiled as :
function a(x) {
let count=0;
let arr = [...];
let $lambda = {
[Call]: {||
...
}
};
arr.forEach($lambda);
$lambda.[Call] = function() { throw new InvalidTargetException(); }
return count;
}
So, if the 'Array.prototype.forEach' function of has been modified to call the iter function asynchronously, it will not work.
As a plus, it allows a very powerful optimisation (block-lambda running in the same frame as the function itself).
, François
-----Message d'origine---
François REMY wrote:
About the 'fn' proposal, I'm happy with it. Asking a 'use' statement just for that feature may prove a little excessive but if it can be merged with other can-be-breaking syntax reforms ('use es6'), why not. But, seriously, what really makes it impossible to use # or @? Those would have no compat issues, would require no header. Even if @ would be used in private-name, it would be in a completely different context. It's like the SQL guys who used '%' as wildchar [and may have used *].
True for @, but not so for #. The latter is used to specify freeze. That is, you probably can write #@(x) {x+1}, but ##(x) {x+1} would be ridiculous.
I think "use fn;" (real pragma syntax), with the low-precedence assignment-expression fn (params) assign-expr production, wins. What do you think?
Having fn would be sweet. For many kinds of pragmas, it would be great if one could configure these per project (or per directory). Then one could put legacy code in one directory and ES6 code in another. And the weight of the pragmas would be negligible. Not sure how to do this, though; reminds me very loosely of CSS (centralized management of style).
You make a good point: "use fn;" may be too much to require in every <script> element content.
From the Haskell experience again: there, one can have language
feature options
- as pragmas in source code [1]
- as options in package descriptor files [2]
The former is better for source readability, and for being specific about which files depend on which extensions. The latter is better for avoiding repetition if every source in a package uses the same set of options, and for package readability (making package feature dependencies obvious without having to scan source files). In-source pragmas override package-wide options.
One thing that Haskell doesn't have, which I have often wanted, is the ability to define extension groups on a per-project basis:
if I always use the same sets of extensions (eg, yield and block scoping, or modules and classes, or ..), then it would be nice to define pragma groups in the package file, and just refer to the group pragma in the source files. That way, use of language features would be explicit in the source, but without repetitive details. However, this is less of an issue in JS than in Haskell (the latter has far more language extensions, with a very conservative base standard, while all ES extensions currently under discussion will be grouped in ES6).
Claus
[1] www.haskell.org/ghc/docs/latest/html/users_guide/pragmas.html [2] "extensions: identifier list" A list of Haskell extensions used by every module. Extension names are the constructors of the Extension type. hackage.haskell.org/packages/archive/Cabal/1.6.0.3/doc/html/Language-Haskell-Extension.html
(sorry, last mail was sent by mistake)
This seems pretty easy to enforce to me.
function a(x) {
let count = 0;
let arr = [
(x+1)^(x-1),
(x*x),
(2*x-1)
];
arr.forEach({|x|
if((x&1)==0) count++;
});
return count;
}
can be compiled as :
function a(x) {
let count=0;
let arr = [...];
let $lambda = {
[Call]: {||
...
}
};
arr.forEach($lambda);
$lambda.[Call] = function() { throw new InvalidTargetException(); }
return count;
}
So, if the 'Array.prototype.forEach' function of has been modified to call the iter function asynchronously, it will not work.
As a plus, it allows a very powerful optimisation (block-lambda running in the same frame as the function itself).
, François
-----Message d'origine---
Great stuff. I’m equally intrigued by the use fn
pragma: It allows one to selectively break compatibility.
Caveat: This kind of pragma introduces a lot of new complexity.
Pragmas would indeed become less burdensome if one could configure them on a per-project basis. It corresponds to Eclipse setting different Java versions per source directory. Ideas (not sure that they are worth the added complexity, but wanted to mention them):
- Meta-tag points to file that configures pragmas based on paths
- Custom, user-defined meta-pragmas that group several pragmas
- A pragma that points to a file with several pragmas
François REMY <mailto:fremycompany_pub at yahoo.fr> January 21, 2012 2:14 AM
function a(x) {
let count=0; let arr = [...]; let $lambda = { [Call]: {|| ... } };
arr.forEach($lambda);
$lambda.[Call] = function() { throw new InvalidTargetException(); }
No, we are not specifying dynamic semantics based on a mutation to the [[Call]] internal property.
I wrote "When you write "it should only be allowed", are you seriously proposing a checkable syntax and static check of some kind?"
I was alluding to ideas such as Ruby's yield operator, which is the only way to name the extra downward-funarg-only block argument, preventing heap escape (but of course, Ruby grew & params and so on, so had to deal with escape anyway).
This is no static check. It is unacceptable both as semantics and in real implementation costs.
Again, in general a block-lambda that escapes and is called after its parent function activation has deactivated is not a bug. It should not be forbidden uncondionally. The only requirement is that control cannot flow back to the defunct activation. Only such attempts via break/continue/return will throw.
François REMY <mailto:fremycompany_pub at yahoo.fr> January 21, 2012 1:47 AM (sorry, last mail was sent by mistake)
You sent your reply twice, to this thread and another. I replied to the other.
As a plus, it allows a very powerful optimisation (block-lambda running in the same frame as the function itself).
That optimization is possible in any event, and optimizations are not semantics -- they are unobservable. What you are proposing is an unnecessary semantic restriction.
François REMY <mailto:fremycompany_pub at yahoo.fr> January 21, 2012 1:34 AM About the 'fn' proposal, I'm happy with it. Asking a 'use' statement just for that feature may prove a little excessive but if it can be merged with other can-be-breaking syntax reforms ('use es6')
Please read the notes from the tc39 meeting, and the recent threads. We are not requiring version opt-in. New syntax is its own opt-in.
The problem here is that 'fn' is not "new syntax" without complex parsing hacks. It's an unreserved identifier in ES1-5.
why not. But, seriously, what really makes it impossible to use # or @?
We've been over this already. # is for freezing, @ is for private names. The Matlab precedent of @ for function is quite weak (thanks for pointing it out).
On 21/01/2012, at 05:31, Brendan Eich wrote:
Jorge <mailto:jorge at jorgechamorro.com> January 20, 2012 7:15 PM
Sorry, I don't follow, with "that" you mean "something else" or "the acute accent" ?
Oh, not ' but the diacritical on é, you mean?
Yes, the acute accent. For example. Or something else. You can choose almost any character you want.
How do I type that on a US or UK keyboard?
I don't know, my keyboard is spanish. Here it's next to the P.
We are not going to use non-ASCII characters, so you are still barking up the wrong tree.
Aren't you discussing the possibility of using ƒ or λ for functions in this same thread ?
Well, the florín is not an ASCII character either.
Jorge <mailto:jorge at jorgechamorro.com> January 21, 2012 12:14 PM On 21/01/2012, at 05:31, Brendan Eich wrote:
We are not going to use non-ASCII characters, so you are still barking up the wrong tree.
Aren't you discussing the possibility of using ƒ or λ for functions in this same thread ?
No. I shot those down in the thread for several reasons. One is inability to type on many keyboards. Another is incompatibility (these are both identifier characters per ES3-5).
Well, the florín is not an ASCII character either.
Right!
François REMY <mailto:fremycompany_pub at yahoo.fr> January 21, 2012 1:00 PM
-----Message d'origine----- From: Brendan Eich
François REMY <mailto:fremycompany_pub at yahoo.fr> January 21, 2012 1:34 AM About the 'fn' proposal, I'm happy with it. Asking a 'use' statement > just for that feature may prove a little excessive but if it can be > merged with other can-be-breaking syntax reforms ('use es6')
Please read the notes from the tc39 meeting, and the recent threads. We are not requiring version opt-in. New syntax is its own opt-in.
The problem here is that 'fn' is not "new syntax" without complex parsing hacks. It's an unreserved identifier in ES1-5.
If it's the case, we should avoid that. Avoiding 'use' for the whole syntax and requiring it just for 'fn' is weird.
"Weird" doesn't quite dismiss the idea, since JS has 'function' and a shorthand for it -- along with a crucial new production adding an expression-bodied alternative short-function-expression -- is something users keep requesting. If the goal is to shorten 'function' yet keep a mnemonic shorter prefix, nothing grawlix will do. Why not 'fn'?
But I understand we can't use 'fn' per se since it breaks compat. We should try to find something else (it's possible, at least).
Some here are positive on 'fn'. We have been searching for usable shorter syntax for a while. We should not dismiss 'fn' so quickly.
why not. But, seriously, what really makes it impossible to use # or @?
We've been over this already. # is for freezing, @ is for private names. The Matlab precedent of @ for function is quite weak (thanks for pointing it out).
Yes, but you didn't respond about where @ is used in private name, and why it makes the @() syntax ambiguous with that use.
You have to follow the strawmen that haven't made it, in particular
and
doku.php?id=strawman:classes_with_trait_composition&rev=1305852025#private_state
and es-discuss threads about foo at bar vs. foo. at bar, just @bar for this-relative addressing, etc.
Since the @ is never used in the private name proposal, I can't check that on my own. My guess is that they don't collide.
They collide conceptually by overloading @ for two quite different things.
If we want shorter named function expression syntax, then they collide syntactically with a fatal ambiguity absent more restricted productions:
@foo(bar) { expr; }
Is this an @-function-expression or a call to private this-relative foo passing bar?
Anyway, there are still other chars left, we should check if they are easy to type on various keyboards (on my keyboard they are): ['6', '~', 'µ', '£'].
Please re-read the recent messages on this thread: no non-ASCII. Also, ~ is in JS already!
From a web development perspective, the small cost of a string literal "use
fn"; in trade for the reduced bytes of "fn" would be well received.
I recently delivered a fairly large scale mobile web application, to a Bocoup client, that had 340 occurrences of "function" (that includes all library and vendor code). That's 2720 bytes that can't be "minified" (but thankfully will be gzipped). That would be only 680 with "fn" - more then 2kb difference (this stuff adds up).
As well as pragmas, macro-expansion would be a possibility!
define fn function undef fn
G.
I have heard many fellow programmers say that, while block lambdas would be welcome, Ruby's |a, b| syntax is ugly. I thought I was the only one feeling this way, but since I am not, I would like to ask why the parentheses cannot be used there.
cannot conflict with any existing program; at least I don't see how it can.
This is a reaction to the following tweet: twitter.com/#!/tabatkins/status/156813490785492992