July 25, 2012 - TC39 Meeting Notes

Allen Wirfs-Brock allen at wirfs-brock.com
Sat Jul 28 18:04:51 PDT 2012


On Jul 28, 2012, at 5:37 AM, Herby Vojčík wrote:
>> ...
> 
> To be precise, [[Put]] and [[DefineProperty]] are different intents. Dveelopers may not like it, because they used to [[Put]], but it is probably needed to distinguish them.
> 
> [[Put]] is high-level contract (a, update your 'push' facet with value), [[DefineProperty]] is low-level contract (a, add/update your slot named 'push' with value).
> 
> I am inclined to see [[Put]] used to shadow methods as an abuse of high-level interface to do low-level patching.
> 
> But of course, unless there is nice sugar, everyone uses [[Put]] since it's easier to write (and read).
> 

I think there is a very important point here that I hope we don't loose in the weeds of this discussion.  The distinction between assignment and definition (ie, between [[Put]] and [[DefineOwnProperty]]) was not  very important when all ES had was data properties and there was no way for ES code to manipulate property attributes. In those pre-ES5 days, [[DefineOwnProperty]] didn't even exist and the installation of object literal properties were specified using [[Put]] semantics.  In those days, it was fine to think of property definition  as simply an assignment  (ie the = operator or Put]]) to a unused property name.

However, as soon as we have things like accessor properties, pragmatically configurable attributes, methods with super bindings, real inheritance hierarchies, classes, etc. the distinction between assignment and definition comes much more important.  Continuing to conflate them is going to lead to increasing confusion.  The "override mistake" issues is just the first and simplest of the sort of issues that result.  In post ES5,  programmers really need to to  learn and use the distinction between property assignment and property definition.  To ensure this, we need to provide language features that guide them towards this understanding and proper usage.

Herby correctly identifies where we stand right now.  ES developers need and want something that approaches the convenience of = for dynamically defining properties. As long as we only have a procedural API (Object.defineProperty) for dynamic property definition most won't learn the distinction and even those that do will frequently ignore it for the sake of convience.  ES6 needs a concise and friendly way to dynamically define properties.  The syntax needs to approach the connivence of = but it needs to bring emphasis to the distinction between assignment and definition.  Without  it,  ES5+ES6 will have collectively result in a more confusing and error prone language.

