block lambda revival, now with semantics

Brendan Eich brendan at mozilla.com
Mon May 23 09:12:28 PDT 2011


On May 23, 2011, at 9:04 AM, Kam Kasravi wrote:

> In the block lambda strawman examples, the surprise 
> is the use of return (and other goto constructs like break). At least in similar discussions related 
> to closures in java, Neal Grafter

(Gafter)


> suggests that return and other control constructs not be allowed (http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html) due to TCP.

That's not quite a solution: you can indeed then factor out the guts of a closure immediately or downward-funarg-ly (sic) invoked -- but you can't factor code the other direction without losing expressiveness at the statement level.


> I assume this is also why var is being excluded in the strawman.

No, var is worse because it hoists to top of function body. Strictly different and harder compared to break/continue loop/switch targeting (which falls out for free with proper Completion type usage), and also compared to return (no hoisting).


> Given the impact to the grammar to exclude var,

Trivial. Can be done a la the "NoIn" productions but better if we do it with prose. Not a big deal, IMHO.


> would this constraint be compile time checked?

Yes, |var| would be forbidden in block-lambdas by early error.


> Would grammar  constraints like no var be semantically checked by implementors or folded into the grammar formally?

See above; not a big deal, up to specificiers.


> The latter would suggest a production rule other than StatementList*, where VariableStatement would be excluded...

That's too duplicative. The NoIn productions are too. We could have parameterized productions, or use prose. This is a sideshow, though.

I don't agree with banning control statements in blocks, in any event.

/be

> 
> On May 23, 2011, at 6:57 AM, Brendan Eich <brendan at mozilla.com> wrote:
> 
>> On May 22, 2011, at 10:15 PM, Kam Kasravi wrote:
>> 
>>> Is this valid?
>>> 
>>> function Person(a) {
>>>   this.age = a;
>>> }
>>> Person.prototype.myage = {|| this.age};
>> 
>> Block-lambda, per Tennent's Correspondence Principle as cited, uses the same this as if you moved the code inside the {|| ... } outside. This is so expr is equivalent to {|| expr }(). JS hackers do not see function () { ... } so should not expect this to change meaning.
>> 
>> (For this reason among others, block-lambdas have no [[Construct]] internal method. Same as for built-in functions in ES1-5.)
>> 
>> So this is not going to do what you want below:
>> 
>>> function info(myage) {
>>>   console.log('my age is '+myage());   
>>> }
>>> info(new Person(10).myage);
>>> info(new Person(12).myage);
>> 
>> Enclosing the block-lambda within the constructor works:
>> 
>> function Person(a) {
>>   this.age = a;
>>   this.myage = {|| this.age};
>> }
>> 
>> Note that in this case, unlike the case with a function expression instead of the block-lambda, the implementation can optimize aggressively: no other this can be bound dynamically in any subsequent call via new Person(10).myage() or myage() in info. This is a stronger guarantee than if Person used
>> 
>>   this.myage = function () { return this.age; }.bind(this);
>> 
>> in the absence of aggressive static analysis (without which, who knows what bind means at compile time)?
>> 
>> The full closure pattern works too, of course:
>> 
>> function Person(a) {
>>   return {get age() { return a; }, myage: {|| a}};
>> }
>> 
>> 
>> but you have to commit to accessors.
>> 
>> One last note: freezing and joining (see # usage in http://wiki.ecmascript.org/doku.php?id=strawman:arrow_function_syntax referencing http://wiki.ecmascript.org/doku.php?id=strawman:const_functions) do not enable much more optimization in this constructor pattern, however you do it. Whether closing over this or the parameter a, the joined block-lambda (or function) identity cannot join across the Person closure boundary.
>> 
>> (I did not make block-lambdas implicitly frozen and joined since some on TC39 and in the community object to that kind of change without opt-in syntax, and because it doesn't help much, given the change to make this a lexical "upvar".)
>> 
>> So block-lambdas are not going to solve the "bound method" cost problem in JS. For that, you need a class proposal that automatically binds methods to the receiver while at the same time disallowing any kind of dynamic inheritance. ES4 after ActionScript 3 had this; Java etc. do too of course.
>> 
>> Does JS really need it, vs. the prototypal function-valued properties as methods pattern? In any event, nothing block-lambdas or any other this-capturing form can fix by themselves.
>> 
>> /be
>> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110523/79445380/attachment.html>


More information about the es-discuss mailing list