strawman for the := operator

Russell Leggett russell.leggett at gmail.com
Wed Aug 8 11:01:24 PDT 2012


On Wed, Aug 8, 2012 at 12:35 PM, Brendan Eich <brendan at mozilla.org> wrote:

> Erik Corry wrote:
>
>> Hi
>>
>> This proposal offers a way to get around some of the strange semantics
>> of '=', specifically the way read-only properties and setters on
>> objects in the prototype chain can restrict what you can do on the
>> receiver of an assignment.
>>
>
> This is "strange" only insofar as you can't say what you mean if you want
> to override. There's no law of nature requiring = to override, though.
>

I actually think that the behavior of = makes total sense in regards to
setters and read-only properties. If that was not the behavior, it would be
pretty silly. If = on a setter overrode the setter, it would defeat the
point of the setter. Same with read-only properties.


>
> Assigning != defining in ES5, even in reality in fixed implementations of
> ES3, and in truth going back to the first JS implementation: proto-setters
> (internal, hidden) are invoked by assignment, readonly proto-props prevent
> = being used to override on a delegating object (silent failure, of course,
> due to lack of try/catch).
>
> What I'm getting at: is assignment != defining strange, or is the lack of
> expressiveness that left JS with = but not := strange, or is = not defining
> strange? I didn't want to assume the last was what you meant, since it is
> not obvious and not the only possible strangeness or asymmetry.


I think the problem is really the conflation of = depending on the context.
In more classical languages, there is a separation between methods and
data. Overriding would never be done with =. If it was data, it would set
the value of the field for that instance, and if it was defining a method,
that would typically be done as an extension. I think that a happy path for
users could aid in untangling the confusion, but I think we have to be
clear about what that happy path should be.


>
>
>     However it has some strangeness itself:
>>
>> * There is little point in having read-only properties if the common
>> way to do assignment is :=.  := will just walk all over a read-only
>> property.
>>
>
> No, readonly properties must be {writable: false, configurable: false} to
> have integrity, and := cannot redefine a non-configurable property.


This brings me to an important point. I think we are conflating too much
the ease of definition with the intent of Object.extend. Allen's strawman
actually says := is similar in intent but not semantics. I don't like that,
and I think it adds to the confusion. I think we have three cases here.

   1. You would like to mixin additional behaviors. This is commonly done
   with something like Object.extend, but that typically relies on [Put]
   semantics, but we really would like those to be defined. Mixins are
   available in many languages in various forms. There are many
   implementations for doing them in JavaScript and they are fairly commonly
   used. Mixins typically involve a group of methods which should always be
   mixed in together. Making super work correctly here is important.
   2. You would like to define a single property using a similar style as
   =, but you want to make sure it is a definition instead of a put.
   3. You have several values that you would like to [Put] - basically just
   a batch assignment.

I believe that := is ill suited to straightening out these cases. I think
it will commonly be needed for #3, but instead do definition. Its
convenience lends itself to abuse.

What I would recommend instead is that we match the cases more accurately.
For #1, I think we try to frame it for what it is - a mixin. I think that
is should be usable in conjunction with classes, as well single objects. I
would probably suggest the keyword mixin, but since I think we're avoiding
new keyword, perhaps we can reuse "with". Scala actually uses "with" for
this exact purpose.

    //can be used as part of the class declaration
    class Foo with Enumerable {
        //if similiar to ruby, would supply an each method here
    }

    //can be using in conjunction with extends
    class Foo extends Bar with Enumerable{...}

    //can also be used on a single instance
    class Point {
       constructor(x,y) {
          this with {
             //define accessor properties on new object that bind to
closure captured private state
             get x() {return x},
             set x(value) {x = value),
             get y() {return y },
             set y(value) {y = value},
             moveTo(newX, newY} {
                x = newX;
                y = newY;
             }
          }
       }
    }

    //assuming the 'with' operator returned the LHS after the operation, it
would be easy to be expressive doing something like
    let myPoint = new Point() with OneOffMixin;

One important point to be clear about. Even though the RHS would allow any
object, it would only actually copy the functions. This would be consistent
with MaxMin classes, and prevent the foot gun that Rick points out in his
dom example.

For case #2, I think that if we still want an easier way to do
defineProperty, we can still have :=, but we keep it similar to =.
>From the strawman problem example, that means you would have the choice -
instead of =, you can do :=, but its one to one.

    //you can choose to do this, which is a [Put]
    obj.c = -3;
    //or this which is a [DefineOwnProperty]
    obj.c := -3;

For case #3, we might want to revisit .= or .{ where they are always [Put]
based. I think that with the other constructs, it will be a lot more
obvious what the intention is. Or potentially, we just do Object.extend
(although I would recommend a different name because I think its confusing
in relation to the extends keyword of classes).

- Russ


>


>
>  * Copying private members from one object to another violates the
>> encapsulation pretty badly.  You would hope that using private names
>> allowed you to easily reason about which objects have which
>> properties, just by looking at the limited number of places a private
>> name is used.  But with this any code in the system that has two
>> instances of a class can splat object a's private properties with
>> those from object b.  It's rather like a replay attack in crypto.
>>
>
> Yes, this is an open issue in my view. If one uses a private name to brand
> an object, attackers who have access to such an object can use := to forge
> trojan objects.
>
>
>  * I don't understand the super stuff.
>>
>
> Allen should field this one.
>
> /be
>
> ______________________________**_________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/**listinfo/es-discuss<https://mail.mozilla.org/listinfo/es-discuss>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120808/670b865b/attachment.html>


More information about the es-discuss mailing list