optional "function" keyword

# Isaac Schlueter (11 years ago)

A great many letters have been typed regarding the number of letters in the word "function".

What if we just made the keyword "function" entirely optional, dart-style?

*ShortNamedFunctionDeclaration* *:**Identifier **<no_linebreak>* *(* 
*FormalParameterListopt* *) <no_linebreak> {* *FunctionBody* *}*
*ShortNamedFunctionExpression* *:**Identifier **<no_linebreak>* *(* 
*FormalParameterListopt* *) <no_linebreak> {* *FunctionBody* *}*

Note that the identifier is no longer optional, and that no linebreak is allowed between the parameter list and the body, unlike current function decl/exprs.

myList.forEach(x (item) {
  item += 2
  doSomethingElse(item)
  assert(typeof x === "function")
})
// expression, not declaration
assert(typeof x === "undefined")

// declaration, hoisted
assert(typeof doSomethingElse === "function")

doSomethingElse (item) {
  // IIFE, not declaration
  ;(y (n) { item += n })(Math.random())
  assert(typeof y === "undefined")
}

Is there an obvious case I'm overlooking where this sort of construction would fail? Clearly, you can't put the { on the next line or else it'll be interpreted as an invocation followed by a block, and that could be a significant footgun. But, it's not a new footgun, since \n{ is problematic with return anyhow. (I long for a mode where \n{ is always a syntax error.) And, it introduces no new keywords, no syntax that isn't already an error, and no new semantics.

I'm eager to see what I missed when you shoot this down :)

# Russell Leggett (11 years ago)

I believe is has effectively been added under the more constrained object literal extensions

# Thaddee Tyl (11 years ago)

An interesting property of this syntax is that anonymous functions can be written this way:

myList.forEach(? (item) { doWith(item) })

(it can also be lambda (item) { ... })

# Isaac Schlueter (11 years ago)

Yes, any valid function identifier could be used, because it's not actually a keyword.

_ (arg) { ... }
f (arg) { ... }
$ (arg) { ... }
? (arg) { ... }
# Brendan Eich (11 years ago)

This approach requires a restriction: [no LineTerminator here] between the ) and the {:

js> function f(a,b,c){return a*b+c}
js> var x=2,y=3,z=4,w
js> f(x,y,z) {
typein:3: SyntaxError: missing ; before statement:
typein:3: f(x,y,z) {
typein:3: .........^
js> eval("w=f(x,y,z)\n{print(w)}")
10

(That last 10 came from print(w) of course; the completion value of the eval was undefined.)

Leaving out the f in the "definition" doesn't help, since (a,b,c) is a comma expression. The requirement is no LineTerminator between the ) and the {.

We went around a nearby block recently, in considering {(a,b)...} for block lambdas. Everyone I remember initially favoring this syntax came around to oppose it because the opportunity for confusion due to parenthesized expression statements already mixing with curly-braced block statements was too great.

Perhaps this case is different enough. But is it? Modified Prototype 1.6:

inGroupsOf(number, fillWith) {
  fillWith = Object.isUndefined(fillWith) ? null : fillWith;
  return this.eachSlice(number, (slice) {
    while(slice.length < number) slice.push(fillWith);
    return slice;
  });
},

inject(memo, iterator, context) {
  iterator = iterator.bind(context);
  this.each((value, index) {
    memo = iterator(memo, value, index);
  });
  return memo;
},

invoke(method) {
  var args = $A(arguments).slice(1);
  return this.map((value) {
    return value[method].apply(value, args);
  });
},

I've obviously also used method definition shorthand by changing any function ( occurrences in the original prototype code to just (.

Without a leading keyword, it's harder to find the functions. Not impossible, not saying this is a deal breaker. But it is harder.

# Isaac Schlueter (11 years ago)

On Tue, Mar 6, 2012 at 10:53, Brendan Eich <brendan at mozilla.org> wrote:

This approach requires a restriction: [no LineTerminator here] between the ) and the {

Yes, I did put that in the OP, but it looks like my mail client helpfully wrapped at that point, which is a bit confusing :)

Leaving out the f in the "definition" doesn't help, since (a,b,c) is a comma expression. The requirement is no LineTerminator between the ) and the {.

Yes, an identifier is required. It would not be possible to define an unnamed function in this way.

Without a leading keyword, it's harder to find the functions. Not impossible, not saying this is a deal breaker. But it is harder.

Agreed. This was inspired by seeing some code in candor and dart, and getting jealous of their nice terse functions :) However, having not used this style in real programs, it's hard to comment on whether it would continue to be nice, or get annoying.

# Brendan Eich (11 years ago)

Isaac Schlueter wrote:

On Tue, Mar 6, 2012 at 10:53, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

This approach requires a restriction: [no LineTerminator here]
between the ) and the {:

Yes, I did put that in the OP, but it looks like my mail client helpfully wrapped at that point, which is a bit confusing :)

No irony.

Leaving out the f in the "definition" doesn't help, since (a,b,c)
is a comma expression. The requirement is no LineTerminator
between the ) and the {.

Yes, an identifier is required. It would not be possible to define an unnamed function in this way.

Why not express an anonymous function, though? Definition != expression. As usual, an expression statement could not start with ( and consist entirely of a function-keyword-free anonymous function expression.

Without a leading keyword, it's harder to find the functions. Not
impossible, not saying this is a deal breaker. But it is harder.

Agreed. This was inspired by seeing some code in candor and dart, and getting jealous of their nice terse functions :) However, having not used this style in real programs, it's hard to comment on whether it would continue to be nice, or get annoying.

Dart has mandatory semicolons to help avoid trouble around the edges of statements/definitions.

Candor is new, so no one has deep experience.

To get further we'd need some brave souls to try this out with a trivial transpiler, at scale.

# Isaac Schlueter (11 years ago)

On Tue, Mar 6, 2012 at 12:06, Brendan Eich <brendan at mozilla.org> wrote:

Yes, an identifier is required.  It would not be possible to define an unnamed function in this way.

Why not express an anonymous function, though? Definition != expression. As usual, an expression statement could not start with ( and consist entirely of a function-keyword-free anonymous function expression.

I see. So, you'd be suggesting using:

( args ) { functionBody }

as an anonymous function expression, though not a function expression statement? What about these?

;(( args ) { functionBody })
!( args ) { functionBody }

Certainly, this is quite nice:

myList.forEach((item) { .. })

Dart has mandatory semicolons to help avoid trouble around the edges of statements/definitions.

Candor is new, so no one has deep experience.

To get further we'd need some brave souls to try this out with a trivial transpiler, at scale.

True that.

# Brendan Eich (11 years ago)

Brendan Eich wrote:

Yes, an identifier is required. It would not be possible to define an unnamed function in this way.

Why not express an anonymous function, though? Definition != expression. As usual, an expression statement could not start with ( and consist entirely of a function-keyword-free anonymous function expression.

I should have stopped at "could not start with". Doesn't matter how it might end, an expression-statement can't start with 'function' now and, under the proposal, could not start with the ( beginning a formal parameter list.

# Rick Waldron (11 years ago)

A few months ago, I made this same suggestion, here: www.mail-archive.com/[email protected]/msg07021.html

And David Herman very succintly explained here:

www.mail-archive.com/[email protected]/msg07031.html

# Brendan Eich (11 years ago)

Isaac Schlueter wrote:

I see. So, you'd be suggesting using:

 ( args ) { functionBody }

as an anonymous function expression, though not a function expression statement?

Right, see followup. The grammar without lookahead restrictions is ambiguous. See ES5,

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.

Of course we cannot naively extend the lookahead rejection set to include ( because that forbids a top level parenthesized expression statement. One way around this is to refactor the grammar similarly to what I propose in

strawman:block_lambda_revival#syntax

to separate InitialValue from AssignmentExpression, and then allow 'function'-free function expressions as alternative right parts for IniitalValues. Thus you could write such shorthand function expressions in all places where you can write function expressions today, except unparenthesized followed by . or [ or another operator (+ might be useful, or confusing, sporadically).

This also allows us to extend the shorthand syntax to support an expression body as well as a braced statement list:

IniitialValue: AssignmentExpression ShortFunctionExpression

AssignmentExpression: ... LeftHandSideExpression = ShortFunctionExpression

ShortFunctionExpression: Identifier_opt ( FormalParameterList_opt ) { FunctionBody } Identifier_opt ( FormalParameterList_opt ) IniitialValue

without precedence inversion problems.

What about these?

 ;(( args ) { functionBody })
 !( args ) { functionBody }

All fine, just as if you'd used the hated eight-letter keyword in front of the (.

Certainly, this is quite nice:

 myList.forEach((item) { .. })

Less nice for map because of the hated six-letter keyword, but yeah.

# Brendan Eich (11 years ago)

I know, that's why I wrote that a [no LineTerminator here] restricted production would be required (which I just forgot to include in my mini-grammar sent to Isaac, guh).

Dave assumed we wouldn't add such a restriction. But if we do, then there's no backward incompatibility because right now you cannot run (a,b,c) up against {foo(); bar = baz()} on the same line.

# Brendan Eich (11 years ago)

Brendan Eich wrote:

ShortFunctionExpression: Identifier_opt ( FormalParameterList_opt ) { FunctionBody } Identifier_opt ( FormalParameterList_opt ) IniitialValue

Let's try that again:

ShortFunctionExpression: Identifier_opt ( FormalParameterList_opt ) [no LineTerminator here] { FunctionBody } Identifier_opt ( FormalParameterList_opt ) [no LineTerminator here] IniitialValue

But of course the second production creates bad ambiguities for all InitialValue bodies that start with an operator or punctuator that could be infix as well as prefix: +/-/[ at least.

More work needed. I may write a strawman up. Encourage me!

# Isaac Schlueter (11 years ago)

I'd suggest dropping the

Identifier_opt ( FormalParameterList_opt ) [no LineTerminator here] IniitialValue

production. Wouldn't that mean that you could have something like this?

var allA = list.map(() "a")

I think the curly braces are ok here. Hated keywords should be attacked one at a time, imo.

# Brendan Eich (11 years ago)

Isaac Schlueter wrote:

I'd suggest dropping the

Identifier_opt ( FormalParameterList_opt ) [no LineTerminator here]

IniitialValue

production. Wouldn't that mean that you could have something like this?

 var allA = list.map(() "a")

I think the curly braces are ok here. Hated keywords should be attacked one at a time, imo.

Divide-and-conquer in language design can go down many bad paths. One is that if we attack 'return' naively, we make a completion-value leak hazard (discussed many times on this list).

Block-lambda revival uses a different-looking syntactic special form to call out the places where TCP holds and return means the same in the body as in the enclosing function.

For what we're discussing here, no such TCP should be contemplated (you'll be relieved to hear :-P). That means we can't just say "curly braces and impute the return value from the completion value", though

# David Herman (11 years ago)

On Mar 6, 2012, at 12:37 PM, Brendan Eich wrote:

Isaac Schlueter wrote:

Certainly, this is quite nice:

myList.forEach((item) { .. })

Less nice for map because of the hated six-letter keyword, but yeah.

What if the anonymous form didn't require return, i.e., used the body's completion value?

myList.map((x) { x + 1 })

(Whether the body is TCP-respecting or not is an orthogonal question.)

# Brendan Eich (11 years ago)

Just replied to Isaac on this. We run into the completion-value leak objection and I do not see a way around it.

# David Herman (11 years ago)

On Mar 6, 2012, at 1:50 PM, Brendan Eich wrote:

Just replied to Isaac on this. We run into the completion-value leak objection and I do not see a way around it.

One way around it is to question the hazard. :)

Seriously, whether

{|x| f() }

or

(x) { f() }

is more likely to be an accidental leak is an empirical question, and not one I can guess just by looking at the syntax.

# Brendan Eich (11 years ago)

David Herman wrote:

On Mar 6, 2012, at 1:50 PM, Brendan Eich wrote:

Just replied to Isaac on this. We run into the completion-value leak objection and I do not see a way around it.

One way around it is to question the hazard. :)

Seriously, whether

 {|x| f() }

or

 (x) { f() }

is more likely to be an accidental leak is an empirical question, and not one I can guess just by looking at the syntax.

Yes, the dilemma has only an empirical (a posteriori) answer, if any. It could be YMMV.

But one design option is to avoid making a thing that looks like a function body today, where no completion-value leak hazard exists. An expression body is presumed useful and manifestly the return value. You get what you ask for.

The risk here may be small but people have run into it before in CL and other LISPs and expression languages. It's a bit scary in JS, what with OCAP the fundamental security model. So I don't think we should treat it lightly and TC39 has not, so far.

# Thaddee Tyl (11 years ago)

From: Brendan Eich

From: Isaac Schlueter

Yes, an identifier is required.  It would not be possible to define an unnamed function in this way.

Why not express an anonymous function, though? Definition != expression. As usual, an expression statement could not start with ( and consist entirely of a function-keyword-free anonymous function expression.

The use of a compulsory identifier is a bright idea. Having an identifier guarantees that we always have a function name in the stack trace, and keeps open the possibility of recursion.

Besides, it is unambiguous, and, as I pointed out earlier, we can still use something like "lambda".

# David Herman (11 years ago)

On Mar 6, 2012, at 1:55 PM, Brendan Eich wrote:

The risk here may be small but people have run into it before in CL and other LISPs and expression languages. It's a bit scary in JS, what with OCAP the fundamental security model. So I don't think we should treat it lightly and TC39 has not, so far.

Perhaps. OTOH, the half-measure of only partly shortening JS functions, i.e.:

myList.map((x) { return x + 1 })

vs

myList.map((x) { x + 1 })

or

myList.map({|x| x + 1})

is disappointing. Moreover, it essentially means abandoning ever having TCP lambda. Now, I know that some in the community look at block-lambda with suspicion (although I'm tempted to call BLUB -- don't knock it till you've tried it). As ever, we have kind of a Mexican stand-off between

  • block lambda (funky pipes, freaks some people out)
  • TCP-respecting shorter function (possible return-value leak, freaks some people out)
  • shorter functions with return (still verbose, possible return-value leak, precludes ever having TCP-respecting lambda)
# Brendan Eich (11 years ago)

Thaddee Tyl wrote:

From: Brendan Eich

From: Isaac Schlueter

Yes, an identifier is required. It would not be possible to define an unnamed function in this way. Why not express an anonymous function, though? Definition != expression. As usual, an expression statement could not start with ( and consist entirely of a function-keyword-free anonymous function expression.

The use of a compulsory identifier is a bright idea. Having an identifier guarantees that we always have a function name in the stack trace, and keeps open the possibility of recursion.

That's an independent consideration. The identifier does not help or hurt, syntactically.

Besides, it is unambiguous,

Not without a [no LineTerminator here] restriction between the ) and { and with that, there's no requirement for the identifier as a disambuatiion device.

and, as I pointed out earlier, we can still use something like "lambda".

You can call it whatever you like, but anonymous means something different in a function expression today vs. named function expression. Without inventing new semantics, considering only syntactic sugar, there is no benefit to rejecting anonymous short functions.

# Rick Waldron (11 years ago)

On Tue, Mar 6, 2012 at 5:14 PM, David Herman <dherman at mozilla.com> wrote:

On Mar 6, 2012, at 1:55 PM, Brendan Eich wrote:

The risk here may be small but people have run into it before in CL and other LISPs and expression languages. It's a bit scary in JS, what with OCAP the fundamental security model. So I don't think we should treat it lightly and TC39 has not, so far.

Perhaps. OTOH, the half-measure of only partly shortening JS functions, i.e.:

myList.map((x) { return x + 1 })

vs

myList.map((x) { x + 1 })

or

myList.map({|x| x + 1})

is disappointing. Moreover, it essentially means abandoning ever having TCP lambda. Now, I know that some in the community look at block-lambda with suspicion (although I'm tempted to call BLUB -- don't knock it till you've tried it). As ever, we have kind of a Mexican stand-off between

  • block lambda (funky pipes, freaks some people out)
  • TCP-respecting shorter function (possible return-value leak, freaks some people out)
  • shorter functions with return (still verbose, possible return-value leak, precludes ever having TCP-respecting lambda)

I'm going to stick my neck out and my foot in my mouth... I would prefer block lambdas, because of the function expression scope win, but like you said, funky pipes are freaky ;)

Here's where the foot goes in the mouth... Why couldn't ()[no LineTerminatorHere]{} be used with block lambda's semantics?

ducks

# Brendan Eich (11 years ago)

David Herman wrote:

On Mar 6, 2012, at 1:55 PM, Brendan Eich wrote:

The risk here may be small but people have run into it before in CL and other LISPs and expression languages. It's a bit scary in JS, what with OCAP the fundamental security model. So I don't think we should treat it lightly and TC39 has not, so far.

Perhaps. OTOH, the half-measure of only partly shortening JS functions, i.e.:

 myList.map((x) { return x + 1 })

vs

 myList.map((x) { x + 1 })

or

 myList.map({|x| x + 1})

is disappointing.

I'm kind of encouraged by Isaac raising this idea, since we didn't (AFAICR) didn't consider a restricted production previously, but anyway, the hard case for what you propose is not the shortest case.

You didn't show the expression-bodied variant:

 myList.map((x) x + 1)

which is even shorter, if it's doable at all (I haven't worked out a good grammar yet).

Longer braced-body cases are where completion-leaks can hide. If you write functional/expression-langauge-y JS then you'll be ok. If you start to write control statements with early returns you may run into trouble.

Moreover, it essentially means abandoning ever having TCP lambda.

Why?

Now, I know that some in the community look at block-lambda with suspicion (although I'm tempted to call BLUB -- don't knock it till you've tried it). As ever, we have kind of a Mexican stand-off between

  • block lambda (funky pipes, freaks some people out)

We went around the block with {(...)...} vs. {|...|...} and everyone jumped back on the pipes bandwagon. The issue I still see as red meat for the BLUB beast is either TCP in general, or just too different-from-C syntactic aversion.

  • TCP-respecting shorter function (possible return-value leak, freaks some people out)

My point remains that TCP-anything should be delimited by new and freaky-deaky syntax. Or else it will (ceteris paribus) result in more completion leaks or mistargeted returns or whatever.

  • shorter functions with return (still verbose, possible return-value leak, precludes ever having TCP-respecting lambda)

I do not see why you write "precludes".

Otherwise I see a similar non-trilemma:

  1. TCP
  2. Short different-from-function syntax
  3. Shorter similar-to-function syntax.

I believe you can have 1 and 2 but not 1 and 3. I see no reason why you can't have 2&3, though, so all of 1-3 is conceivable (we discussed 'fn' as shorthand for 'function' last time and considered block lambdas as fitting too -- need methods&blocks not just blocks).

# David Herman (11 years ago)

On Mar 6, 2012, at 2:30 PM, Rick Waldron wrote:

Here's where the foot goes in the mouth... Why couldn't ()[no LineTerminatorHere]{} be used with block lambda's semantics?

I'm sayin'. :)

Seriously, though, Brendan's objecting (as MarkM has in the past) that

(x) { f(x); }

is more likely to be an accidental leak of the return value of the call to f than

{|x| f(x); }

is. That claim looks a little hard to believe, but MarkM does have anecdotal evidence (basically, his experience with E) that the former style, by virtue of looking like a traditional function body, is more likely to lead the programmer to forget that it implicitly returns the result of the last expression statement.

# David Herman (11 years ago)

On Mar 6, 2012, at 2:37 PM, Brendan Eich wrote:

You didn't show the expression-bodied variant:

myList.map((x) x + 1)

which is even shorter, if it's doable at all (I haven't worked out a good grammar yet).

That's actually a pretty good space to aim at, IMO. I conjecture the majority cases where you really want the concise notation is for an expression body. Moreover, if you wanted to do some control along with an expression, you could use do-expressions:

myList.map((x) do { for (...) { ... } f(x) })

Note that combination (expression closures + do-expressions) brings back the exact same expressiveness of block-lambda. Of course, I consider that to be a feature, not a bug!

Longer braced-body cases are where completion-leaks can hide. If you write functional/expression-langauge-y JS then you'll be ok. If you start to write control statements with early returns you may run into trouble.

I can believe that. Do you think do-expressions would mitigate the risk? I suspect they might, by virtue of the explicit opt-in via the keyword at the head.

Moreover, it essentially means abandoning ever having TCP lambda.

Why?

Just because there are only so many variations on function syntax we're going to add to the language.

Now, I know that some in the community look at block-lambda with suspicion (although I'm tempted to call BLUB -- don't knock it till you've tried it). As ever, we have kind of a Mexican stand-off between

  • block lambda (funky pipes, freaks some people out)

We went around the block with {(...)...} vs. {|...|...} and everyone jumped back on the pipes bandwagon. The issue I still see as red meat for the BLUB beast is either TCP in general, or just too different-from-C syntactic aversion.

Yes, it was TCP I was referring to.

  • TCP-respecting shorter function (possible return-value leak, freaks some people out)

My point remains that TCP-anything should be delimited by new and freaky-deaky syntax. Or else it will (ceteris paribus) result in more completion leaks or mistargeted returns or whatever.

I hereby propose expression closures + do-expressions as the new, freaky-deaky syntax. Thoughts?

# Brendan Eich (11 years ago)

David Herman wrote:

On Mar 6, 2012, at 2:37 PM, Brendan Eich wrote:

You didn't show the expression-bodied variant:

myList.map((x) x + 1)

which is even shorter, if it's doable at all (I haven't worked out a good grammar yet).

That's actually a pretty good space to aim at, IMO. I conjecture the majority cases where you really want the concise notation is for an expression body.

Yes, it's a sweet spot (take a look at prototypejs's use of function expressions as parameter to inject calls, where the callback returns a value. Almost all expression bodied, a few overlong ones that must have a final return).

Moreover, if you wanted to do some control along with an expression, you could use do-expressions:

 myList.map((x) do { for (...) { ... } f(x) })

Note that combination (expression closures + do-expressions) brings back the exact same expressiveness of block-lambda.

Not so, because no TCP. Only block-lambdas preserve TCP fully.

If you mean only completion-value implicit return, sure, but that's not enough to expression the exact same things as block-lambdas.

Longer braced-body cases are where completion-leaks can hide. If you write functional/expression-langauge-y JS then you'll be ok. If you start to write control statements with early returns you may run into trouble.

I can believe that. Do you think do-expressions would mitigate the risk? I suspect they might, by virtue of the explicit opt-in via the keyword at the head.

Maybe, and I like do expressions, but it's a bit of serendipity here

# David Herman (11 years ago)

On Mar 6, 2012, at 2:56 PM, Brendan Eich wrote:

David Herman wrote:

Moreover, if you wanted to do some control along with an expression, you could use do-expressions:

myList.map((x) do { for (...) { ... } f(x) })

Note that combination (expression closures + do-expressions) brings back the exact same expressiveness of block-lambda.

Not so, because no TCP. Only block-lambdas preserve TCP fully.

Says you! :)

To be clear: what I'm suggesting is that expression closures would not be syntactic sugar for statement bodies, but rather would be TCP-respecting. Most of the time this wouldn't even be noticeable, until you use do-expressions.

# Domenic Denicola (11 years ago)

This wins on so many levels:

  • Short but C-like myList.map((x) x*x) syntax for the most common cases, where TCP doesn't matter.
  • Full TCP support via an entirely new, but still pretty C-like, syntax with myList.map((x) do { … }).
  • The two are sufficiently close to each other and to the existing syntax to be recognizable as functions, but sufficiently different to convey new semantics.

The earlier Prototype 1.6 example:

inGroupsOf(number, fillWith) { fillWith = Object.isUndefined(fillWith) ? null : fillWith; return this.eachSlice(number, (slice) do { while(slice.length < number) slice.push(fillWith); slice; }); },

inject(memo, iterator, context) { iterator = iterator.bind(context); this.each((value, index) do { memo = iterator(memo, value, index); }); return memo; },

invoke(method) { var args = $A(arguments).slice(1); return this.map((value) valuemethod); },

From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of David Herman Sent: Tuesday, March 06, 2012 17:58 To: Brendan Eich Cc: es-discuss Subject: Re: optional "function" keyword

On Mar 6, 2012, at 2:56 PM, Brendan Eich wrote:

David Herman wrote:

Moreover, if you wanted to do some control along with an expression, you could use do-expressions:

myList.map((x) do { for (...) { ... } f(x) })

Note that combination (expression closures + do-expressions) brings back the exact same expressiveness of block-lambda.

Not so, because no TCP. Only block-lambdas preserve TCP fully.

Says you! :)

To be clear: what I'm suggesting is that expression closures would not be syntactic sugar for statement bodies, but rather would be TCP-respecting. Most of the time this wouldn't even be noticeable, until you use do-expressions.

# Herby Vojčík (11 years ago)

I like this, too (to use this as a TCP expression).

It is even sorta logically readable. If you encounter do expression without (args), it is naturally TCP. If you precede it with (args), it is easy to read it as "do expression with args, to be evaluated on-demand", so TCP as well.

Though I understand you are trying to find shorter function syntax, not a lambda replacement... but:

  • is it needed (it would be nice, and could be in par with method definition in object literal) when (args) do {...} fills the spot in majority of short ad-hoc callback scenario?
    • couldn't Isaac's (args) {...} exists alongside of it? do expression is, by definition, a TCP things, since there is do; do-less variant declares/expresses old-style function? They may look similar, but they are really different.
# Andreas Rossberg (11 years ago)

I'm a little bit confused here. Wasn't arrow syntax "(args) -> expr"

shot down earlier because it is too difficult to parse for both human and machine? I don't understand how "(args) expr" can possibly be any better in this regard. (And I don't believe that a mandatory function name in front, like suggested by Isaac, makes a difference either way.)

If we are going for any of this (and I'm all for it!), then the arrow form seems much more readable (even if -- or rather, because -- it is 2 characters longer).

# Russell Leggett (11 years ago)

On Wed, Mar 7, 2012 at 4:41 AM, Andreas Rossberg <rossberg at google.com>wrote:

I'm a little bit confused here. Wasn't arrow syntax "(args) -> expr" shot down earlier because it is too difficult to parse for both human and machine? I don't understand how "(args) expr" can possibly be any better in this regard. (And I don't believe that a mandatory function name in front, like suggested by Isaac, makes a difference either way.)

I missed this step as well. I think you guys are just on the same brain wavelength or something.

# Brendan Eich (11 years ago)

Big grammatical issues remain unresolved with the expression-bodied short function syntax. For sure.

But ignoring expression bodies, just considering any shorter function syntax that starts with ( params ) ..., we will have trouble reaching consensus in Ecma TC39. To recap a bit:

From strawman:arrow_function_syntax

"This cover grammar approach may be future-hostile without, e.g., extending guards strawman:guards to be legal in

expressions."

We could resolve to allow guards in expressions, and get past this one.

Another lingering objection to arrows is this-binding (=> vs. ->).

Another minor objection is to having arrows + curly braces (vs. alternatives such as block-lambdas). But JS won't have indentation-based blocks, and block-lambdas are incommensurate in some ways with mere shorter function syntax.

# Brendan Eich (11 years ago)

David Herman wrote:

To be clear: what I'm suggesting is that expression closures would not be syntactic sugar for statement bodies, but rather would be TCP-respecting. Most of the time this wouldn't even be noticeable, until you use do-expressions.

I'm not sure do expression syntax is freaky-deaky enough. Maybe, maybe not.

Also, if we hope to get shorter function syntax into ES6 this is a bridge too far. Later, sure. Just a consideration. I could see reaching consensus on shorter function syntax sooner than on a new f-d TCP form.

# Rick Waldron (11 years ago)

On Wed, Mar 7, 2012 at 11:11 AM, Brendan Eich <brendan at mozilla.org> wrote:

David Herman wrote:

To be clear: what I'm suggesting is that expression closures would not be syntactic sugar for statement bodies, but rather would be TCP-respecting. Most of the time this wouldn't even be noticeable, until you use do-expressions.

I'm not sure do expression syntax is freaky-deaky enough. Maybe, maybe not.

Also, if we hope to get shorter function syntax into ES6 this is a bridge too far. Later, sure. Just a consideration. I could see reaching consensus on shorter function syntax sooner than on a new f-d TCP form.

Honestly, I'm starting to believe that most nay-sayers would get over block-lambda looking weird at first and learn to really love the benefit it provides. Sure they might say "it looks really bizarre", but they will also say "remember when we had to assign var that = this; or use bind()? The dark ages!! I love block-lambda!"

Even for me, it was hard to look past the weirdness, but the more I read about "freaky-deaky" syntax, the more it begins to make sense.

/two cents.

# Brendan Eich (11 years ago)

David Herman wrote:

On Mar 7, 2012, at 8:09 AM, Brendan Eich wrote:

But ignoring expression bodies, just considering any shorter function syntax that starts with ( params ) ..., we will have trouble reaching consensus in Ecma TC39. To recap a bit:

From strawman:arrow_function_syntax

"This cover grammar approach may be future-hostile without, e.g., extending guardsstrawman:guards to be legal in expressions."

I'm with Andreas -- don't you have the exact same problem with mandatory-identifier? |f(x, y, z)| requires a cover grammar just the same as |(x, y, z)| does.

Yes, I did not say an optional identifier in front of ( somehow helps. It doesn't (think I've been clear on that previously; sorry for any confusion above).

My point in reply to Andreas was that making ( expr ) or ( args ) cover ( params ) is a problem if the goal is to reach consensus in TC39.

We could resolve to allow guards in expressions, and get past this one.

Sure. Just checking: is there something about Isaac's proposal (or variations on it) that somehow circumvents these issues? If so I'm missing something.

No, I started with "just considering any shorter function syntax that starts with ( params )..." on purpose, emphasis added.

We need an introductory keyword or sigil of some kind, not just a random one -- or we need different bracketing (a la block lambdas).

# Gavin Barraclough (11 years ago)

On Mar 7, 2012, at 8:35 AM, Rick Waldron wrote:

Honestly, I'm starting to believe that most nay-sayers would get over block-lambda looking weird at first and learn to really love the benefit it provides. Sure they might say "it looks really bizarre", but they will also say "remember when we had to assign var that = this; or use bind()? The dark ages!! I love block-lambda!"

I think there are more valid criticisms than the bizarre look alone. The block lambda syntax has the unintuitive restriction that only a subset of expressions may be used in an initializer: {|a = b&c| a} // valid {|a = b&&c| a} // invalid (This arises from the rule "BlockParameterInitialiser : = BitwiseXorExpression", from strawman:block_lambda_revival).

Using '|' to wrap arguments is problematic, given its existing usage within the language. There is a real advantages to a proposal that wrap arguments in parentheses, such as "optional function" based ones on this thread.

# Brendan Eich (11 years ago)

Gavin Barraclough wrote:

On Mar 7, 2012, at 8:35 AM, Rick Waldron wrote:

Honestly, I'm starting to believe that most nay-sayers would get over block-lambda looking weird at first and learn to really love the benefit it provides. Sure they might say "it looks really bizarre", but they will also say "remember when we had to assign var that = this; or use bind()? The dark ages!! I love block-lambda!"

I think there are more valid criticisms than the bizarre look alone. The block lambda syntax has the unintuitive restriction that only a subset of expressions may be used in an initializer: {|a = b&c| a} // valid {|a = b&&c| a} // invalid (This arises from the rule "BlockParameterInitialiser : = BitwiseXorExpression", from strawman:block_lambda_revival).

The obvious workaround: parenthesize. This is arguably small potatoes given parameter default values being new.

Using '|' to wrap arguments is problematic, given its existing usage within the language. There is a real advantages to a proposal that wrap arguments in parentheses, such as "optional function" based ones on this thread.

But (modulo separate "do" TCP proposal of yesterday) shorter function syntax is just syntax. No TCP, no lexical |this| in particular.

Just replying to try to assign weights. The | vs. pdv issue is small IMHO. The incommensurate nature of block-lambdas vs. shorter functions hangs on TCP, which is big (bigger, anyway).

# Gavin Barraclough (11 years ago)

On Mar 7, 2012, at 11:54 AM, Brendan Eich wrote:

The obvious workaround: parenthesize. This is arguably small potatoes given parameter default values being new.

Using '|' to wrap arguments is problematic, given its existing usage within the language. There is a real advantages to a proposal that wrap arguments in parentheses, such as "optional function" based ones on this thread. But (modulo separate "do" TCP proposal of yesterday) shorter function syntax is just syntax. No TCP, no lexical |this| in particular.

Just replying to try to assign weights. The | vs. pdv issue is small IMHO. The incommensurate nature of block-lambdas vs. shorter functions hangs on TCP, which is big (bigger, anyway).

You're absolutely right that the semantics are a more weighty issue, and should be the primary concern. We do also need to discuss the syntax too, and I think it's worthwhile highlighting a concrete advantage of Dave's "do" proposal (or the arrow function syntax strawman) over the syntax in the block lambda strawman.

# Isaac Schlueter (11 years ago)

This might be a dumb question: TCP = "this-context preservation"? (Sorry, my brain has that acronym hard-linked to "Transport Control Protocol" and I keep getting confused.)

On Wed, Mar 7, 2012 at 13:01, Gavin Barraclough <barraclough at apple.com> wrote:

But (modulo separate "do" TCP proposal of yesterday) shorter function syntax is just syntax. No TCP, no lexical |this| in particular.

Yes, "functionless function" proposal is just syntax, not a change in semantics. IIUC, reducing the syntax weight of callable-things (whether functions or freaky-deaky lambda things) is a goal in itself.

Assuming that we get "() do {}" as the freaky-deeky syntax, would that do the TCP stuff? If so, why not drop block-lambdas, and do the TCP stuff by saying that single-return-expression functions are the ones that do TCP and other freaky stuff? Then you can treat "do {...}" as an expression, somewhat like a block lambda that takes no arguments and is invoked when evaluated.

Then you really have three somewhat modest proposals:

  1. optional "function".
  2. braceless functions that auto-return the completion value of the expression and exhibit TCP freakdeekness.
  3. do-expressions blocks that evaluate to their completion value.

These three seem to combine to let you do all the good things that block lambdas offer, and are pretty elegant and easy enough to explain to new users, IMHO. The "do" token sort of looks like it means "Do this right here, not in a separate context".

It seems like #2 would require the most ironing out. For example: x = (y) +1 is ambiguous, as is x = (y)(z) and many many others. If we use do {...} (or something) as an unambiguous "this is an expression", then that could be a nice escape hatch. Ie, x = (y) do { 1 } would be akin to x = tcp_freaky_deeky(function (y) { return 1 }).

# Russell Leggett (11 years ago)

On Wed, Mar 7, 2012 at 4:56 PM, Isaac Schlueter <i at izs.me> wrote:

This might be a dumb question: TCP = "this-context preservation"? (Sorry, my brain has that acronym hard-linked to "Transport Control Protocol" and I keep getting confused.)

TCP = Tennent's Correspondence Principle

Yehuda Katz has a great explanation here: yehudakatz.com/2012/01/10/javascript-needs-blocks

tl;dr; It means that among other things, a return from the block would actually return from the outer function, not just the anonymous function. It lets you create stronger control abstractions.

# Kevin Smith (11 years ago)

(weighing in just as a developer...)

So this is pretty consistent with the language and would be easy to explain to current and future developers:

// Short function, familiar rules: () { // .. }

// Lexical this, tcp and all that () => { // .. }

// Or () => expr

Pipes are fine in and of themselves, but IMO you get something that looks like a frankenstien language:

mtime: function() { // use the proposed block syntax, { |args| }. this.withFile { |f| // in block lambdas, +this+ is unchanged var mtime = this.mtimeFor(f); if (mtime < new Date() - 1000) { // block lambdas return from their nearest function return "too old"; } sys.print(mtime); } },

"Fat arrows" are more aesthetically consistent and I feel like they will be easier to explain:

mtime: function() { // use fatty arrows this.withFile((f) => { // in block lambdas, +this+ is unchanged var mtime = this.mtimeFor(f); if (mtime < new Date() - 1000) { // block lambdas return from their nearest function return "too old"; }); sys.print(mtime); }); },

just my opinion...

# Claus Reinke (11 years ago)

Another lingering objection to arrows is this-binding (=> vs. ->).

If it is possible to make 'function'-free functions work, then how about dropping the implicit bindings for 'arguments' and 'this' for the short form? Then the 'function' keyword would be a modifier on the short form, to re-introduce these implicit bindings.

That would support current code while connecting the semantic difference (wrt 'this') to a more visible hint than -> vs =>.

This would still leave room for having both short functions and block lambdas, as they serve different uses

function f(..) {..} binds 'this', binds 'arguments', binds 'return'

f(..) {..} no 'this', no 'arguments', binds 'return'

{|..| ..} no 'this', no 'arguments', does not bind 'return'

(binding 'return' means that 'return' is local to this construct; not binding 'return' means that 'return' is bound non-locally, in a lexically enclosing construct).

There is at least one further dimension to this (abstracting over expressions vs abstracting over statements), so there may be room/need for another construct/modifier, to get a more orthogonal design. It is part convenience, part historical accident that Javascript has 'running a block' mixed together with 'calling a callable'.

Claus

# Herby Vojčík (11 years ago)

Claus Reinke wrote:

If it is possible to make 'function'-free functions work, then how about dropping the implicit bindings for 'arguments' and 'this' for the short form? Then the 'function' keyword would be a modifier on the short form, to re-introduce these implicit bindings. That would support current code while connecting the semantic difference (wrt 'this') to a more visible hint than -> vs =>.

This would still leave room for having both short functions and block lambdas, as they serve different uses

function f(..) {..} binds 'this', binds 'arguments', binds 'return'

f(..) {..} no 'this', no 'arguments', binds 'return'

{|..| ..} no 'this', no 'arguments', does not bind 'return'

Three is too much. IMHO. It creates a lot less discrete border and for developer it creates lots of rules to learn and complexity in thinking. I firmly believe the middle one is not needed, and if there were only two of them, the code will be more readable (and it is very important trait; I think Brendan Eich also emphasizes this with 'freaky-deaky syntax'). I think the difference need not to as freaky-deaky (I told I like 'f_opt (params) { body }' for the first and 'f_opt (params) do { body }' for the second), but there should not be more than two forms.

# Kevin Smith (11 years ago)

(Slightly off topic...)

In Yehuda Katz's examples (one of which I quoted) he's doing synchronous file stuff. Which we don't really do in javascript. Since so much of the code we write is asynchronous it seems like TCP return semantics are not really applicable. Or are they?

Thanks,

# Russell Leggett (11 years ago)

Its mostly about being able to make control abstractions - being able to make your own loops, conditionals, DSLs - and while you can get close with anonymous functions, you'll never get there, because someone will want to use 'return' or 'break' or 'throw' and the behavior is all screwed up (as we all know).

OTH - if you call a TCO obeying block asynchronously and it calls return, what happens? Does it throw an error? Is there a way to make that blow up early (maybe not statically, but as part of passing the block to the function in the first place?) If not, I fear you may add more confusion than any gain you would get with TCP.

# Herby Vojčík (11 years ago)

Russell Leggett wrote:

Its mostly about being able to make control abstractions - being able to make your own loops, conditionals, DSLs - and while you can get close with anonymous functions, you'll never get there, because someone will want to use 'return' or 'break' or 'throw' and the behavior is all screwed up (as we all know).

OTH - if you call a TCO obeying block asynchronously and it calls return, what happens? Does it throw an error? Is there a way to make that blow up early (maybe not statically, but as part of passing the block to the function in the first place?) If not, I fear you may add

You don't know if it does a bad thing, until you let it run. Alternatively, you may want to disallow any and all async lambda blocks. But from what was presented on the list, async lambda blocks are pefectly ok until they do the bad thing - only then the error occurs.

Which is ok, we are in dynamic language, after all.

# Russell Leggett (11 years ago)

On Thu, Mar 8, 2012 at 8:43 AM, Herby Vojčík <herby at mailbox.sk> wrote:

Russell Leggett wrote:

Its mostly about being able to make control abstractions - being able to make your own loops, conditionals, DSLs - and while you can get close with anonymous functions, you'll never get there, because someone will want to use 'return' or 'break' or 'throw' and the behavior is all screwed up (as we all know).

OTH - if you call a TCO obeying block asynchronously and it calls return, what happens? Does it throw an error? Is there a way to make that blow up early (maybe not statically, but as part of passing the block to the function in the first place?) If not, I fear you may add

You don't know if it does a bad thing, until you let it run. Alternatively, you may want to disallow any and all async lambda blocks. But from what was presented on the list, async lambda blocks are pefectly ok until they do the bad thing - only then the error occurs.

Which is ok, we are in dynamic language, after all.

Yeah, I was referring to any async lambda block, but I can see how that would be too restrictive. My problem is that the cognitive load goes up when having to understand more gotchas. Its a measure of overall language complexity. I can see a lot of people supplying blocks instead of functions because the syntax is so much shorter without understanding the actual difference. In ruby this probably comes up less because of what Kevin brought up, ruby is mostly synchronous.

Hmm... I'm more on the fence with this than I originally thought.

# Kevin Smith (11 years ago)

Thank you for the explanations. Block lambda syntax with pipes makes more sense to me now. Given that most of the stuff I do is asynchronous though, I personally would prioritize the semantics this way:

  1. Short function syntax with lexical this.
  2. Block lambdas with TCP return.

Thanks again,

# Andrea Giammarchi (11 years ago)

I was thinking and playing with something similar ... here an example: www.3site.eu/functionize

Looks like the "function" keyword could actually be dropped easily without compromising the syntax too much.

That is just a test, but it looks OKish to me

br

# Russell Leggett (11 years ago)

On Thu, Mar 8, 2012 at 9:35 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

I was thinking and playing with something similar ... here an example: www.3site.eu/functionize

Looks like the "function" keyword could actually be dropped easily without compromising the syntax too much.

That is just a test, but it looks OKish to me

This has been discussed, but one of the biggest issues with that syntax is grammar ambiguities and required lookahead. It is being brought up again, but that part is not a solved problem.

# Andrea Giammarchi (11 years ago)

I don't see ambiguity on (){} but as I have said it was only a test.

I'd like to know what kind of code could brake it thought :-)

# John Tamplin (11 years ago)

On Wed, Mar 7, 2012 at 8:31 PM, Kevin Smith <khs4473 at gmail.com> wrote:

// Or () => expr

You don't need TCP here, since there is no return. Bound this would be nice, since it is a new form that can't break existing code.

# Russell Leggett (11 years ago)

Bound this would be nice, since it is a new form that can't break existing code.

Yes - I think this could be pretty excellent - no pun intended. I think think it would be a great compromise between current functions and full TCP.

# Kevin Smith (11 years ago)

If I understand correctly, it's still an open question whether parsing () => {} is feasible or not using a recursive top-down parser.

Is that right?

# Brendan Eich (11 years ago)

Russell Leggett wrote:

Bound this would be nice, since it is a new form that can't break
existing code.

Yes - I think this could be pretty excellent - no pun intended. I think think it would be a great compromise between current functions and full TCP.

Again, it's a bad idea to mix too much TCP into the current function body plan, or anything derived from it. Bound-this, ok. Control effects on the outer function? No. What about 'arguments'? Just say no.

# Axel Rauschmayer (11 years ago)

IIRC, "fn" for functions (as a breaking change) was in the running, at some point. Is it still?

# Brendan Eich (11 years ago)

Kevin Smith wrote:

If I understand correctly, it's still an open question whether parsing () => {} is feasible or not using a recursive top-down parser.

Is that right?

No. Top down parsers can be hacked to do a great many things. What we seek is a grammar that can be parsed by a well-established algorithm and validated as unambiguous -- only one parse tree for a valid sentence.

ES3's grammar was validated using LR(1) plus ASI and lookahead restrictions, automatically (Waldemar's Mac Common Lisp code at cvs.mozilla.org/mozilla/js2/semantics).

Failure to disambiguate can paint us into painful corners in the future. Ambiguous grammars are "easy" to parse using top-down LL(1)-equivalent techniques -- many people write recursive descent parsers for ambiguous grammars and think everything's great, until it isn't.

That's one issue.

Another which I cited just a few messages back: parsing ( params ) as ( expr ), as any top down parser must until it gets to an arrow or other distinguishing token ({ on same line as ), e.g.), can be considered future-hostile. params should be declarative, but expr must now cover all future declarative syntax, including optional guards.

At the limit, the grammar would become a loose "cover grammar" and semantic validation of the parse tree produced for either (expr) or (params)->... would be required. That would be a mistake. We want a

fairly tight grammar, with only a few simple semantic validation steps required by runtime or mandated as early errors (e.g. return from non-function, illegal LHS of assignment).

# Aymeric Vitte (11 years ago)

Then you really have three somewhat modest proposals:

  1. optional "function".
  2. braceless functions that auto-return the completion value of the expression and exhibit TCP freakdeekness.
  3. do-expressions blocks that evaluate to their completion value.

These three seem to combine to let you do all the good things that block lambdas offer, and are pretty elegant and easy enough to explain to new users, IMHO. The "do" token sort of looks like it means "Do this right here, not in a separate context".

Indeed it looks more intuitive than block lambdas (IMHO).

A variant/extension of "() do {...}" could be "do {...}" that (if not followed by a "while") will create Function() {...} that gets evaluated when retrieveing a property ([[Get]]) or when called directly, allowing new possibilities for "this" not all feasible with block lambdas or "=>" , not impacting a lot current specs, easy to understand and use, examples :

1- this.x in prototype

 var f=(name) {
     this.name=name;
 };

 f.prototype={
     nodeName : do {return this.name}; //creates an object = 

Function() {return this.name} with a flag reminding that it was a "do" creation };

 var g=new f('test');
 //g.prototype={
 //    nodeName : do {return this.name};
 //};


 console.log(g.nodeName); //'test'
 //[[Get]] returns a function=Function() {return this.name}, if the 

do flag is true [[Get]] does evaluate it as if g.nodeName was called (g.nodeName() --> function.[Call]) //this is evaluated to "this.name", where "this" does refer to g

2- closures

 //old way
 var a={name : 'test'};

 var f=function () {
     var self=this;
     (function() {console.log(self.name) })();
 };

 f.call(a) --> 'test'

 //new way
 var a={name : 'test'};

 var f=() {
     (do {console.log(this.name)})();
 };

 f.call(a) --> 'test'

 //old way
 function writeNodes() {
   var self = this
   var writeall = function() {
     for (var n in self.nodes) {
         self.write(n);
     }
   }
   writeall();
 }

 //new way
 writeNodes() {
     var writeall = () do {
         for (var n in this.nodes) {
             this.write(n);
         }
     };
     writeall();
 }

3- json

 var x={
     a: 10,
     b: do {this.a},
     c: {
         d: do {return GetBase(this)}
     }
 };

 console.log(x.b);
 //[[Get]] returns Function() {return this.a} and evaluate it (see 

above) //it is evaluated to "this.a" where "this" does refer to x //returns 10

 console.log(x.c.d);
 //[[Get]] returns Function() {return GetBase(this)} and evaluates 

it (see above) //it is evaluated to GetBase(this) where "this" does refer to x.c //returns x

 var x={
     a: do (this.b),
     b: do {this.a}
 };

 console.log(x.b); //infinite loop...

4- lexical this (??)

 var test = 'aaa';
 var o = {test:'bbb'};
 o.m = do {return this.test};
 var f=o.m;

 o.m(); //bbb
 f(); //aaa

 No improvement here, but if I am correct the improvement with block 

lambdas is not obvious too :

 var test = 'aaa';
 var o = {test:'bbb'};
 o.m = {|| return this.test};
 var f=o.m;

 o.m(); //aaa
 f(); //aaa
# Brendan Eich (11 years ago)

Aymeric Vitte wrote:

Indeed it looks more intuitive than block lambdas (IMHO).

Intuitions vary, but why does

function huh(a, b) { let toss = () do{return a*b}; downward(toss); return 42; }

where downward, if it calls its argument, forces a return of a*b from huh, and control never reaches the return 42, seem "intuitive" to you? Does the () do ... syntax by itself convey a "Tennent sequel"?

Block-lambdas have precedent, intuitive or not, going back to Smalltalk via Ruby, of behaving in this different and (to some) surprising way. They look odd enough to better call attention to the novelty, in my opinion, than a mix of () and do.

function huh(a, b) { let lambda = {|| return a*b}; downward(toss); return 42; }

Anyway, the current proposal has grammatical issues already plaguing arrow function syntax. Block-lambdas have no such problems. But this is not a contest: we could have shorter function syntax (e.g., 'fn' if not some prefix-less proposal that solves the grammar problems). And we could have block-lambdas on top.

What we won't have is full TCP in a function-body-plan. Sufficiently distinct semantics need markedly different syntax. Is '()do' freaky-deaky enough? I don't think so, right now.

# John Tamplin (11 years ago)

On Thu, Mar 8, 2012 at 1:08 PM, Brendan Eich <brendan at mozilla.org> wrote:

Another which I cited just a few messages back: parsing ( params ) as ( expr ), as any top down parser must until it gets to an arrow or other distinguishing token ({ on same line as ), e.g.), can be considered future-hostile. params should be declarative, but expr must now cover all future declarative syntax, including optional guards.

For this particular problem, the Dart parser looks ahead to see if it could be a function declaration. If so, it parses it that way, otherwise it parses as a parenthesized expression. I don't know if this sort of approach would be considered acceptable here or not (it does impose some restrictions on the scanner for n-token lookahead).

# Aymeric Vitte (11 years ago)

I forgot to mention that the call to () do {...} should work as written in 11.1.7 in block_lambda_revival (if not my second example does not work), so the behavior is supposed to be the same (if [[FormalParameters]] is empty), I was more focused on the "this" stuff (do examples 1 and 3 look useless ?).

I did not say that block lambdas were not interesting and powerfull, I just think that this might be difficult to swallow/use by developpers, so an intermediate approach maybe was not bad, but apparently it is no possible.

Le 08/03/2012 19:22, Brendan Eich a écrit :

# Kevin Smith (11 years ago)

Thanks for the clear explanation, Brendan - I think I understand the concerns now. And if not, I don't mind looking foolish : )

I just looked at the code for the Dart parser and it basically queues tokens until it finds the matching right paren (recursively counting nested parens). It peeks at what comes afterward (like a => or {), and then it

goes back to the original left paren and resumes parsing, dequeueing the already scanned tokens.

Would this technique work for us? The problem that I see is that in JS, you can't really tokenize without also parsing (because of the cursed lexical ambiguity on '/').

# Kevin Smith (11 years ago)

I think we can use a modification of the Dart technique to parse "=>"

functions:

On encountering "(" in a context where an expression is acceptable, you can try to parse it as ( Expression ) or as ( FormalParameterList ).

Attempt to parse it as a formal parameter list, enqueuing each token encountered. Either the parameter list will be successfully parsed or not.

If a parameter list is successfully parsed, then

peek at the next token to see if it's a "=>". If it is, then clear the queued tokens and continue parsing as normal. If it's not a "=>", then put the queued tokens back into the token stream and restart parsing from the original "(", this time as an expression.

otherwise, if we fail to parse the parameter list, then

put the queued tokens back into the token stream and restart parsing from the original "(", this time as an expression.

Lemma: The "lookahead" token stream that we queue while attempting to parse ( FormalParameterList ) will be identical to the token stream that we would have queued if we had attempted to parse ( Expression ).

Thanks,

# Isaac Schlueter (11 years ago)

So, assuming that these two are valid function expressions that mean roughly the same thing:

 a: (x) { return "x" }
 b: function (x) "x"

and then you might conclude that this is as well:

 c: (x) "x"

So far, so good, I think.

What about these?

d: (x) (y)
e: (x) (y) { return "z" }
f: (x) (y) ("z")
g: (x) (y) "z"

Leaving aside block lambda TCP freaky-deakiness, I'm not sure mentally how to even parse those. I suppose you could just say that you're not allowed to start an expression-bodied-auto-return function with a (, maybe?

I'm not sure. But it is certainly weird and confusing.

If e and g both desugar to:

h: function (x) { return function (y) { return "z" }}

then that could be extremely useful to me. Every ceremonial token has been stripped, leaving only the meaningful bits. It's especially lovely for curry-able functions that are multiply-called:

i: curryableFn (a) (b) (c, d) {  .. do lots of stuff with a, b, c,

and d .. }

gogoAsync(args, curryableFn(1)(2)) // calls the resulting cb, a

and b pre-set.

# Herby Vojčík (11 years ago)

Isaac Schlueter wrote:

So, assuming that these two are valid function expressions that mean roughly the same thing:

  a: (x) { return "x" }
  b: function (x) "x"

and then you might conclude that this is as well:

  c: (x) "x"

So far, so good, I think.

What about these?

 d: (x) (y)
 e: (x) (y) { return "z" }
 f: (x) (y) ("z")
 g: (x) (y) "z"

Leaving aside block lambda TCP freaky-deakiness, I'm not sure mentally how to even parse those. I suppose you could just say that you're not allowed to start an expression-bodied-auto-return function with a (, maybe?

I'm not sure. But it is certainly weird and confusing.

If e and g both desugar to:

 h: function (x) { return function (y) { return "z" }}

+1! (how else should they be? IMO, this is the only sane interpretation)

Alas :-/ you would need infinite lookahead since you can't tell it from the call. The best thing would be if there are other parens to use instead of ( and ) for this(ese) shorter function(s).

[=x, y] x+y // function (x, y) { return x+y; } [=x] [=y] "z" // function (x) { return function (y) { return "z"; }; } [=x] (y) "z" // error anyway [=x] (y) (z) // 1. function (x) { return (y)(z); } | // 2. (function (x) { return (y); })(z); // Probably the former.

I also though about using <x, y> x+y <x> <y> "z" <x> (y) "z" <x> (y) (z)

I can see only minor problems: cannot pack tightly with < and << (but who would compare or bitshift by function; this should not happen often) and parameter default values using '>'.

Possible Smalltalk-inspired precedent is also: (:x, y) x+y (:x) (:y) "z" (:x) (y) "z" (:x) (y) (z) I think I like this most...

# Herby Vojčík (11 years ago)

Herby Vojčík wrote:

Possible Smalltalk-inspired precedent is also: (:x, y) x+y (:x) (:y) "z" (:x) (y) "z" (:x) (y) (z) I think I like this most...

Herby

Maybe this could even be competely paren-free (tell me where it breaks):

:x, :y x+y :x :y "z" :x (y) "z" :x (y) (z)

Imagine the code like coll.map(:x x*x) or coll.filter(:x x%2==1).

# Brendan Eich (11 years ago)

We could add such an ad-hoc-GLR-like algorithm to ECMA-262, but we would lose the LR(1) (modulo ASI and lookahead restrictions, and the conventional shift-reduce conflict resolution for dangling else) validation I wrote about. That's considered unacceptable by TC39 members who spoke up last time we talked about this.

But let's say we find a way forward on the validation front. Can't assume this (paging Waldemar) but for now let's waive it.

Then the grammar would look something like this:

FunctionDeclaration: function Identifier ( FormalParameterList_opt ) { FunctionBody } Identifier ( FormalParameterList_opt ) [no LineTerminator here] { FunctionBody }

ShortFunctionExpression: Identifier_opt ( FormalParameterList_opt ) [no LineTerminator here] { FunctionBody } Identifier_opt ( FormalParameterList_opt ) => InitialValue

Fat-arrow binds lexical |this| as in CoffeeScript and all right-thinking languages :-P.

Change AssignmentExpression like so:

AssignmentExpression: ConditionalExpression LeftHandSideExpression = InitialValue LeftHandSideExpression AssignmentOperator InitialValue

AssignmentExpressionNoIn: ConditionalExpressionNoIn LeftHandSideExpression = InitialValueNoIn LeftHandSideExpression AssignmentOperator InitialValueNoIn

And change all uses of /AssignmentExpression/ outside of the /Expression/ sub-grammar to /InitialValue/:

ElementList : // See 11.1.4 Elision_opt InitialValue ElementList , Elision_opt InitialValue

PropertyAssignment : // See 11.1.5 PropertyName : InitialValue

ArgumentList : // See 11.2 InitialValue ArgumentList , InitialValue

Initialiser : // See 12.2 = InitialValue

InitialiserNoIn : // See 12.2 = InitialValueNoIn

The cherry on top is the new InitialValue production:

InitialValue : AssignmentExpression ShortFunctionExpression

This avoids precedence inversion and restricts short function expressions to appearing unparenthesized only where initial values may occur today (in array and object literals, in argument lists, on the right of assignment operators, and in variable initialisers).

# Kevin Smith (11 years ago)

LR(1) is for granddads ; )

This is really sexy.

Question: for ShortFunctionExpression, do we need an additional "=>" form

for this-binding a function body?

ShortFunctionExpression: Identifier_opt ( FormalParameterList_opt ) [no LineTerminator here] { FunctionBody } Identifier_opt ( FormalParameterList_opt ) => { FunctionBody } Identifier_opt ( FormalParameterList_opt ) => InitialValue

Thanks,

# Herby Vojčík (11 years ago)

You want non-this-bound callback, too... or do you want to force them to use function?

Herby

P.S.: I would even see place for non-this bound, InitialValue-based ones, like fetchers of value, shortcuts for function () { return this.r; }

# Brendan Eich (11 years ago)

Herby Vojčík wrote:

Herby Vojčík wrote:

Possible Smalltalk-inspired precedent is also: (:x, y) x+y (:x) (:y) "z" (:x) (y) "z" (:x) (y) (z) I think I like this most...

Herby

We've talked about these variations, but they all seem grawlixy or worse (the C++ pre-11x bug with template instantiation nesting where you have to put a space between > and > to avoid writing a right-shift operator).

I agree this one is nicer but it's still grawlixy, or emoticon-y ;-). It does avoid the ambiguous grammar worry, on the up side.

The fat-arrow, =>, is better, and it can be made to work if we figure

out and agree to pay for the spec-grammar validation against ambiguity work.

Maybe this could even be competely paren-free (tell me where it breaks):

:x, :y x+y :x :y "z" :x (y) "z" :x (y) (z)

Imagine the code like coll.map(:x x*x) or coll.filter(:x x%2==1).

Formal parameter list syntax should include all the bells and whistles (rest parameter optional at end, paramater default values for trailing formals, destructuring patterns) so we should be conservative. One-parameter paren-free special casing, as in C#, is plausible.

x => xx (x,y) => xy

Thanks for throwing out alternative syntaxes. We should keep beating on this anvil, shorter function syntax is well worth it. But we need a non-GLR parsing procedure that rejects ambiguous grammars.

# Brendan Eich (11 years ago)

Kevin Smith wrote:

LR(1) is for granddads ; )

Knuth. Respect.

This is really sexy.

Question: for ShortFunctionExpression, do we need an additional "=>" form for this-binding a function body?

ShortFunctionExpression: Identifier_opt ( FormalParameterList_opt ) [no LineTerminator here] { FunctionBody } Identifier_opt ( FormalParameterList_opt ) => { FunctionBody }

We could do that and leave the arrow-free form with unbound |this|. Better by my argument that anything based on today's function body-plan should not start adding TCP conformance -- need new (fat arrow if not freaky-deaky) syntax for new semantics (some or all of TCP).

# Brendan Eich (11 years ago)

Kevin Smith wrote:

InitialValue : AssignmentExpression ShortFunctionExpression

This avoids precedence inversion and restricts short function expressions to appearing unparenthesized only where initial values may occur today (in array and object literals, in argument lists, on the right of assignment operators, and in variable initialisers).

To allow a parenthesized short function expression, we'll also need

PrimaryExpression: ... ( ShortFunctionExpression )

which really walks back into the parsing issue that I waived, but it's already an issue without this production. With this production, one can write short function expressions nested within larger expressions that are not entirely the initialiser in some literal or arg-list. Seems worth considering.

# Brendan Eich (11 years ago)

If the shorter syntax is derived from function's body-plan we do not want arbitrary TCP creep. I think Alex Russell made this point at a TC39 meeting last year (regarding HoBD hash functions). Shorter syntax is just syntax, anything novel (=>) can change semantics too, with more

novelty or freaky looks required for wilder semantic shifts.

# Kevin Smith (11 years ago)

Knuth. Respect.

For sure - LR parsing still blows my mind (looks for his trusty dragon book

  • here it is!).

We could do that and leave the arrow-free form with unbound |this|. Better

by my argument that anything based on today's function body-plan should not start adding TCP conformance -- need new (fat arrow if not freaky-deaky) syntax for new semantics (some or all of TCP).

I agree and think that fat arrows should indicate bound this wherever fat arrows appear.

=> bound this { |x, y| } bound this, TCP control semantics, etc...

I'm not sold on the utility of block lambdas (because it's all gone async these days) but fat arrows would be immediately useful to me. "var self = this" no more...

# Brendan Eich (11 years ago)

Kevin Smith wrote:

Knuth. Respect.

For sure - LR parsing still blows my mind (looks for his trusty dragon book - here it is!).

We could do that and leave the arrow-free form with unbound
|this|. Better by my argument that anything based on today's
function body-plan should not start adding TCP conformance -- need
new (fat arrow if not freaky-deaky) syntax for new semantics (some
or all of TCP).

I agree and think that fat arrows should indicate bound this wherever fat arrows appear.

=> bound this { |x, y| } bound this, TCP control semantics, etc...

I'm not sold on the utility of block lambdas (because it's all gone async these days) but fat arrows would be immediately useful to me. "var self = this" no more...

I originally wrote up

strawman:arrow_function_syntax

and

strawman:block_lambda_revival

as mutually exclusive alternatives, but changed the framing for the latter to recognize the new semantics (beyond =>'s |this| TCP

conformance). Yet I agree that if we get shorter function syntax together, block-lambdas lose some of their "oomph".

For downward funargs called by the control flow, and so for novel control structures, with paren-free call affordances even, they still have some win. Perhaps not enough to make it into any future edition without prototyping in popular engines and grass roots pressure...

Anyway, I'm still trying to get something for shorter function syntax into ES6. I think TC39 can yet make an exception if we have our validation story figured out. That is where to focus fire.

# Russell Leggett (11 years ago)

Thanks for throwing out alternative syntaxes. We should keep beating on this anvil, shorter function syntax is well worth it. But we need a non-GLR parsing procedure that rejects ambiguous grammars.

So here's my pitch - I've been thinking over a lot of the different syntaxes that have been passed around, and I've got a combination that might just work:

// ':' is the clue that this is the start of a short function
coll.map(:x -> x*x) //function(x){ return x*x;}

// even short functions can be named
coll.filter(when: x -> x%2==1) //function when(x){ return x%2==1;}

// multiple parameters, and if the function needs a full function body
coll.forEach(:x, i -> {
    if(i%2 === 0){
        console.log(x + " is " + even);
    }else{
         console.log(x + " is " + odd);
    }

});

// the names can be an asset in more complicated callback situations
when(promise,
     ok: result -> doSomething(result),
     error: e -> handleError(e)
);

//if no parameters or name, can drop the ':'
$("#other").click(->$("#target").click());

I think this should be easy to parse, and I feel like using : keeps it from seeming too foreign.

# Kevin Smith (11 years ago)

Anyway, I'm still trying to get something for shorter function syntax into ES6. I think TC39 can yet make an exception if we have our validation story figured out. That is where to focus fire.

Must it be LR(1), then? I don't think an LR parser can handle short functions (or arrows) as we've laid them out. Essentially, we can't reduce anything until we know whether we're trying to parse an ( Expression ) or a ( FormalParameterList ). Same goes for Dart.

And yet my intuition tells me it's unambiguous nonetheless (or that it can be, if the grammar is constructed with care).

I think that the procedure I outlined yesterday can be implemented efficiently, especially for the expected case where a parameter list is short (e.g. not the length of the entire file). And since the source code of every parsed function has to stay in memory anyway (via Function.prototype.toString), I don't think the arbitrary lookahead imposes additional restrictions there.

Given this situation, what kind of validation story would be sufficient?

# Kevin Smith (11 years ago)

Sorry for the tl;dr - last post for today...

It occurs to me that we might be able to prove that the language is something like "non-deterministic LR(1)". A non-deterministic LR parser could have shift entries in it's parsing table pointing to more than one state. When it hits a cell like that, it spawns as many additional parsers as it needs to simultaneously occupy those states.

To parse short-functions, there's some cell in our NLR parsing table under the column for "(" that has more than one target state. One state would correspond to "I'm parsing an ( Expression )" and the other would correspond to "I'm parsing a ( FormalParameterList )". I bet we could prove that our NLR parser only constructs one valid parse tree for any string in the language.

# Brendan Eich (11 years ago)

Kevin Smith wrote:

Anyway, I'm still trying to get something for shorter function
syntax into ES6. I think TC39 can yet make an exception if we have
our validation story figured out. That is where to focus fire.

Must it be LR(1), then? I don't think an LR parser can handle short functions (or arrows) as we've laid them out. Essentially, we can't reduce anything until we know whether we're trying to parse an ( Expression ) or a ( FormalParameterList ). Same goes for Dart.

If Expression covers FormalParameterList then LR(1) is enough -- we'll know when we see that arrow (or { on same line as the ) before it). This is considered somewhat future-hostile by some on TC39.

If we bought into GLR (what I think you mean in your next post by NLR) then no problemo!

LR(1) is well-understood and safe. Committees do well by being conservative. We have enough in the way of exceptions on top: ASI, operand/operator role change for /, restricted productions, -NoIn productions, and dangling-else disambiguation (easy but it's on the list).

And yet my intuition tells me it's unambiguous nonetheless (or that it can be, if the grammar is constructed with care).

Intuition is not as reliable as a validated-unambiguous grammar. We had let-expressions and expression closures in ES4, both ran into trouble even though "intuitive".

I think that the procedure I outlined yesterday can be implemented efficiently, especially for the expected case where a parameter list is short (e.g. not the length of the entire file). And since the source code of every parsed function has to stay in memory anyway (via Function.prototype.toString), I don't think the arbitrary lookahead imposes additional restrictions there.

The cover grammar approach works too and requires only some AST-label staging.

Given this situation, what kind of validation story would be sufficient?

If we buy into the cover grammar approach, used so far in ES6 drafts (see "Supplemental Syntax"), then we still have some trouble if we want to prefer

(x,y) => {p:x*y} // return an object literal

over parsing the body as a block statement. The problem is that the p: looks like a label. For the arrow function syntax strawman,

strawman:arrow_function_syntax#grammar_changes

I wrote a two-token lookahead restriction. It seemed simpler than alternatives, but it's a step up from k=1 lookahead.

An alternative, mostly-compatible change I drafted:

strawman:block_vs_object_literal

The fundamental trade-off remains: object literals cannot grow extra features (~ or ~ prefixes to property names, e.g.) that make them ambiguous with block statements. This restriction is arguably not future-hostile, rather user-friendly!

# Herby Vojčík (11 years ago)

Russell Leggett wrote:

Thanks for throwing out alternative syntaxes. We should keep beating
on this anvil, shorter function syntax is well worth it. But we need
a non-GLR parsing procedure that rejects ambiguous grammars.

So here's my pitch - I've been thinking over a lot of the different syntaxes that have been passed around, and I've got a combination that might just work:

I know it's just bikeshedding, but do you need the -> arrow (it is

better visually)? You can as well take arrowless approach but putting : before every parameter (as I suggested):

 // ':' is the clue that this is the start of a short function
 coll.map(:x -> x*x) //function(x){ return x*x;}
    coll.map(:x x*x)
 // even short functions can be named
 coll.filter(when: x -> x%2==1) //function when(x){ return x%2==1;}
    coll.filter(when :x x%2==1)
 // multiple parameters, and if the function needs a full function body
 coll.forEach(:x, i -> {
     if(i%2 === 0){
         console.log(x + " is " + even);
     }else{
          console.log(x + " is " + odd);
     }
 });
    coll.forEach(:x, :i {
        if(i%2 === 0){
            console.log(x + " is " + even);
        }else{
             console.log(x + " is " + odd);
        }
    });
 // the names can be an asset in more complicated callback situations
 when(promise,
      ok: result -> doSomething(result),
      error: e -> handleError(e)
 );
    when(promise,
         ok :result doSomething(result),
         error :e handleError(e)
    );
 //if no parameters or name, can drop the ':'
 $("#other").click(->$("#target").click());

Ah, here is only problem... the arrow does it nicely when there is no parameter. This could work: $("#other").click(: $("#target").click()); and the system is changed so that only first param is colonized ;-) so previous two-param example would be coll.forEach(:x, i { if(i%2 === 0){ console.log(x + " is " + even); }else{ console.log(x + " is " + odd); } });

I think this should be easy to parse, and I feel like using : keeps it from seeming too foreign.

The rationale for arrowless one was to make trivial ones (where the clutter is most visible) as short as possible. :x x*x is one example.

# Herby Vojčík (11 years ago)

Brendan Eich wrote:

If we buy into the cover grammar approach, used so far in ES6 drafts (see "Supplemental Syntax"), then we still have some trouble if we want to prefer

(x,y) => {p:x*y} // return an object literal

over parsing the body as a block statement. The problem is that the p: looks like a label. For the arrow function syntax strawman,

This problem with {...} exists in this or that form in other places, too... I don't see is as a big problem... You should parethesize because { implies code block. We should be already used to it. IMO.

strawman:arrow_function_syntax#grammar_changes

I wrote a two-token lookahead restriction. It seemed simpler than alternatives, but it's a step up from k=1 lookahead.

An alternative, mostly-compatible change I drafted:

strawman:block_vs_object_literal

The fundamental trade-off remains: object literals cannot grow extra features (~ or ~ prefixes to property names, e.g.) that make them ambiguous with block statements. This restriction is arguably not future-hostile, rather user-friendly!

It is subjective, I think. I value expressiveness more.

# Russell Leggett (11 years ago)

I know it's just bikeshedding, but do you need the -> arrow (it is better visually)? You can as well take arrowless approach but putting : before every parameter (as I suggested):

// ':' is the clue that this is the start of a short function
coll.map(:x -> x*x) //function(x){ return x*x;}
  coll.map(:x x*x)

I really don't find this more readable at all - quite the opposite, in fact. The arrow actually helps visually see whats going on without feeling noisy. I really feel like either an arrow (fat or skinny, I don't care) or {} are required.

   coll.forEach(:x, :i {

multiple arguments in combination with a label would look really funny

  when(promise,
       ok :result doSomething(result),
       error :e handleError(e)

  );

With the : paired with the param instead of the name, it does not read well. I don't read the 'ok' as a label for what is to follow, my eyes try to read the whole thing like a sentence, and fails.

 //if no parameters or name, can drop the ':'
$("#other").click(->$("#**target").click());

Ah, here is only problem... the arrow does it nicely when there is no parameter. This could work:

  $("#other").click(: $("#target").click());

and the system is changed so that only first param is colonized ;-)

I don't think this is unambiguous, unless you are assuming that there can be no whitespace between : and the parameter.

so previous two-param example would be coll.forEach(:x, i {

      if(i%2 === 0){
          console.log(x + " is " + even);
      }else{
           console.log(x + " is " + odd);
      }
  });

I think this should be easy to parse, and I feel like using : keeps it

from seeming too foreign.

The rationale for arrowless one was to make trivial ones (where the clutter is most visible) as short as possible. :x x*x is one example.

Like I said, something as trivial as this has plenty of room for an arrow, which I think improves readability. We got rid of 'function()' {}'s and 'return' - isn't that enough?

# Brendan Eich (11 years ago)

Herby Vojčík wrote:

Russell Leggett wrote:

Thanks for throwing out alternative syntaxes. We should keep beating
on this anvil, shorter function syntax is well worth it. But we need
a non-GLR parsing procedure that rejects ambiguous grammars.

So here's my pitch - I've been thinking over a lot of the different syntaxes that have been passed around, and I've got a combination that might just work:

I know it's just bikeshedding, but do you need the -> arrow (it is better visually)? You can as well take arrowless approach but putting : before every parameter (as I suggested):

It's easy to get on thin ice without the arrow, due to destructuring parameters vs. array literal expression bodies, unary +/-, leading [ vs. [] as indexing operator, and of course /. Consider

:x = default_x [x,x*x]

That [ indexes into the default_x that was intended as the parameter default value for x. Oops.

Saying "parenthesize!" does not help if the author then parenthesizes the body:

:x = default_x ([x,x*x])

further on there could be an expression body, in which case the whole thing parses and the p.d.v. is default_x([x,x*x]).

Waldemar pointed out flaws of the general kind that affect paren-free "head" expression run together with an expression body:

esdiscuss/2011-September/016804

For paren-free statements, I plan to fix this by making newlines more significant (haven't had time to write it up). For what you've proposed, we cannot require a newline between parameter list and expression body.

So while you could come up with an unambiguous grammar, the readability is poor at the margin and maintainability goes down under obvious editing scenarios.

Again there is some future-unfriendliness but perhaps not worth much worry (but it still dogs proposals that shrug it off).

Arrows avoid all readability and future-proofing concerns that arise from running the (paren-free!) parameter list up against the expression body.

# Herby Vojčík (11 years ago)

Brendan Eich wrote:

Herby Vojčík wrote: It's easy to get on thin ice without the arrow, due to destructuring parameters vs. array literal expression bodies, unary +/-, leading [ vs. [] as indexing operator, and of course /. Consider

:x = default_x [x,x*x]

That [ indexes into the default_x that was intended as the parameter default value for x. Oops.

Saying "parenthesize!" does not help if the author then parenthesizes the body:

:x = default_x ([x,x*x])

Yes, these are good examples... thanks. Then I would +1 the Russel's paren-free syntax... less parens, more whitespace (is what I like).

# Herby Vojčík (11 years ago)

Russell Leggett wrote:

I know it's just bikeshedding, but do you need the -> arrow (it is
better visually)? You can as well take arrowless approach but
putting : before every parameter (as I suggested):


         // ':' is the clue that this is the start of a short function
         coll.map(:x -> x*x) //function(x){ return x*x;}

       coll.map(:x x*x)

I really don't find this more readable at all - quite the opposite, in fact. The arrow actually helps visually see whats going on without feeling noisy. I really feel like either an arrow (fat or skinny, I

For me it feels noisy. :x is automatically "argument x to the function" and I do not need more.

Not to argue, just to show it can be otherwise.

don't care) or {} are required.

       coll.forEach(:x, :i {

multiple arguments in combination with a label would look really funny

I do not see it as a label...

       when(promise,
            ok :result doSomething(result),
            error :e handleError(e)

       );

With the : paired with the param instead of the name, it does not read well. I don't read the 'ok' as a label for what is to follow, my eyes try to read the whole thing like a sentence, and fails.

... but as a paramter(-list)-specifier of the short function. On the contrary to what you write, I read

add :a, b (maybe ->) a+b

a lot better (sentence-wise), than

add: a, b (maybe ->) a+b

(xxx: is automatically read as property name in object literal, I feel very uneasy reading it. But hopefully, : placement is not fixed to name with you proposal and can go with (the beginning of list of) parameters.

The rationale for arrowless one was to make trivial ones (where the
clutter is most visible) as short as possible. :x x*x is one example.

Like I said, something as trivial as this has plenty of room for an arrow, which I think improves readability. We got rid of 'function()' {}'s and 'return' - isn't that enough?

Well, not, if it is possible to do nicely. I think, we should first make shortest possible variants, and if they would be too hard to read or implement, to decorate them further.

  • Russ

Herby

P.S.: I read :x as "parameter x" because I have some experience with Smalltalk. There, it is readable very fine. For people without that experience, it probably can look strange.

P.P.S.: As I wrote in reply to Brendan's mail, since arrowless variant has serious problems, I'd say ok to your one which is paren-free, using : to delimit parameter list and is pretty short.

# Herby Vojčík (11 years ago)

Brendan Eich wrote:

Herby Vojčík wrote:

Herby Vojčík wrote:

Possible Smalltalk-inspired precedent is also: (:x, y) x+y (:x) (:y) "z" (:x) (y) "z" (:x) (y) (z) I think I like this most...

Herby

We've talked about these variations, but they all seem grawlixy or worse (the C++ pre-11x bug with template instantiation nesting where you have to put a space between > and > to avoid writing a right-shift operator).

I agree this one is nicer but it's still grawlixy, or emoticon-y ;-). It does avoid the ambiguous grammar worry, on the up side.

Well, I just wanted easy way to retain familiar '(params) expr' syntax, but to use something lightweight (not % or @ or something like it) to disambiguate it so to make parsing it possible.

Dot is even more lightweight, but maybe could look grawlixy to many, too:

(.x) (.y) "z" (.x, y) x+y

or maybe better

.(x) .(y) "z" .(x, y) x+y shortFoo .(bar) bar+1

(though, if parenfree Russell's one could get in, it would probably be help more, there is many parens already)

The fat-arrow, =>, is better, and it can be made to work if we figure out and agree to pay for the spec-grammar validation against ambiguity work.

Maybe this could even be competely paren-free (tell me where it breaks):

:x, :y x+y :x :y "z" :x (y) "z" :x (y) (z)

Imagine the code like coll.map(:x x*x) or coll.filter(:x x%2==1).

Formal parameter list syntax should include all the bells and whistles (rest parameter optional at end, paramater default values for trailing formals, destructuring patterns) so we should be conservative.

Is this the problem of Russell's :x, y => x+y

as well?

One-parameter paren-free special casing, as in C#, is plausible.

x => xx (x,y) => xy

I do not like special cases much.

Thanks for throwing out alternative syntaxes. We should keep beating on this anvil, shorter function syntax is well worth it. But we need a

I am doing it now.

# Jorge (11 years ago)

On Mar 9, 2012, at 9:45 PM, Brendan Eich wrote:

I originally wrote up

strawman:arrow_function_syntax

and

strawman:block_lambda_revival

as mutually exclusive alternatives, but changed the framing for the latter to recognize the new semantics (beyond =>'s |this| TCP conformance). Yet I agree that if we get shorter function syntax together, block-lambdas lose some of their "oomph".

For downward funargs called by the control flow, and so for novel control structures, with paren-free call affordances even, they still have some win. Perhaps not enough to make it into any future edition without prototyping in popular engines and grass roots pressure...

Anyway, I'm still trying to get something for shorter function syntax into ES6. I think TC39 can yet make an exception if we have our validation story figured out. That is where to focus fire.

If short function syntax and block lambdas are mutually exclusive, then the block lambdas' syntax should be considered as an alternative for short function syntax, that is, {| params | /* body */ } is a perfectly valid candidate for short functions, without TCP.

And given that this is an invalid name for a parameter, to indicate a bound this we could simply include it in the parameters list:

{|this, arg1, arg2| /* body /} bound this {|arg1, arg2| / body */} unbound this

# Herby Vojčík (11 years ago)

Jorge wrote:

If short function syntax and block lambdas are mutually exclusive, then the block lambdas' syntax should be considered as an alternative for short function syntax, that is, {| params | /* body */ } is a perfectly valid candidate for short functions, without TCP.

And given that this is an invalid name for a parameter, to indicate a bound this we could simply include it in the parameters list:

{|this, arg1, arg2| /* body /} bound this {|arg1, arg2| / body */} unbound this

I'd say other way:

{|this, arg1, arg2| ...} // unbound this (the function has its own this) {|arg1, arg2| ...} // bound this (this is shared from outside)


And one more orthogonal thing to 'this' parameter: though imo not very pretty, it is generic: it can be used to distinguish unbound/bound this in any other new function syntax (short, TCP, etc.; if used the proposed way, even with plain old ES5 syntax, but it seems to me contrainuitive that one with 'this' shared this and one without 'this' has the real this)!

# Kevin Smith (11 years ago)

If Expression covers FormalParameterList then LR(1) is enough -- we'll know when we see that arrow (or { on same line as the ) before it). This is considered somewhat future-hostile by some on TC39.

I need to think about cover grammars some more...

(x,y) => {p:x*y} // return an object literal

Ha - brilliant! IMO this ambiguity shouldn't be blanketed over with additional lookahead and label statement restrictions.

The most bang for the buck, as I see it, is in this form:

(x, y) => { FunctionBody }

If we have implicit return (which I think we can bundle in with the "=> is

different" meme), then we have a sufficiently succinct alternative:

(x, y) => { { p:x * y} }

(I have yet to think through the issues with implicit return.)

I think (x, y) => expr can be difficult for human readers (particularly

non-experienced readers) to figure out anyway.

It might be useful to analyze a cross-section of real world code to get a feel of how often (x, y) => expr would be applicable. The sweet spot that

I'm aiming for is to make the callback syntax/semantics better, e.g:

IO.readText(path).then((text, err) => { // Many statements in here, including: this.whatever(); });

My hunch is that (x, y) => { FunctionBody } would cover >=90% of the use

cases out there, but it's just a hunch.

# Brendan Eich (11 years ago)

Jorge wrote:

If short function syntax and block lambdas are mutually exclusive, then the block lambdas' syntax should be considered as an alternative for short function syntax, that is, {| params | /* body */ } is a perfectly valid candidate for short functions, without TCP.

No, based on Ruby and Smalltalk precedent this syntax implies TCP. If you don't like TCP, use function-based shorter syntax. The choice is clear enough, and "crossing the streams" is simply going to make for more confusion, all else equal.

And given that this is an invalid name for a parameter, to indicate a bound this we could simply include it in the parameters list:

{|this, arg1, arg2| /* body /} bound this {|arg1, arg2| / body */} unbound this

The "Harmony of My Dreams" era had this optional leading |this| formal for #-functions but we did not agree on this-parameterization. What is the parameter's default value? Can .apply/.call override?

Again block-lambdas are TCP-only by their heritage. No unbound-this, no arguments, break/control/return affect enclosing function control flow. Mixing up block-like syntax and function-like semantics is strictly worse than any alternatives that hew one way or the other.

# Brendan Eich (11 years ago)

Kevin Smith wrote:

The most bang for the buck, as I see it, is in this form:

(x, y) => { FunctionBody }

If => means bound-this, I agree this has a lot of bang/buck. Not clear

the b/b beats the expression-body case, though. More below.

If we have implicit return (which I think we can bundle in with the "=> is different" meme), then we have a sufficiently succinct alternative:

(x, y) => { { p:x * y} }

I don't know about implicit return. TC39 wants to avoid it in anything that looks like a function (body). Does the => distinguish this case

enough? Consider a large body-block.

I think (x, y) => expr can be difficult for human readers (particularly non-experienced readers) to figure out anyway.

The sweet spot looking at PrototypeJS is a single expression body. There are some longer inject funargs but they are the minority AFAICS.

Surveying other libraries would be helpful.

# Kevin Smith (11 years ago)

I don't know about implicit return. TC39 wants to avoid it in anything that looks like a function (body). Does the => distinguish this case enough? Consider a large body-block.

I can see the problem. I would personally rate this as less potentially confusing than "() => expr", though.

The sweet spot looking at PrototypeJS is a single expression body. There are some longer inject funargs but they are the minority AFAICS.

That wouldn't surprise me, given the nature of Prototype. I'll wager it's not typical though.

Surveying other libraries would be helpful.

Yes - enough guessing - I'll do my best to do some counting with different libraries (both for DOM and for nodejs) and report back some numbers. Erg

  • busy work : )

Any one else want to help? We should be able to get a good idea by counting anonymous function expressions, and of those, which ones only contain a single statement, which is a return statement.

# Claus Reinke (11 years ago)

Surveying other libraries would be helpful.

Yes - enough guessing - I'll do my best to do some counting with different libraries (both for DOM and for nodejs) and report back some numbers. Erg

  • busy work : )

Surveying is useful in the sense of visually comparing code versions with/without different short function proposals.

I doubt that (simple) statistics are equally useful here, though: the current syntax is sufficiently awkward that programmers actively avoid using the very code patterns that short functions would help to make readable.

With the large range of avoidance patterns (from preferring named callbacks over control-flow libs all the way to preprocessors), proper statistics about the pain that could be avoided by improved function syntax would involve non-trivial amounts of inspection/interpretation and rewriting.

Claus

# Kevin Smith (11 years ago)

I doubt that (simple) statistics are equally useful here, though: the current syntax is sufficiently awkward that programmers actively avoid using the very code patterns that short functions would help to make readable.

Thanks Claus - I understand but I think you're overstating. In any case, I've created a tool to count function expressions:

www.khs4473.com/function-expr-count

Here's what I get for jQuery 1.7.1:

Total Function Expressions: 485 (100%) Initial Value: 130 (26.804123711340207%) Block: 355 (73.19587628865979%)

Node.js's http module:

Total Function Expressions 78 (100%) Initial Value 2 (2.564102564102564%) Block 76 (97.43589743589743%)

I'll run some others though it as time permits.

# Claus Reinke (11 years ago)

Like many others, I would like to have very lightweight function definition and call syntax, because those are the cornerstones of functional abstraction.

This transcends classical functions, and would ideally include the ability to define, in userland, many of the control-flow constructs that currently need to be built in to the language, like iterators or non-local returns.

The present discussion, and mulling over control-flow abstractions in JS, reminds me that Javascript is not friendly towards functional abstraction. Even with short functions, we would still run into trouble at every corner, such as:

  • JS code tends to be statement-oriented, not expression-oriented
  • blocks of statements get run when they are encountered, unless wrapped by eta-expansion (which then leads to lots of explicit unwrapping calls)
  • References cannot be passed as function parameters
  • APIs, including core language APIs, tend to be imperative in nature

In light of this, the ideal of lightweight expression-oriented functions might not be sufficient, even if we could agree on the language constructs.

For instance, I would like my abstractions to work both with expressions and with statement blocks, meaning implicit returns, and possibly non-local binding of explicit returns. I definitely do not want to worry about implicit 'this' or 'arguments' getting in the way. Which looks a lot like block lambdas, even though I don't like the non-local returns!

So, while I haven't changed my long-term views about what comprises a good programming language (*), I'm coming around to the view that block lambdas, with implicit return, might be a good stopgap measure for JS now, both to make progress, and to fit the ideal of control-flow abstractions to the realities of JS.

I'd like to encourage other fp fans in this discussion to consider whether block lambdas might serve their needs, and to list detailed objections with a view to improving block-lambdas instead of rejecting them with a generic "this isn't what I'm used to";-)

One thing that block-lambdas don't solve is callback nesting

The sweet spot that I'm aiming for is to make the callback syntax/ semantics better, e.g:

IO.readText(path).then((text, err) => { // Many statements in here, including: this.whatever(); });

I have been playing with 'let'-sugar for the common 'then' callback pattern, by replacing

{ let param <- rhs; body }

with rhs.then( {| param | body } )

Pronounce this as "let param from rhs in body" (it is inspired by computation expressions in F# and do-notation in Haskell).

Kevin's example would become

let (text,err) <- IO.readText(path)
// Many statements in here, including:
this.whatever();

making asynchronous API usage similar to synchronous usage.

I've found desugaring to short or other functions problematic (see list above) and don't know yet whether desugaring to block lambdas solves these issues, but I'd like to throw the idea into the room, for everyone to play with.

Claus

(*) I would still prefer JS to become more functional, where functional abstraction, expression-oriented programming, and user- defined control abstractions are as commonplace as in Haskell, say. But ES6 not including any progress here (no short functions, no block lambdas) would be the worst possible result.

# Marc Harter (11 years ago)

Honestly, I'm starting to believe that most nay-sayers would get over block-lambda looking weird at first and learn to really love the benefit it provides. Sure they might say "it looks really bizarre", but they will also say "remember when we had to assign var that = this; or use bind()? The dark ages!! I love block-lambda!"

Even for me, it was hard to look past the weirdness, but the more I read about "freaky-deaky" syntax, the more it begins to make sense.

Echo this. I didn't get block lambdas at first nor did I get the syntax as it was a little freakish but programming with them in mind seems to be big win for JS, not JUST shorter syntax, the TCP wins introduce literally new (arguably better) ways to solve existing problems. We'll get used to it and prefer it is my gut. The syntax could be forever argued, I'm used to it now. Way prefer it over shorter function syntax if I had to choose (in a sense it is but with TCP).

/extra 2 cents

Marc

# Marc Harter (11 years ago)

This may of been beaten to death, so sorry to bring it up again :)

Whenever I've thought of shorter function syntax, I think of a shorter word for function, nothing else. I honestly don't care what it is, rubyish like def or fn or func or your pick. I'm mixed about the goal of shortening function to something that is uber short. Does it really need that? No messing with return (although that could be shorter), and then with block-lambdas, just use that for implicit return, TCP stuff (like w/ array extras or even callbacks).

Of course I'm probably over simplifying as I'm not a language expert but just my thoughts.

Marc (@wavded)

# Herby Vojčík (11 years ago)

Hello,

to see the problem various syntaxes can bring, various crazy code samples should be written in each of them to see if and how hard can they be used, and how it is readable.

I created a little gallery of a few code samples written in a few proposals presented here, it's in docs.google.com/spreadsheet/ccc?key=0AtmBA7R8TygwdEdWenc1X210YzIxVWxfY0NsU0h1cVE.

Please feel free to edit/add/repair-what-I-did-wrong. So they can be compared.

Herby

P.S.: From the look of it so far, parenfree ones are hard to read if too dense, for me the best seems to be (:x) x*x (and not because I proposed it, it just is most readable for me).

# Claus Reinke (11 years ago)

This may of been beaten to death, so sorry to bring it up again :)

To the extent that design rationales are not recorded on the wiki, repetition is hard to avoid. There have been just too many variations in the mailing list archives to make catching up just from there realistic.

Whenever I've thought of shorter function syntax, I think of a shorter word for function, nothing else. I honestly don't care what it is, rubyish like def or fn or func or your pick. I'm mixed about the goal of shortening function to something that is uber short. Does it really need that? No messing with return (although that could be shorter), and then with block-lambdas, just use that for implicit return, TCP stuff (like w/ array extras or even callbacks).

Of course I'm probably over simplifying as I'm not a language expert but just my thoughts.

Different people may have different goals, but for some of us, short functions are not a goal, but merely a pre-requisite.

Possible goals enabled by that include cleaning up callback nesting without preprocessors or, more generally, defining your own control structures or, even more generally, defining your own domain-specific languages (DSLs), embedded in JS.

If you think about that latter goal (defining a language that is tailor-made for writing and reading the kind of solutions that your problem domain requires, without having to write a parser or compiler), then two aspects tend to stand out:

  • functions represent variable binding in the embedded DSL, so they are extremely common, and any syntactic noise imposed by the host language (JS here) distracts from the variable binding structure

  • inflexibilities in the base language (such as implied bindings, evaluation order, References as auxiliary constructs, ..) impose constraints on what is possible to do (or practical to do) in embedded languages, defeating the advantages of embedded DSLs

[If you are interested in these kinds of things, try to imagine defining a set of functions that embeds as JS-like language core (especially variable bindings, conditionals, loops). In theory, this should be possible using only functions, function application, and variables, and very nearly readable with just a few additional helper functions. In JS practice, once one has worked around JS limitations, the result tends to be unreadable, or resembles JQuery/RxJS. Which is why JS coders tend to give up on the idea of embedding DSLs and use preprocessors like CoffeeScript or streamline.js]

Now, if we just shorten function/return to fn/ret, we'd still shadow bindings for this/arguments and targets for return whenever we wrap some code in a function (which some posters here refer to as TCP violations). Not to mention that fn/ret might be used as variable/function names in existing code. And so we get from simple intentions to messy details..

Discussing short functions is about as interesting as discussing their lexical syntax. Most of us would like to move on, and be able to build on the results. Unfortunately, the discussions have shown that the details are tricky to get right in the context of JS syntax, semantics, and pragmatics (such as anticipated usage or future language evolution). "unfortunate", because everyone seems to want some progress, but everyone is wary of moving in the "wrong" direction.

The committee's concerns and insistence on fully worked out and validateable proposals stem from experience with just how easy it is to get things wrong. Fully worked out proposals can also be prototyped, to get practical experience.

Given that some JS engines have only recently caught up with ES5, it seems safe to say that if ES6 doesn't promise progress in embedded DSL support within the next few years, then pre-processors (or alternative languages) are going to win. ES7 is just too far away.

Just my view, Claus

PS. implicit return (return block completion value) and non-local return (return to lexically enclosing function) are not the same thing.

# Kevin Smith (11 years ago)

I updated the tool so that it doesn't count function expressions that reference "this" (as those aren't suitable for => functions).

www.khs4473.com/function-expr-count

For most of the libraries that I've ran through it, 60-75% of the functions it scans are of the "block" type. I haven't run it on any "application" code yet.

# Russell Leggett (11 years ago)

On Sat, Mar 10, 2012 at 3:10 PM, Herby Vojčík <herby at mailbox.sk> wrote:

Hello,

to see the problem various syntaxes can bring, various crazy code samples should be written in each of them to see if and how hard can they be used, and how it is readable.

I created a little gallery of a few code samples written in a few proposals presented here, it's in docs.google.com/** spreadsheet/ccc?key=0AtmBA7R8TygwdEdWenc1X210YzIxVWxfY0NsU0h1cVEdocs.google.com/spreadsheet/ccc?key=0AtmBA7R8TygwdEdWenc1X210YzIxVWxfY0NsU0h1cVE .

Please feel free to edit/add/repair-what-I-did-**wrong. So they can be compared.

Herby

P.S.: From the look of it so far, parenfree ones are hard to read if too dense, for me the best seems to be (:x) x*x (and not because I proposed it, it just is most readable for me).

Thanks for putting that together, however, I think it's a little misleading, because I think the majority use case here will be inside of a function call, therefore, it would be more realistic to show that in the examples for each.

# Herby Vojčík (11 years ago)

Russell Leggett wrote:

On Sat, Mar 10, 2012 at 3:10 PM, Herby Vojčík <herby at mailbox.sk <mailto:herby at mailbox.sk>> wrote:

Hello,

to see the problem various syntaxes can bring, various crazy code
samples should be written in each of them to see if and how hard can
they be used, and how it is readable.

I created a little gallery of a few code samples written in a few
proposals presented here, it's in
https://docs.google.com/__spreadsheet/ccc?key=__0AtmBA7R8TygwdEdWenc1X210YzIxV__WxfY0NsU0h1cVE
<https://docs.google.com/spreadsheet/ccc?key=0AtmBA7R8TygwdEdWenc1X210YzIxVWxfY0NsU0h1cVE>.

Please feel free to edit/add/repair-what-I-did-__wrong. So they can
be compared.

Herby

P.S.: From the look of it so far, parenfree ones are hard to read if
too dense, for me the best seems to be (:x) x*x (and not because I
proposed it, it just is most readable for me).

Thanks for putting that together, however, I think it's a little misleading, because I think the majority use case here will be inside of a function call, therefore, it would be more realistic to show that in the examples for each.

Add such examples. I made it editable for anyone exactly for that reason

  • to add code samples they see as relevant.
# Russell Leggett (11 years ago)

Add such examples. I made it editable for anyone exactly for that reason - to add code samples they see as relevant.

Will do