Block lambda grammar: BlockArguments

Brendan Eich brendan at mozilla.org
Sat Jan 14 22:55:43 PST 2012


Nothing is going to match Smalltalk on this. Keyword parameters are not 
even on the map because object literals suck the oxygen out of the room. 
And then you'll want to get rid of the || for empty block parameters.

I say pause with a simpler design and digest, ruminate, prototype, 
user-test.

/be

> Axel Rauschmayer <mailto:axel at rauschma.de>
> January 14, 2012 9:35 PM
> Cool. I also have a feeling that every paren-free lambda after the 
> first one should have some kind of preceding keyword or name, but I 
> don’t know how one could make that happen. Brace-free object literals? 
> (only half joking)
>
> On Jan 15, 2012, at 3:42 , Brendan Eich wrote:
>
> -- 
> Dr. Axel Rauschmayer
> axel at rauschma.de <mailto:axel at rauschma.de>
>
> home: rauschma.de <http://rauschma.de>
> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
> blog: 2ality.com <http://2ality.com>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
> Brendan Eich <mailto:brendan at mozilla.org>
> January 14, 2012 6:42 PM
>
> Done: 
> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival&rev=1326440792&do=diff
>
> /be
> Brendan Eich <mailto:brendan at mozilla.org>
> January 14, 2012 6:31 PM
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 14, 2012 4:58 PM
>>
>> Three arguments in favor of yanking:
>>
>> - Thanks to lambdas, setTimeout already looks very nice – even as a 
>> parenthesized function call.
>
> Good -- and you make a good case here in terms of paren-counting:
>
>   setTimeout {|arg| ...} (1000) (x)
>
> is worse by paren-count cost than:
>
>   setTimeout({|arg| ...}, 1000, x)
>
> and with the style I expect where there's a space between the 
> setTimeout and the {, worse in total character count even including 
> the commas required in the second form.
>
> Let B be the number of block-lambdas and I be the number of 
> InitialValue expressions. In general we have B spaces (one before each 
> block-lambda) and 2I parens and I spaces (one before each 
> parenthesized InitialValue) or B+3I overhead with the paren-free 
> syntax in the expected style, vs. 2 parens around the whole arg list + 
> 2(B+I-1) ", " pairs between args, for 2(B+I). So the trade-off relation is
>
> paren-free <=> parenthesized
> B+3I <=> 2B+2I or
> I <=> B
>
> So if I < B, paren-free wins. If I >= B, parenthesized wins by I-B 
> chars. This ignores readability trade-off between ", " separation and 
> " (" with ")" on the right of each InitialValue.
>
>> - Putting callable values last is more likely, the grammar rule 
>> introduces an odd asymmetry, by not allowing leading a leading ( 
>> InitialValue ) . I’m not saying that that would be a desirable 
>> feature (currying is fine here), just that trailing non-lambdas seem 
>> much rarer than leading non-lambdas.
>
> Agreed.
>>
>> - The counter-argument (*) you mention above weighs heavily. Most 
>> people (certainly me) probably expect a function or method call when 
>> they see a parenthesized value.
>
> I think you are right.
>>
>> The following seems like an elegant and easy to understand rule to me:
>>     “The parameters of a function or method call are either 
>> parenthesized or paren-free. In the latter case, all parameters must 
>> be lambdas.”
>
> Given the overhead trade-off relation, I'm ditching the whole ( 
> InitialValue ) alternative. Thanks for poking at this!
>
> /be
>>
>> -- 
>> Dr. Axel Rauschmayer
>> axel at rauschma.de <mailto:axel at rauschma.de>
>>
>> home: rauschma.de <http://rauschma.de>
>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>> blog: 2ality.com <http://2ality.com>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> Brendan Eich <mailto:brendan at mozilla.org>
>> January 14, 2012 2:41 PM
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 14, 2012 1:32 PM
>>>>>       myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| 
>>>>> }, {|| })
>>>>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>>>
>>>> The closing parenthesis after arg2 really ought to mean end of 
>>>> formal parameter list. Anything else is too magical.
>>>
>>> I think that’s how Ruby does it.
>> Indeed:
>>
>> $ ruby
>> def foo(a,b)
>>   yield a+b
>> end
>> foo(1,2) {|x| puts x}
>> 3D
>>
>>
>>> I’m on the fence. It is indeed quite magical (and not the good kind 
>>> of magical).
>>
>> It's way too magical in my view to retrofit to JS. Again, making "if" 
>> and "while" and so on into block-lambda calling functions seems a 
>> wild goose chase. These forms take *statements*, not just 
>> block-statements (and not block-lambdas to call). They're built into 
>> the language via the C-like syntax.
>>
>> Imitating "if" etc. is fine. It's a motivation for the paren-free and 
>> *semicolon-free* CallWithBlockArguments statement form.
>>
>> And such imitation is doable via the strawman grammar, simply by 
>> returning a function (currying a bit). Singletons or pairs can be 
>> used if there's no need to retain the "if condition":
>>
>> function myIf(cond) {
>>   // The block-lambdas here are singletons (no upvars)...
>>   return cond ? {|t, e| t} : {|t, e| e};
>> }
>>
>> myIf (x > y) {|| "greater"} {|| "not greater"}
>>
>> Of course, one might want a dummy "else" ("myElse") in between the 
>> then and else blocks.
>>>
>>>> Why should foo(arg1)(arg2) and foo(arg1){||arg2} differ?
>>>
>>> BlockArguments :
>>>     BlockLambda
>>>     BlockArguments [no LineTerminator here] BlockLambda
>>>     BlockArguments [no LineTerminator here] ( InitialValue )
>>>
>>> I still don’t fully understand the ( InitialValue ) at the end – 
>>> it’s a single value in parens that can come after several blocks. If 
>>> Ruby-style magic isn’t an option then I would expect (and prefer) 
>>> that there were only two calling “modes”:
>>>
>>> - Traditional: myfunc(arg1, arg2, ...}
>>> - Paren-free – lambdas only: myfunc {|| body1} {|| body2} ...
>>
>> The objection (if you read the whole thread containing the message I 
>> cited, I think you'll find it) was that requiring *only* 
>> block-lambdas for the paren-free call form means expression 
>> arguments, even ones as simple as numeric literals, must be bracketed 
>> by {|| and }. Why not allow ( and ) instead?
>>
>> One counter-argument is that this looks the argument list of a call 
>> expression whose callee is everything to the left. But paren-free 
>> call syntax supports newline termination, so I added the ... ( 
>> Initial Value ) production to satisfy setTimeout-like use cases 
>> without requiring {|| and | as brackets.
>>
>> If this alternate production bites back in some way, or is simply 
>> under-used or too surprising, I'll yank it.
>>
>> Indeed I could simplify CallWithBlockArguments to take only one 
>> BlockLambda argument but that seems unnecessarily restrictive. More 
>> comments welcome.
>>>
>>>>> Following a block with a non-block doesn’t seem like a good idea.
>>>> This was an explicit goal, in order to support use-cases including 
>>>> setTimeout and promises APIs.
>>>
>>> With the above I meant: In paren-free mode, following a block with a 
>>> non-block doesn’t seem like a good idea. With a normal paren call, I 
>>> don’t see any problems.
>>
>> The consequences might include
>>
>>   setTimeout {|arg| ...} {|| 1000} {|| x}
>>
>> That is a bit harsh since 1000 (one second) is a literal and /* 
>> compute arg here */ is typically simply a pass-by-value reference to 
>> a variable in the scope of this setTimeout call, e.g. x in this example.
>>
>> The strawman therefore supports
>>
>>   setTimeout {|arg| ...} (1000) (x)
>>
>> for example.
>>
>> Could we go further and support certain primary expressions, namely 
>> all of the PrimaryExpression right-hand sides except for object literal?
>>
>> NonObjectLiteralPrimaryExpression :
>>     this
>> Identifier
>> Literal
>> ArrayLiteral
>>     ( Expression )
>>
>> ?
>>
>> Not a problem grammatically except for the added complexity, and the 
>> specific usability issues arising from that slight complexity:
>>
>> * Leaving out object literal means one must parenthesize an object 
>> literal but not an array literal.
>> * Allowing K but not K+1, requiring instead (K+1), may blow back with 
>> users.
>>
>> /be
>>>
>>> -- 
>>> Dr. Axel Rauschmayer
>>> axel at rauschma.de <mailto:axel at rauschma.de>
>>>
>>> home: rauschma.de <http://rauschma.de>
>>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>>> blog: 2ality.com <http://2ality.com>
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 14, 2012 9:41 AM
>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>> January 13, 2012 9:09 PM
>>>>
>>>> If I read the grammar correctly, then you can do things such as 
>>>> (read "~~~>" as "desugars to"):
>>>>
>>>>      myfunc {|| } {|| } (arg3) (arg4)  ~~~>  myfunc({|| }, {|| }, 
>>>> arg3, arg4)
>>>>
>>>> The above is a function call with 4 arguments. My wish would be 
>>>> different: I would want to put lambdas after a function or method 
>>>> call and treat those lambdas as additional arguments:
>>>>
>>>>      myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| 
>>>> }, {|| })
>>>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>>
>>> The closing parenthesis after arg2 really ought to mean end of 
>>> formal parameter list. Anything else is too magical.
>>>>
>>>> Rationale: I would always make lambdas trailing arguments, similar to
>>>>      if (cond) {} {}
>>>> And I would rather achieve this effect without currying.
>>>
>>> Why should foo(arg1)(arg2) and foo(arg1){||arg2} differ?
>>>
>>> Your use-case is satisfied by returning a function (memoized, 
>>> singleton even), but the symmetry between (arg1, ... argN) and 
>>> space-separated BlockArguments should not be broken.
>>>
>>>> Following a block with a non-block doesn’t seem like a good idea.
>>>
>>> This was an explicit goal, in order to support use-cases including 
>>> setTimeout and promises APIs.
>>>>
>>>> Has the other approach been considered?
>>>
>>> Yes, see
>>>
>>> https://mail.mozilla.org/pipermail/es-discuss/2011-May/014675.html
>>>
>>> /be
>>>>
>>>> -- 
>>>> Dr. Axel Rauschmayer
>>>> axel at rauschma.de <mailto:axel at rauschma.de>
>>>>
>>>> home: rauschma.de <http://rauschma.de>
>>>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>>>> blog: 2ality.com <http://2ality.com>
>>>>
>>>> Brendan Eich <mailto:brendan at mozilla.org>
>>>> January 13, 2012 12:58 PM
>>>> Fixed: 
>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival&do=diff
>>>>
>>>> The LeftHandSideExpression productions and their kids 
>>>> (NewExpression and CallExpression) are funky and I keep 
>>>> misremembering how NewExpression is what bottoms out via 
>>>> MemberExpression -> PrimaryExpression at Identifier.
>>>>
>>>> /be
>>>>
>>>> Brendan Eich <mailto:brendan at mozilla.org>
>>>> January 12, 2012 11:43 PM
>>>>> Brendan Eich <mailto:brendan at mozilla.org>
>>>>> January 12, 2012 11:39 PM
>>>>>   v.map {|e| e*e}
>>>>
>>>> Er, not even that -- Arguments required in a CallExpression, so 
>>>> v().map or v.map() but not just v.map. Fixes coming tomorrow.
>>>>
>>>> /be
>>>>>
>>>>> or
>>>>>
>>>>>   get_map() {|e| e*e}
>>>>>
>>>>> or similar. I will fix.
>>>>>
>>>>> /be
>>>>> _______________________________________________
>>>>> es-discuss mailing list
>>>>> es-discuss at mozilla.org
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>>> January 12, 2012 11:16 PM
>>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>>
>>>>> I’m trying to understand the syntax:
>>>>> BlockArguments :
>>>>> BlockLambda
>>>>> BlockArguments [no LineTerminator here] BlockLambda
>>>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>>>
>>>>> - Wouldn’t this allow the following? BlockLambda [no 
>>>>> LineTerminator here] BlockLambda
>>>>> - InitialValue means that paren-free can be combined with 
>>>>> arguments that aren’t blocks, right?
>>>>>
>>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>>>
>>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>>
>>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>>
>>>>>
>>>>>
>>>> Brendan Eich <mailto:brendan at mozilla.org>
>>>> January 12, 2012 11:39 PM
>>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>>> January 12, 2012 11:16 PM
>>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>>
>>>>> I’m trying to understand the syntax:
>>>>> BlockArguments :
>>>>>      BlockLambda
>>>>>      BlockArguments [no LineTerminator here] BlockLambda
>>>>>      BlockArguments [no LineTerminator here] ( InitialValue )
>>>>>
>>>>> - Wouldn’t this allow the following? BlockLambda [no 
>>>>> LineTerminator here] BlockLambda
>>>>
>>>> Yes.
>>>>
>>>>> - InitialValue means that paren-free can be combined with 
>>>>> arguments that aren’t blocks, right?
>>>>
>>>> Yes.
>>>>>
>>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>>
>>>> No, the myLoopFunc(initValue1) is a CallExpression -- see
>>>> CallWithBlockArguments :
>>>>      CallExpression [no LineTerminator here] BlockArguments
>>>>   
>>>> The *return value* of that ordinary CallExpression is the callee of the paren-free call.
>>>>>
>>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>>
>>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>>
>>>>
>>>> That parses, as described above. The two-argument CallExpression 
>>>> must return a function that takes the block arguments.
>>>>
>>>> I see a problem in the grammar in the strawman, now that you 
>>>> mention it: no way to produce a simple identifier callee from 
>>>> CallExpression, so no
>>>>
>>>>   map {|e| e*e}
>>>>
>>>> only
>>>>
>>>>   v.map {|e| e*e}
>>>>
>>>> or
>>>>
>>>>   get_map() {|e| e*e}
>>>>
>>>> or similar. I will fix.
>>>>
>>>> /be
>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>> January 12, 2012 11:16 PM
>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>
>>>> I’m trying to understand the syntax:
>>>> BlockArguments :
>>>> BlockLambda
>>>> BlockArguments [no LineTerminator here] BlockLambda
>>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>>
>>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>>> here] BlockLambda
>>>> - InitialValue means that paren-free can be combined with arguments 
>>>> that aren’t blocks, right?
>>>>
>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>>
>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>
>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>
>>>>
>>>>
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 13, 2012 9:09 PM
>>>
>>> If I read the grammar correctly, then you can do things such as 
>>> (read "~~~>" as "desugars to"):
>>>
>>>      myfunc {|| } {|| } (arg3) (arg4)  ~~~>  myfunc({|| }, {|| }, 
>>> arg3, arg4)
>>>
>>> The above is a function call with 4 arguments. My wish would be 
>>> different: I would want to put lambdas after a function or method 
>>> call and treat those lambdas as additional arguments:
>>>
>>>      myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| }, 
>>> {|| })
>>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>>
>>> Rationale: I would always make lambdas trailing arguments, similar to
>>>      if (cond) {} {}
>>> And I would rather achieve this effect without currying. Following a 
>>> block with a non-block doesn’t seem like a good idea.
>>>
>>> Has the other approach been considered?
>>>
>>> -- 
>>> Dr. Axel Rauschmayer
>>> axel at rauschma.de <mailto:axel at rauschma.de>
>>>
>>> home: rauschma.de <http://rauschma.de>
>>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>>> blog: 2ality.com <http://2ality.com>
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 13, 2012 12:58 PM
>>> Fixed: 
>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival&do=diff
>>>
>>> The LeftHandSideExpression productions and their kids (NewExpression 
>>> and CallExpression) are funky and I keep misremembering how 
>>> NewExpression is what bottoms out via MemberExpression -> 
>>> PrimaryExpression at Identifier.
>>>
>>> /be
>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 12, 2012 11:43 PM
>>>> Brendan Eich <mailto:brendan at mozilla.org>
>>>> January 12, 2012 11:39 PM
>>>>   v.map {|e| e*e}
>>>
>>> Er, not even that -- Arguments required in a CallExpression, so 
>>> v().map or v.map() but not just v.map. Fixes coming tomorrow.
>>>
>>> /be
>>>>
>>>> or
>>>>
>>>>   get_map() {|e| e*e}
>>>>
>>>> or similar. I will fix.
>>>>
>>>> /be
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>> January 12, 2012 11:16 PM
>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>
>>>> I’m trying to understand the syntax:
>>>> BlockArguments :
>>>> BlockLambda
>>>> BlockArguments [no LineTerminator here] BlockLambda
>>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>>
>>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>>> here] BlockLambda
>>>> - InitialValue means that paren-free can be combined with arguments 
>>>> that aren’t blocks, right?
>>>>
>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>>
>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>
>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>
>>>>
>>>>
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 14, 2012 1:32 PM
>>>>       myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| 
>>>> }, {|| })
>>>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>>
>>> The closing parenthesis after arg2 really ought to mean end of 
>>> formal parameter list. Anything else is too magical.
>>
>> I think that’s how Ruby does it. I’m on the fence. It is indeed quite 
>> magical (and not the good kind of magical).
>>
>>> Why should foo(arg1)(arg2) and foo(arg1){||arg2} differ?
>>
>> BlockArguments :
>>     BlockLambda
>>     BlockArguments [no LineTerminator here] BlockLambda
>>     BlockArguments [no LineTerminator here] ( InitialValue )
>>
>> I still don’t fully understand the ( InitialValue ) at the end – it’s 
>> a single value in parens that can come after several blocks. If 
>> Ruby-style magic isn’t an option then I would expect (and prefer) 
>> that there were only two calling “modes”:
>>
>> - Traditional: myfunc(arg1, arg2, ...}
>> - Paren-free – lambdas only: myfunc {|| body1} {|| body2} ...
>>
>>>> Following a block with a non-block doesn’t seem like a good idea.
>>> This was an explicit goal, in order to support use-cases including 
>>> setTimeout and promises APIs.
>>
>> With the above I meant: In paren-free mode, following a block with a 
>> non-block doesn’t seem like a good idea. With a normal paren call, I 
>> don’t see any problems.
>>
>> -- 
>> Dr. Axel Rauschmayer
>> axel at rauschma.de <mailto:axel at rauschma.de>
>>
>> home: rauschma.de <http://rauschma.de>
>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>> blog: 2ality.com <http://2ality.com>
>>
>> Brendan Eich <mailto:brendan at mozilla.org>
>> January 14, 2012 9:41 AM
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 13, 2012 9:09 PM
>>>
>>> If I read the grammar correctly, then you can do things such as 
>>> (read "~~~>" as "desugars to"):
>>>
>>>      myfunc {|| } {|| } (arg3) (arg4)  ~~~>  myfunc({|| }, {|| }, 
>>> arg3, arg4)
>>>
>>> The above is a function call with 4 arguments. My wish would be 
>>> different: I would want to put lambdas after a function or method 
>>> call and treat those lambdas as additional arguments:
>>>
>>>      myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| }, 
>>> {|| })
>>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>
>> The closing parenthesis after arg2 really ought to mean end of formal 
>> parameter list. Anything else is too magical.
>>>
>>> Rationale: I would always make lambdas trailing arguments, similar to
>>>      if (cond) {} {}
>>> And I would rather achieve this effect without currying.
>>
>> Why should foo(arg1)(arg2) and foo(arg1){||arg2} differ?
>>
>> Your use-case is satisfied by returning a function (memoized, 
>> singleton even), but the symmetry between (arg1, ... argN) and 
>> space-separated BlockArguments should not be broken.
>>
>>> Following a block with a non-block doesn’t seem like a good idea.
>>
>> This was an explicit goal, in order to support use-cases including 
>> setTimeout and promises APIs.
>>>
>>> Has the other approach been considered?
>>
>> Yes, see
>>
>> https://mail.mozilla.org/pipermail/es-discuss/2011-May/014675.html
>>
>> /be
>>>
>>> -- 
>>> Dr. Axel Rauschmayer
>>> axel at rauschma.de <mailto:axel at rauschma.de>
>>>
>>> home: rauschma.de <http://rauschma.de>
>>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>>> blog: 2ality.com <http://2ality.com>
>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 13, 2012 12:58 PM
>>> Fixed: 
>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival&do=diff
>>>
>>> The LeftHandSideExpression productions and their kids (NewExpression 
>>> and CallExpression) are funky and I keep misremembering how 
>>> NewExpression is what bottoms out via MemberExpression -> 
>>> PrimaryExpression at Identifier.
>>>
>>> /be
>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 12, 2012 11:43 PM
>>>> Brendan Eich <mailto:brendan at mozilla.org>
>>>> January 12, 2012 11:39 PM
>>>>   v.map {|e| e*e}
>>>
>>> Er, not even that -- Arguments required in a CallExpression, so 
>>> v().map or v.map() but not just v.map. Fixes coming tomorrow.
>>>
>>> /be
>>>>
>>>> or
>>>>
>>>>   get_map() {|e| e*e}
>>>>
>>>> or similar. I will fix.
>>>>
>>>> /be
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>> January 12, 2012 11:16 PM
>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>
>>>> I’m trying to understand the syntax:
>>>> BlockArguments :
>>>> BlockLambda
>>>> BlockArguments [no LineTerminator here] BlockLambda
>>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>>
>>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>>> here] BlockLambda
>>>> - InitialValue means that paren-free can be combined with arguments 
>>>> that aren’t blocks, right?
>>>>
>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>>
>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>
>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>
>>>>
>>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 12, 2012 11:39 PM
>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>> January 12, 2012 11:16 PM
>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>
>>>> I’m trying to understand the syntax:
>>>> BlockArguments :
>>>>      BlockLambda
>>>>      BlockArguments [no LineTerminator here] BlockLambda
>>>>      BlockArguments [no LineTerminator here] ( InitialValue )
>>>>
>>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>>> here] BlockLambda
>>>
>>> Yes.
>>>
>>>> - InitialValue means that paren-free can be combined with arguments 
>>>> that aren’t blocks, right?
>>>
>>> Yes.
>>>>
>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>
>>> No, the myLoopFunc(initValue1) is a CallExpression -- see
>>> CallWithBlockArguments :
>>>      CallExpression [no LineTerminator here] BlockArguments
>>>   
>>> The *return value* of that ordinary CallExpression is the callee of the paren-free call.
>>>>
>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>
>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>
>>>
>>> That parses, as described above. The two-argument CallExpression 
>>> must return a function that takes the block arguments.
>>>
>>> I see a problem in the grammar in the strawman, now that you mention 
>>> it: no way to produce a simple identifier callee from 
>>> CallExpression, so no
>>>
>>>   map {|e| e*e}
>>>
>>> only
>>>
>>>   v.map {|e| e*e}
>>>
>>> or
>>>
>>>   get_map() {|e| e*e}
>>>
>>> or similar. I will fix.
>>>
>>> /be
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 12, 2012 11:16 PM
>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>
>>> I’m trying to understand the syntax:
>>> BlockArguments :
>>> BlockLambda
>>> BlockArguments [no LineTerminator here] BlockLambda
>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>
>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>> here] BlockLambda
>>> - InitialValue means that paren-free can be combined with arguments 
>>> that aren’t blocks, right?
>>>
>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>
>>> I think I would prefer the following (IIRC, more like Ruby):
>>>
>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>
>>>
>>>
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 13, 2012 9:09 PM
>>
>> If I read the grammar correctly, then you can do things such as (read 
>> "~~~>" as "desugars to"):
>>
>>      myfunc {|| } {|| } (arg3) (arg4)  ~~~>  myfunc({|| }, {|| }, 
>> arg3, arg4)
>>
>> The above is a function call with 4 arguments. My wish would be 
>> different: I would want to put lambdas after a function or method 
>> call and treat those lambdas as additional arguments:
>>
>>      myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| }, 
>> {|| })
>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>
>> Rationale: I would always make lambdas trailing arguments, similar to
>>      if (cond) {} {}
>> And I would rather achieve this effect without currying. Following a 
>> block with a non-block doesn’t seem like a good idea.
>>
>> Has the other approach been considered?
>>
>> -- 
>> Dr. Axel Rauschmayer
>> axel at rauschma.de <mailto:axel at rauschma.de>
>>
>> home: rauschma.de <http://rauschma.de>
>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>> blog: 2ality.com <http://2ality.com>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
> Axel Rauschmayer <mailto:axel at rauschma.de>
> January 14, 2012 4:58 PM
>
> Three arguments in favor of yanking:
>
> - Thanks to lambdas, setTimeout already looks very nice – even as a 
> parenthesized function call.
>
> - Putting callable values last is more likely, the grammar rule 
> introduces an odd asymmetry, by not allowing leading a leading ( 
> InitialValue ) . I’m not saying that that would be a desirable feature 
> (currying is fine here), just that trailing non-lambdas seem much 
> rarer than leading non-lambdas.
>
> - The counter-argument (*) you mention above weighs heavily. Most 
> people (certainly me) probably expect a function or method call when 
> they see a parenthesized value.
>
> The following seems like an elegant and easy to understand rule to me:
>     “The parameters of a function or method call are either 
> parenthesized or paren-free. In the latter case, all parameters must 
> be lambdas.”
>
> -- 
> Dr. Axel Rauschmayer
> axel at rauschma.de <mailto:axel at rauschma.de>
>
> home: rauschma.de <http://rauschma.de>
> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
> blog: 2ality.com <http://2ality.com>
>
> Brendan Eich <mailto:brendan at mozilla.org>
> January 14, 2012 2:41 PM
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 14, 2012 1:32 PM
>>>>       myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| 
>>>> }, {|| })
>>>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>>
>>> The closing parenthesis after arg2 really ought to mean end of 
>>> formal parameter list. Anything else is too magical.
>>
>> I think that’s how Ruby does it.
> Indeed:
>
> $ ruby
> def foo(a,b)
>   yield a+b
> end
> foo(1,2) {|x| puts x}
> 3D
>
>
>> I’m on the fence. It is indeed quite magical (and not the good kind 
>> of magical).
>
> It's way too magical in my view to retrofit to JS. Again, making "if" 
> and "while" and so on into block-lambda calling functions seems a wild 
> goose chase. These forms take *statements*, not just block-statements 
> (and not block-lambdas to call). They're built into the language via 
> the C-like syntax.
>
> Imitating "if" etc. is fine. It's a motivation for the paren-free and 
> *semicolon-free* CallWithBlockArguments statement form.
>
> And such imitation is doable via the strawman grammar, simply by 
> returning a function (currying a bit). Singletons or pairs can be used 
> if there's no need to retain the "if condition":
>
> function myIf(cond) {
>   // The block-lambdas here are singletons (no upvars)...
>   return cond ? {|t, e| t} : {|t, e| e};
> }
>
> myIf (x > y) {|| "greater"} {|| "not greater"}
>
> Of course, one might want a dummy "else" ("myElse") in between the 
> then and else blocks.
>>
>>> Why should foo(arg1)(arg2) and foo(arg1){||arg2} differ?
>>
>> BlockArguments :
>>     BlockLambda
>>     BlockArguments [no LineTerminator here] BlockLambda
>>     BlockArguments [no LineTerminator here] ( InitialValue )
>>
>> I still don’t fully understand the ( InitialValue ) at the end – it’s 
>> a single value in parens that can come after several blocks. If 
>> Ruby-style magic isn’t an option then I would expect (and prefer) 
>> that there were only two calling “modes”:
>>
>> - Traditional: myfunc(arg1, arg2, ...}
>> - Paren-free – lambdas only: myfunc {|| body1} {|| body2} ...
>
> The objection (if you read the whole thread containing the message I 
> cited, I think you'll find it) was that requiring *only* block-lambdas 
> for the paren-free call form means expression arguments, even ones as 
> simple as numeric literals, must be bracketed by {|| and }. Why not 
> allow ( and ) instead?
>
> One counter-argument is that this looks the argument list of a call 
> expression whose callee is everything to the left. But paren-free call 
> syntax supports newline termination, so I added the ... ( Initial 
> Value ) production to satisfy setTimeout-like use cases without 
> requiring {|| and | as brackets.
>
> If this alternate production bites back in some way, or is simply 
> under-used or too surprising, I'll yank it.
>
> Indeed I could simplify CallWithBlockArguments to take only one 
> BlockLambda argument but that seems unnecessarily restrictive. More 
> comments welcome.
>>
>>>> Following a block with a non-block doesn’t seem like a good idea.
>>> This was an explicit goal, in order to support use-cases including 
>>> setTimeout and promises APIs.
>>
>> With the above I meant: In paren-free mode, following a block with a 
>> non-block doesn’t seem like a good idea. With a normal paren call, I 
>> don’t see any problems.
>
> The consequences might include
>
>   setTimeout {|arg| ...} {|| 1000} {|| x}
>
> That is a bit harsh since 1000 (one second) is a literal and /* 
> compute arg here */ is typically simply a pass-by-value reference to a 
> variable in the scope of this setTimeout call, e.g. x in this example.
>
> The strawman therefore supports
>
>   setTimeout {|arg| ...} (1000) (x)
>
> for example.
>
> Could we go further and support certain primary expressions, namely 
> all of the PrimaryExpression right-hand sides except for object literal?
>
> NonObjectLiteralPrimaryExpression :
>     this
> Identifier
> Literal
> ArrayLiteral
>     ( Expression )
>
> ?
>
> Not a problem grammatically except for the added complexity, and the 
> specific usability issues arising from that slight complexity:
>
> * Leaving out object literal means one must parenthesize an object 
> literal but not an array literal.
> * Allowing K but not K+1, requiring instead (K+1), may blow back with 
> users.
>
> /be
>>
>> -- 
>> Dr. Axel Rauschmayer
>> axel at rauschma.de <mailto:axel at rauschma.de>
>>
>> home: rauschma.de <http://rauschma.de>
>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>> blog: 2ality.com <http://2ality.com>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> Brendan Eich <mailto:brendan at mozilla.org>
>> January 14, 2012 9:41 AM
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 13, 2012 9:09 PM
>>>
>>> If I read the grammar correctly, then you can do things such as 
>>> (read "~~~>" as "desugars to"):
>>>
>>>      myfunc {|| } {|| } (arg3) (arg4)  ~~~>  myfunc({|| }, {|| }, 
>>> arg3, arg4)
>>>
>>> The above is a function call with 4 arguments. My wish would be 
>>> different: I would want to put lambdas after a function or method 
>>> call and treat those lambdas as additional arguments:
>>>
>>>      myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| }, 
>>> {|| })
>>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>
>> The closing parenthesis after arg2 really ought to mean end of formal 
>> parameter list. Anything else is too magical.
>>>
>>> Rationale: I would always make lambdas trailing arguments, similar to
>>>      if (cond) {} {}
>>> And I would rather achieve this effect without currying.
>>
>> Why should foo(arg1)(arg2) and foo(arg1){||arg2} differ?
>>
>> Your use-case is satisfied by returning a function (memoized, 
>> singleton even), but the symmetry between (arg1, ... argN) and 
>> space-separated BlockArguments should not be broken.
>>
>>> Following a block with a non-block doesn’t seem like a good idea.
>>
>> This was an explicit goal, in order to support use-cases including 
>> setTimeout and promises APIs.
>>>
>>> Has the other approach been considered?
>>
>> Yes, see
>>
>> https://mail.mozilla.org/pipermail/es-discuss/2011-May/014675.html
>>
>> /be
>>>
>>> -- 
>>> Dr. Axel Rauschmayer
>>> axel at rauschma.de <mailto:axel at rauschma.de>
>>>
>>> home: rauschma.de <http://rauschma.de>
>>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>>> blog: 2ality.com <http://2ality.com>
>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 13, 2012 12:58 PM
>>> Fixed: 
>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival&do=diff
>>>
>>> The LeftHandSideExpression productions and their kids (NewExpression 
>>> and CallExpression) are funky and I keep misremembering how 
>>> NewExpression is what bottoms out via MemberExpression -> 
>>> PrimaryExpression at Identifier.
>>>
>>> /be
>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 12, 2012 11:43 PM
>>>> Brendan Eich <mailto:brendan at mozilla.org>
>>>> January 12, 2012 11:39 PM
>>>>   v.map {|e| e*e}
>>>
>>> Er, not even that -- Arguments required in a CallExpression, so 
>>> v().map or v.map() but not just v.map. Fixes coming tomorrow.
>>>
>>> /be
>>>>
>>>> or
>>>>
>>>>   get_map() {|e| e*e}
>>>>
>>>> or similar. I will fix.
>>>>
>>>> /be
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>> January 12, 2012 11:16 PM
>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>
>>>> I’m trying to understand the syntax:
>>>> BlockArguments :
>>>> BlockLambda
>>>> BlockArguments [no LineTerminator here] BlockLambda
>>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>>
>>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>>> here] BlockLambda
>>>> - InitialValue means that paren-free can be combined with arguments 
>>>> that aren’t blocks, right?
>>>>
>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>>
>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>
>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>
>>>>
>>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 12, 2012 11:39 PM
>>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>>> January 12, 2012 11:16 PM
>>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>>
>>>> I’m trying to understand the syntax:
>>>> BlockArguments :
>>>>      BlockLambda
>>>>      BlockArguments [no LineTerminator here] BlockLambda
>>>>      BlockArguments [no LineTerminator here] ( InitialValue )
>>>>
>>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>>> here] BlockLambda
>>>
>>> Yes.
>>>
>>>> - InitialValue means that paren-free can be combined with arguments 
>>>> that aren’t blocks, right?
>>>
>>> Yes.
>>>>
>>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>
>>> No, the myLoopFunc(initValue1) is a CallExpression -- see
>>> CallWithBlockArguments :
>>>      CallExpression [no LineTerminator here] BlockArguments
>>>   
>>> The *return value* of that ordinary CallExpression is the callee of the paren-free call.
>>>>
>>>> I think I would prefer the following (IIRC, more like Ruby):
>>>>
>>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>>
>>>
>>> That parses, as described above. The two-argument CallExpression 
>>> must return a function that takes the block arguments.
>>>
>>> I see a problem in the grammar in the strawman, now that you mention 
>>> it: no way to produce a simple identifier callee from 
>>> CallExpression, so no
>>>
>>>   map {|e| e*e}
>>>
>>> only
>>>
>>>   v.map {|e| e*e}
>>>
>>> or
>>>
>>>   get_map() {|e| e*e}
>>>
>>> or similar. I will fix.
>>>
>>> /be
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 12, 2012 11:16 PM
>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>
>>> I’m trying to understand the syntax:
>>> BlockArguments :
>>> BlockLambda
>>> BlockArguments [no LineTerminator here] BlockLambda
>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>
>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>> here] BlockLambda
>>> - InitialValue means that paren-free can be combined with arguments 
>>> that aren’t blocks, right?
>>>
>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>
>>> I think I would prefer the following (IIRC, more like Ruby):
>>>
>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>
>>>
>>>
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 13, 2012 9:09 PM
>>
>> If I read the grammar correctly, then you can do things such as (read 
>> "~~~>" as "desugars to"):
>>
>>      myfunc {|| } {|| } (arg3) (arg4)  ~~~>  myfunc({|| }, {|| }, 
>> arg3, arg4)
>>
>> The above is a function call with 4 arguments. My wish would be 
>> different: I would want to put lambdas after a function or method 
>> call and treat those lambdas as additional arguments:
>>
>>      myfunc(arg1, arg2) {|| } {|| }  ~~~>  myfunc(arg1, arg2, {|| }, 
>> {|| })
>>      myfunc {|| } {|| }  ~~~>  myfunc({|| }, {|| })
>>
>> Rationale: I would always make lambdas trailing arguments, similar to
>>      if (cond) {} {}
>> And I would rather achieve this effect without currying. Following a 
>> block with a non-block doesn’t seem like a good idea.
>>
>> Has the other approach been considered?
>>
>> -- 
>> Dr. Axel Rauschmayer
>> axel at rauschma.de <mailto:axel at rauschma.de>
>>
>> home: rauschma.de <http://rauschma.de>
>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>> blog: 2ality.com <http://2ality.com>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> Brendan Eich <mailto:brendan at mozilla.org>
>> January 13, 2012 12:58 PM
>> Fixed: 
>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival&do=diff
>>
>> The LeftHandSideExpression productions and their kids (NewExpression 
>> and CallExpression) are funky and I keep misremembering how 
>> NewExpression is what bottoms out via MemberExpression -> 
>> PrimaryExpression at Identifier.
>>
>> /be
>>
>> Brendan Eich <mailto:brendan at mozilla.org>
>> January 12, 2012 11:43 PM
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 12, 2012 11:39 PM
>>>   v.map {|e| e*e}
>>
>> Er, not even that -- Arguments required in a CallExpression, so 
>> v().map or v.map() but not just v.map. Fixes coming tomorrow.
>>
>> /be
>>>
>>> or
>>>
>>>   get_map() {|e| e*e}
>>>
>>> or similar. I will fix.
>>>
>>> /be
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 12, 2012 11:16 PM
>>> http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda_revival
>>>
>>> I’m trying to understand the syntax:
>>> BlockArguments :
>>> BlockLambda
>>> BlockArguments [no LineTerminator here] BlockLambda
>>> BlockArguments [no LineTerminator here] ( InitialValue )
>>>
>>> - Wouldn’t this allow the following? BlockLambda [no LineTerminator 
>>> here] BlockLambda
>>> - InitialValue means that paren-free can be combined with arguments 
>>> that aren’t blocks, right?
>>>
>>> myLoopFunc(initValue1)(initValue2) { | arg1, arg2 | ... }
>>>
>>> I think I would prefer the following (IIRC, more like Ruby):
>>>
>>> myLoopFunc(initValue1, initValue2) { | arg1, arg2 | ... }
>>>
>>>
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/bf99f269/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1222 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/bf99f269/attachment-0002.jpg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1290 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/bf99f269/attachment-0003.jpg>


More information about the es-discuss mailing list