On Wed, Jul 22, 2009 at 11:30 AM, P T Withington<[email protected]> wrote:
On 2009-07-22, at 10:52EDT, Mark S. Miller wrote:

Lately Alex and I been
reading <http://prog.vub.ac.be/Publications/2009/vub-prog-tr-09-04.pdf >.
Later today I will post some initial thoughts on how this could also
be supported in within the same general framework.

Thanks for this pointer.  I am just starting to read and it looks
intriguing. Traits that also have state might just make me give up my
mixins!

I'm very pleased to see that our work is relevant for the Javascript community. If it's any help, the slides I used to present the paper a couple of weeks ago at ECOOP09 are available at: http://prog.vub.ac.be/~tvcutsem/presentations/Traits-ECOOP09.pdf

Some comments on Mark's proposal follow:

Given the following helper abstractions:

------------------------
const traitConflict() {
 throw new Error("trait conflict");
}

const conflictDesc = Object.freeze({
 get: traitConflict,
 set: undefined,
 enumerable: false,
 configurable: true
});

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?

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);
   }
 });
}
------------------------

If I understand correctly, the line

        Object.defineProperty(self, k, newDesc);

installs the trait's 'newDesc' property in the object denoted by 'self'. Two issues need be taken into account if the property refers to a method:

- the trait method needs a way to refer to the composite, such that it can invoke required methods. From your example below I infer that 'self' can be made to refer to the composite because you explicitly parameterize classes with 'self'. This makes sense, especially for traits which can be seen as 'incomplete classes'.

- if the trait method refers to lexically free identifiers, will these identifiers be bound correctly if the method is invoked on the composite object? More concretely, using the example below: if the method 'start' is invoked on a ParticleMorph, can I rest assured that the reference to 'timer' in AnimationTrait's 'start' method refers to AnimationTrait's timer?

I think it works because you represent methods simply as functions (closures). I assume that newDesc's 'get' propety 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? If this is the case, that's great because it avoids the need for Self- style delegation, which we required in our paper because methods were not mere functions (they were implicitly parameterized with 'self', and delegation was required to late-bind 'self' to the composite).

and using no sugar beyond that defined in the thread starting at
<https://mail.mozilla.org/pipermail/es-discuss/2009-March/ 009115.html>,
here is the main example from Tom's paper:

------------------------
class AnimationTrait(self, refreshRate) {
 const timer = makeTimer();
 public start() {
   timer.everyDo(refreshRate, const() { self.animate(); });
 }
 public stop() { timer.reset(); }
}

const ParticleTrait(self, radius, moveRate, dx, dy) {
 const resultTrait = {};
 addTrait(resultTrait, CircleTrait(self, radius));
 addTrait(resultTrait, AnimationTrait(self, moveRate), {
   start: 'startMoving',
   stop: false
 });
 addTrait(resultTrait, object {
   public animate() { self.move(dx, dy); }
 });
 return Object.freeze(resultTrait);
}
const ParticleMorph(radius, moveRate, dx, dy) {
 var self = {};
 addTrait(self, ParticleTrait(self, radius, moveRate, dx, dy));
 return Object.freeze(self);
}
------------------------

Once we know whether or not we want to adopt traits, and whether we
wish to adopt precisely Tom's traits semantics, we could consider
further sugar for supporting exactly this pattern. Until then, and in
order to get to such a state of clarity, it is pleasing that traits
semantics such as Tom's can be expressed well directly in terms of the
currently proposed non-inheriting classes-as-sugar system.


--
   Cheers,
   --MarkM

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to