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
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to