block lambda revival, now with semantics

Kam Kasravi kamkasravi at yahoo.com
Mon May 23 09:04:34 PDT 2011


My expectation was that that this would not be bound in the same manner as 
this.myage = function() { return this.age }.bind(this); using block lambdas. Thanks 
for the clarifications below. 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 suggests that return and other control constructs not be allowed (http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html) due to TCP. I assume this is also why var is being excluded in the strawman. Given the impact to the grammar to exclude var, would this constraint be compile time checked? Would grammar  constraints like no var be semantically checked by implementors or folded into the grammar formally? The latter would suggest a production rule other than StatementList*, where VariableStatement would be excluded...

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/25eb61f6/attachment-0001.html>


More information about the es-discuss mailing list