Class double-bind

Allen Wirfs-Brock allen at wirfs-brock.com
Wed Mar 4 16:56:31 UTC 2015


On Mar 4, 2015, at 4:21 AM, Jason Orendorff wrote:

> Oops, accidentally sent this only to Allen.
> 

and I replied as follows:

On Mar 3, 2015, at 9:34 AM, Jason Orendorff wrote:

> On Mon, Mar 2, 2015 at 5:54 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
>> Pretty much everyone I've seen discover the function binding equivalent  (or have it explained to them for the first time) was shocked.  What! 'function fact(n) {return n>1? n*fact(n-1):1}' doesn't necessarily recur on the same function?  that's crazy! is the typical reaction.
> 
> Perhaps this is a little overstated? In Scheme, Python, Lua, Ruby, and
> Smalltalk, code in a function/class/method that refer to it by name
> refer to the external binding. That binding is mutable in all those
> languages except Ruby and Smalltalk (and even in those, I think you
> *can* mutate it, but the language discourages it).

indeed:
Smalltalk at:#className put: whatever.

but it would typically be frowned upon.

> 
> So basically every language in this space has the same behavior. JS
> differs only in that it has a named-function-expression syntax, which
> I think the others lack.

I agree that strictly from a binding perspective it's not particularly crazy, but it is surprising as most programmers don't think about function and class declarationa primarily as binding forms.  The focus is on defining the entity that is the initial value (and typically only value) of the binding rather than on the binding itself.

The real anomaly is that the exact same function definition text (eg: `function x() {x()}` ) in some context's creates a local binding for 'x' and in others does not. 

We can avoid that anomaly for class definitions by always providing a local binding if an identifier follows the 'class'.  If we do that, then a class declaration like:
  class x {};
can be understood and even implemented as desugaring into:
  let x = class x{};

That's essentially what the ES6 spec. describes.

This gives a single internal naming semantics for all class definition which is how most people think about it most of the time.  In the rarer situations where developer really want  to manipulate the bindings, it is good that  they make that intention clear by using:
  let x = class {};

> 
> I can't think of a language where two bindings are created and they
> can then diverge. This is novel weirdness.

In C++/Java/C# etc. you don't see it because the corresponding declarations create immutable bindings. I agree that it would have been nice of we could have done that.  From that perspective having an immutable class name binding that is visible from the class body isn't novel at all.  The two bindings is simply an specification level hack to provide that without using an immutable binding.

> 
>> Similarly, it would be crazy if:
>> class Foo {
>>     static makeFoo() {return new Foo}
>> }
>> 
>> didn't give you a factory method that created instances of Foo.
> 
> Well, you're calling every existing dynamic language with classes
> crazy. I don't think that's very useful.

Oh, it would be interesting to talk about how central (or not) the role of binding mechanisms is in various languages, but it probably isn't going to help us reach agreement right now. 

I will add that I don't find your class wrapping use case very compelling. I think it is much more applicable for simple functions than for classes.  Creating a high fidelity  class wrapper is challenging when you factor in things like the 'new' operator, 'instanceof', and downstream subclassing of the now wrapped function. I don't think we're going to see nearly as often as we see self referential class bodies.

Allen
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150304/98e17156/attachment.html>


More information about the es-discuss mailing list