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