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
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss