Inner functions and outer 'this' (Re: That hash symbol)

Brendan Eich brendan at mozilla.com
Tue Mar 29 14:38:31 PDT 2011


On Mar 29, 2011, at 11:19 AM, Allen Wirfs-Brock wrote:

> The original discussion in January added explicit naming and lexical scoping of the implicit "this" parameter to # function declarations.  In January we didn't settle on a syntax but in March we seem to be converging on something like:
>   #foo(self | arg1, arg2) {...}

That's what I wrote on the white board last week at the meeting, yes. It's not set in stone but I'm warmest to it (which is why I threw it up! ;-). Alternatives include

  #foo(self . arg1, arg2) {...}

where . is a play on how |this| comes from the base object to the left of dot in a property reference expression forming the callee in the call expression, but dot is higher precedence as an operator than comma, and visually light, so this seems less good.

The only "lower precedence" punctuator/operator that comes to mind is semicolon:

  #fooo(self; arg1, arg2) {...}

but that seems like asking for trouble given ; being otherwise only a statement terminator, modulo ASI. Consider someone using src.split(';') on existing well-formed (perhaps from Function.prototype.toString()) source in a string named src.


> in the body of the above function, "self" would be bound to the implicit this parameter of the function and "this" would resolve to any lexically enclosing declarations of "this" or would be undefined if there were none.

That was the key point of the discussion: lexical |this|, treating it as an identifier, to capture |this| from a closure. Exactly what people do today via

  function f() {
    var self = this;
    return function (){... self... };
  }


> It isn't clear whether or not arbitrary declarations of "this" would be allowed within such functions or whether "this" could only be bound in a "this |" context.

We did not discuss allowing |this| to be bound otherwise, *except*

  #foo(this = this| arg1, arg2) {...}

which did come up. One could use |self| on the left of = there, but the idea was to support

http://wiki.ecmascript.org/doku.php?id=harmony:parameter_default_values

even for the optional receiver parameter. The default parameter would be used if the function were called via an unqualified name, e.g., f() instead of o.p().

Then Waldemar asked how one omits the receiver when calling via .apply or .call. Obviously passing undefined is defined in ES5 strict as propagating that value, not selecting a default parameter. This question could be answered plausibly, I speculate (not gonna try here).


> I believe that the intent of the proposal was that if you actually want to use "this" to refer to the implicit parameter you would have to explicitly declare it as:
>   #foo(this | arg1, arg2) {...}
> and that a declaration such as
>   #foo( arg1, arg2) {...}
> would not have a local binding for "this" and its implicit parameter would be inaccessible.

... and therefore the outer |this| binding would be inherited, as is done for a lexically scoped closure upvar.


> this| Cons
> It requires a new function declaration form.
> The meaning of "this" will be different in functions declared using various forms.
> Most methods only need the inner implicit this, yet they are forced to choose between:
>   function() {}
>   #(this|){}   //net saving only 4 chars.

I wouldn't require the | there, for a small savings. The |this| reserved word as first and only parameter name, in this case only, is enough.


> Self-calls are no longer lexically explicit, you have to look at the function header to recognize one

This is true today anyway (even ignoring host objects), for functions bound to a particular this or self using var self=this or Function.prototype.bind.


> Refactoring hazard when moving statements containing self-calls between methods.

Same counter-argument applies here.


> It  is a solution that is more general than is needed to address the primary use cases at issue.

I don't think we agree on the primary use case. If the primary use case is lexical |this| inherited by inner (sharp-function) from outer (sharp- or old-style) function, then #(arg1, arg2) {... this ...} suffices.

If the goal is to support OO-and-only-OO methods where |this| always has a sane binding, even when the function is not called via o.p() but rather via f() or f.apply(...), then the generality of not only an explicit receiver formal parameter, but a default value for the parameter (which could be lexically-outer |this|) is as far as I can tell necessary.


> It remove "the this object" from the vocabulary for talking about  methods. What is the new terminology for the implicit method parameters?

This is a con?! :-P

