Using Object Literals as Classes

Claus Reinke claus.reinke at talk21.com
Sat Mar 24 02:53:04 PDT 2012


>> Neither. Currently, <| sets the [[prototype]] field of the RHS object,
>> and -if the RHS is a function literal- also specifies the [[prototype]]
>> field of objects generated from it. I suggest to remove that special
>> case for function RHS.
>
> (function () {}) creates two object, not one.

I'm not sure what you meant here. That is one (function) object,
using 'new' on it creates one new object to be initialized. Perhaps
you meant (p <| function(){}) creates two objects, the modified
function and its prototype? I had indeed managed to overlook
that for a while, so some of my earlier examples lacked a level
of indirection.

> We have to set the [[Prototype]] of both objects to something.

But why ever set both to the same thing? Actually, I find myself
disagreeing even before this, at the make-two-objects stage -
it needlessly overloads a useful operator (<|) to make it fit one
particular use case.

Assume that <|_ is an operator representing the simple base
case of <|, namely

    lhs <|_ rhs
    - create object from rhs as normal, but set [[Prototype]] to lhs

Then, instead of setting two properties in a pre-determined
create-two-objects pattern

    { prototype: p } <| function(){}

we can write out which property we mean to set, either

    function(){}.{ prototype: p <|_ {} }

or

    { prototype: p } <|_ function(){}

which isn't much longer, makes explicit what is going on, and
leaves the proto operator simple and flexible.

If the order of operands is inconvenient, define an infix operator
(or the best approximation thereof that JS can do):

    function class(cls) { return { subclass:
        function(constr){ return constr.{prototype: cls.prototype <|_ {} } }
    } }

or, more readably, with some form of short expression functions

    class(cls)=>{ subclass: (constr)=>constr.{prototype: cls.prototype <|_ 
{} } }

Using this, we can write

    class( { prototype: p } ).subclass( function(){} )

There are several language shortcomings that get in the way

- proper infix operators need language change
- <| and <|_ are half operator (taking expression parameters)
    and half language construct (taking syntax phrases), so one
    cannot built abstractions over them (example: extending the
    class wrapper above to set the function [[Prototype]] as well)
- making <| and <|_ proper operators requires shallow cloning,
    which isn't available

It would be good to address these. Apart from that, I see no
convincing arguments, so far, for overloading <|.

Arguments in favor of <|_ :
- has simpler semantics
- can emulate <|
- is invertible (could also be used for destructuring)

The latter point, in particular, would allow to describe prototypical
inheritance without reference to [[Prototype]] or __proto__, entirely
at the JS source level, using <|_.

> In deciding how to do this it is important to look at the role of
> each member of that object pair.  The existing language defines
> a relationship between them that we need to respect.  We also
> need to consider what is going to be least surprising to users
> given the most common usage patterns.

Surprise depends on expectation. Since we're defining a new
operator, expectation depends on how that operator is defined
and promoted.

> Consider
>
>  let F1 = function() {};
>  F1.foo = "foo";
>  F1.prototype.bar = "bar"
>
>  let F2 = F1 <| function() {};
>
> will a ES programmer expect F2.foo be be "foo" or undefined.
> Understanding that <| defines the [[Prototype]] of the the
> function they should expect "foo".

Agreed.

> What about
>   (new F2).bar
> will they expect "bar" or undefined. This is probably not something
> that they have thought about before, but I'm pretty confident that
> they will be astonished if they get undefined instead of "bar"

Why should they, unless they are confused about Function's
[[Prototype]] vs prototype? Of course, most JS coders are
confused about that at some point of their learning curve,
but <| would be the first operator to support that confusion.

Speaking for myself, I was sold on <| when I thought it was <|_,
and I was astonished about the extra bits thrown into its spec.

Perhaps the extra convenience of <| will win, but I thought it
useful to think about the language limitations that made the
convenient/complex definition seem necessary to you.

Claus
 



More information about the es-discuss mailing list