More below...
>> a call to Object.defineProperty instead, my first reaction would
>> certainly be "but why isn't a regular assignment used here?". A comment
>> could be added to explain the [[CanPut]], but that's what I would call
>> "boilerplate comment".
>> 
>> So far, to the general question "why is Object.defineProperty used
>> instead of a regular assignment used here?", the only answer I find
>> acceptable is "defining custom configurable/writable/enumerable",
>> because these are things local to the code that have no syntax for them.
>> In most cases, getter/setters can be defined in object literals.
>> Adding "the prototype may be frozen, thus preventing shadowing" to the
>> acceptable answers makes local code review harder.
> 
> :-/ But that is how it is, no?
> 
>>> Though not very constructive, I'd say this is the case where
>>> 
>>> a.{
>>>   push(elem) {
>>>     ...
>>>   }
>>> };
>>> 
>>> is definitely missing.
>> I remembered that .{ semantics was a [[Put]] semantic, so it wouldn't
>> solve the problem. Did I remember something wrong?
> 
> Of course. Mustache has the same semantics as extended literal, so it was [[DefineProperty]] with appropriate enum/conf/writ (and setting home context for methods, so in fact it did defineMethod).

I still think  a dynamic property definition syntax can be based on something like mustache. Two months ago,  there was interest at the TC39 meeting in further exploring mustache.  Some of that interest was motivated by these definitional issues.  However, our attempt to do this crashed and burned badly because we tried to accommodate a desire to make the same syntactic construct also serve as a "cascade".  However, cascades require [[Put]]/[[Get]] semantics and this is in directly conflict with the requirements of dynamic property definition. Confusion about this is reflected in the quotes immediately above.  We should have recognized before we even tried, that trying to combine those two semantics just won't work.

However, here is the sketch of a proposal for something that might work.

We introduce a new operator that is looks like  :=

This is the "define properties" operator.  Both the LHS and RHS must be objects (or ToObject convertible).  Its semantics is to [[DefineOwnProperty]] on the LHS obj a property corresponding to each RHS own property.  I does this with all reflectable own properties. It includes non-enumerable properties and unique named properties but not non-reflectable private name properties.  It rebinds methods with super bindings to the RHS to new methods that are super bound to the LHS.

The above example would then be written as:

a := {
  push(elem) {
    ...
  }
};

rather than, perhaps incorrectly as:

a.push = function (elem) {
    ...
};

or, correctly but very inconveniently as:

Object.defineProperty(a, "push", {writable: true, configurable: true, enumberable: true,
    data:function (elem) {
        ...
        }
    }
);

Note that while the above example uses an object literal as the RHS, it could be any object.  So, := is essentially a operator level definition of one plausible semantics for a Object.extend function. Using an operator has usability advantages and it also makes it easier to optimize the very common case where the RHS will be a literal.

:= is used because it is suggestive of both property definition (the use of : in object literals) and of assignment (the = operator).  := also has a long history of use as an assignment-like operator in programming languages. The visual similarity of = and := should push ES programmers to think about then as situational alternatives whose semantic differences must be carefully considered.  The simple story is that one is used for assigning a value to an existing property and the other is used to define or over-ride the definition of properties. 

I really think in a language where we have both [[Put]] and [[DefineOwnProperty]] semantics that we really need both = and :=

Finally, this discussion caused me to realize that I messed-up  on an important detail when I prepared and presented the class semantics deck (http://t.co/PwuF12Y0) at the TC39 meeting. 

In the deck, I incorrectly stated that I was proposing that the attributes associate with a property created via a concise method definition (in a class or object literal definition) should have the attributes {writable: true, configurable: false}. I had a hard time defending that choice at the meeting.  There is a good reason for this, that attribute combination was never what I intended but I ended up trying to defend what I wrote rather than what I really wanted.

Instead, what I meant to say was {writable: false, configurable: true}. Brendan and perhaps others have in the past expressed that this is a strange combination because it disallows assigning a value but changing the value can still be accomplished using Object.definePropertry.  The above discussion gives me the concepts necessary to explain the motivation for this design.   It's simple, this attribute combination disallows using [[Put]] updates while allowing [[DefineOwnProperty]] updates, just as described above. A concise method definition should be conceptualize as defining an invokable method, not an assignable data property.  That is exactly what my desired attribute combination supports.  writable: false means that [[Put]] expressed via the = operator cannot be used to modify such a method, even if it is inherited.  configurable:true says  that Object.defineProperty or the := operator proposed above may be used to modify or over-rided the method definition.   Consider a class definition such as:

class Stack {
   push(elem) {...}
   pop () {...}
   isEmpty() {...}
   constructor () {...}
}

let stk = new Stack;
...
stk.push=5; 

The last statement probably is not doing what the user intended when they mind-farted that line of code.  With my proposed attributes this assignment won't create a probably unintended instance specific over-ride of the method defined by the class.  If this is strict code it will throw. If an ES programmer really wanted to do such an over-ride their intention is much clearer if they have to say:

stk := {push: 5};  //over-ride class provided method with an assignable data property 

or

stk := {push(elem=null) {...}} //over-ride class provided method with an instance specific method

In summary, concise methods are new syntax for defining properties.  To me, it makes a lot of sense that the properties they create do not allow  =/[[Put]] updates.
:= is a better mustache that helps distinguish [[Put]]/[[DefineOwnproperty]] intents. We need both.

Allen




   





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


More information about the es-discuss mailing list