"The |this| object" (with unpronounceable | brackets) is completely confusing and a source of endless frustration. Anyway, see my first counter-"con" above: var self=this and bind already make it uncertain that you always get the receiver, even if you contrive all calls to be of the form o.p().

It may be o is the self-same bound receiver and therefore it doesn't matter, but that's not the crucial case. Where o might be the wrong object (or via f.apply(o)), then the lack of ability to override a lexical or otherwise-bound |this| exists already.


> It  takes a lexically scoped functional view rather than a OO view of methods.

I say this is a "pro" in the face of no strong OO method support in JS today. If a class proposal wins approval and does strengthen the OO view of methods, well, that proposal can adjust or interact with this one accordingly.


> ^this Pros
> It doesn't change anything about existing conventions for the implicit "this" parameter or "this" references.
> It works equally well with both new and legacy function declaration forms
> It address the primary use cases for outer this access without adding seldom needed generality

No, I don't think it does. Some of the use-cases want |this| not |^this|, i.e., the syntax matters. The refactoring hazard (I don't see you mention it below) bites.

The other use-case, what Alex Russell calls soft-bind, also wants |this| not |^this|. It simply wants a parameter default value instead of a hard binding to a given receiver object.


> Consistency: "this" always means "this"; self-calls are always lexically explicit
> It takes a classic OO view of methods .
> 
> ^this Cons
> Overloads "^" character; maintaining backwards compat with expressions like foo^this.bits would require parsing hackery.
> May need to use "#this", "@this", or "#^this" instead.
> It doesn't handle more than one level of function nesting.
> It isn't like Python
> It preserves the common OO language convention of implicitly naming the "receiver" parameter
> It takes a classic OO view of methods.

Leaving out the refactoring hazard seems like a big omission here. Functions get wrapped in lambdas all the time, and one can propagate |this| with some care by several means (var self=this, .bind) already.

But changing every *use* of the receiver parameter from |this| or |self| to |^this|, and then having to wish for |^^this| or else do more (var grand_self = this) hacks, shows the cost of working around the brittleness. Programmers do not want to distribute the encoding of an incidental or provisional parent-child relationship to *every use* of the receiver parameter.


> I'll leave it to reader to weigh the above pros and cons.  But I do have a closing statement:
> 
> There is a decades long disagreement among designers/users of function and object-oriented languages.  OO proponents think there is something special about the "receiver" of a method call and that "self-calls" have special significance. This perspective pervasively colors OO software designs. Functional proponents (and while I'm happy to represent OO people,  I'm reluctant to put specific words into the broad group of  functional people) seem to view objects/methods as simply one of many abstractions than can be constructed out of higher-order functions.  They see little that is "special" about OO conventions and don't generally apply   OO software design techniques. 
> 
> JavaScript up to this point seems to have done a pretty good job of balancing the OO and functional perspective within the language. However, I think removing the specialness of "this" and the implicit this parameter may be a step too far that breaks that balance.  

I'm to blame for all this. I'm not religious either. But I have to disagree about "good job of balancing".

JS's OO commitments are quite weak, or "flexible". Developers get burned by |this| being a parameter computed based on the callee's expression form all the time. This is a specific design decision I made in great haste, and while it allows different patterns or paradigms to be used by convention, programming in the large requires more than fallible, attackable conventions.

Since people already use var self=this and f.bind(o), we can clearly see the active use-cases (at least by source frequency if not dynamic frequency, but the latter could be studied in an instrumented browser engine). I claim these use-cases are not driven by any religious commitment. Rather, often you need to partially apply a function with a var self=this receiver, and it can't possibly work with any other receiver. Is that really "FP" vs. "OO"? I don't think so.

But the need to pass a callback or downward funarg that receives an guaranteed |this|, which most conveniently is often the enclosing function's |this|, is a common use-case. I think we should address it directly, and not turn this into a philosophical balancing act.

Also, I strongly believe that we need a strong-OO proposal in hand before we can judge the harm that explicit receiver parameterization might do to OO methods.

/be


More information about the es-discuss mailing list