Harmony classes [Was: Operator overloading revisited]
Tom Van Cutsem
tvcutsem at vub.ac.be
Mon Jul 27 02:56:04 PDT 2009
>> Do I get it right that a trait composition conflict is only raised
>> upon
>> accessing the conflicting property? In AmbientTalk, the error is
>> signaled at
>> composition-time (i.e. as if your addTrait method would raise an
>> exception).
>> What's your rationale for deferring the exception until the
>> property is
>> accessed?
>
> This change of semantics was more exploratory than purposeful. A
> retroactive rationalization though is that a conflict that isn't used
> shouldn't need resolution. C++ does something like this for its
> multiple inheritance. Of course, they detect possible use statically
> as well, so they don't pay the price of delaying the failure till the
> use actually occurs.
>
> If desired, it would be simple to alter the scheme I posted so that it
> signals the conflict at composition time instead. I have no strong
> opinion on which we should prefer.
Me neither, as long as it's obvious to the programmer what composition
caused the conflict.
>>> const isSameDesc(desc1, desc2) { ... }
>>>
>>> const addTrait(self, trait, opt_advice) {
>>> const advice = opt_advice || {};
>>> Object.getOwnPropertyNames(trait).forEach(const(k) {
>>> const newDesc = Object.getOwnPropertyDescriptor(trait, k);
>>> if (k in advice) {
>>> const k2 = advice[k];
>>> if (k2) { k = k2; /*String(k2); ? */ } else { return; }
>>> }
>>> const oldDesc = Object.getOwnPropertyDescriptor(self, k);
>>> if (oldDesc) {
>>> if (isSameDesc(oldDesc, newDesc)) {
>>> // already cool
>>> } else {
>>> Object.defineProperty(self, k, conflictDesc);
>>> }
>>> } else {
>>> Object.defineProperty(self, k, newDesc);
>>> }
>>> });
>>> }
>>> ------------------------
It's really nice that you can specify trait composition in Javascript
using metaprogramming this easily. I checked your implementation
against our implementation of traits in AmbientTalk. There is one
issue that we had to work around, which relates to "default
properties" that are present in every object: we had to exclude such
properties 'by default' since they would otherwise always cause
conflicts. My guess is that if such properties exist in JS, you would
probably set their 'enumerable' field to false to filter them out.
>> I think it works because you represent methods simply as functions
>> (closures). I assume that newDesc's 'get' prope[r]ty refers to a
>> function that
>> represents the trait method, and this function has closed over its
>> lexical
>> scope and will correctly refer to lexically free identifiers even
>> when
>> installed in a different object. Am I right?
>
> Yes, except for one detail. In this case, it is newDesc's 'value'
> property rather than its get property. Starting with ES5, a property
> is either a "data property" or an "accessor property". The descriptor
> of a data property has the form
> { value: <any>, writable: <boolean>, enumerable: <boolean>,
> configurable: <boolean> }
> The descriptor of an accessor property has the form
> { get: <function () -> any>, set: <function (any)>, enumerable:
> <boolean>, configurable: <boolean> }
Thanks for the clarification. Could you point me to a page that
explains the rationale behind distinguishing data properties from
accessor properties? At first sight, it appears you don't need both
since accessor properties can easily subsume data properties.
Kind regards,
Tom
More information about the es-discuss
mailing list