"Syntax for Efficient Traits" is now ready for discussion (was: Classes as Sugar is...)

Dmitry A. Soshnikov dmitry.soshnikov at gmail.com
Wed Sep 15 07:50:32 PDT 2010


  On 15.09.2010 1:39, Tom Van Cutsem wrote:
>
>>         Hm, "function for making trait", "trait itself". Too many
>>         things. It may bring complexity. Yes, i thought (and see in
>>         other languages, the same Ruby) that Enumerable is a trait
>>         itself, and not a special (non-needed to me) function. A user
>>         needed just a trait, not a function + trait.
>>
>>
>>     Please check out <http://traitsjs.org/>. The notion of traits
>>     there and in this strawman is the "stateful traits" approach that
>>     Tom first explained at
>>     <http://prog.vub.ac.be/Publications/2009/vub-prog-tr-09-04.pdf>.
>>     Until I saw this paper, I had written traits off as a dead end.
>
>     It seems something interesting there, thanks, I'll take a look on
>     the paper (as well as on all examples on the site). However, I'm
>     aware that concept of a "trait" in contrast of a "mixin" has no
>     state to avoid conflicts.
>
>
> It is true that original traits are always stateless. This is not so 
> much to avoid name conflicts as to avoid the whole problem of diamond 
> inheritance (duplicate state inherited via different inheritance 
> paths, cf. C++'s virtual inheritance).

Yeah, I see. Though, in ES it should be quite clear: there is one 
(vertical) prototype chain; at every link of the prototype chain, can be 
several included traits/mixins (i.e. horizontal chain of mixins). Thus, 
the last included module -- overrides (shadows) the previously included 
one. The same again is made in Ruby. At last, the whole inheritance 
chain (vertical + horizontal) is linear.

I.e. possibly the diamond problem should not be solving in case of 
mixins/traits at all. Because traits/mixins are /exactly small traits/ 
(small additional part of functionality), which may be /completely 
independent by semantics/ from the the vertical chain. And therefore 
there is no need to know -- which method to call. The answer is simple 
(in case of mixins) -- the last one included module -- overrides 
previously included module in respect of the same method name (thus, in 
Ruby, own a class's method with the same name overrides module's one).

It's the easiest way though (Python e.g. uses linearization too to avoid 
diamonds issue). The way with warning a user about naming conflicts (in 
particular, in your implementation too) seems also good. From the other 
hand we don't warn a user if he overrides (in JS) a parent method with 
the same name.

> On the other hand, stateless traits have their drawbacks: you need to 
> pollute a trait's public API with getter/setter methods to 'fake' state.
>
I though (again, maybe in "original traits"?) that all needed state for 
a trait is provided by a class (which includes the trait). And the trait 
in contrast with a mixin do not provide (and create during execution of 
its methods) any additional object's state, creating new instance 
properties. Or you mean some additional (auxiliary) variables needed for 
a trait?

> The key idea behind the Traits discussed here and in that paper is 
> that they are 'generative': you generate them by calling a function 
> that returns new traits.

Yeah, sort of Meta-Traits (constructors of traits).

> This allows these traits to capture lexically visible state (usually 
> the arguments of the generator function). Using this approach you can 
> make stateful traits, and apart from instantiation issues (see below), 
> there is no need to distinguish a class from a trait anymore.

Yes, I see. It seems interesting approach. Since you have all needed 
(auxiliary) variables for a trait and do not pollute (as a mixin) 
object's state. I.e. the object may even not to have required 
methods/state for the trait? All this may be passed as arguments for a 
meta-trait?

>>     I'm still not sure I understand you correctly. But if I do,
>>     Override is your vertical composition and Compose is your
>>     horizontal composition. Via Object.create, one can also mix
>>     traits with vertical composition using the JavaScript prototype
>>     chain. But then unbound inherited methods become observable,
>>     leading to all the normal confusions. By using only Override for
>>     vertical composition, only bound methods are observable. (This is
>>     like the old "binding on extraction" property of old ES4 classes.)
>>
>     Yes, regarding the end state of an object (via
>     override/composition) you understand it correctly. But I meant
>     exactly delegation based inheritance and asked -- why do you have
>     (propose) a scheme where an object will have /own/ properties but
>     not inherited? In Ruby as I showed, if we change Enumerable module
>     (read Enumerable trait/mixin) by adding a new method "new_method"
>     then the instance "things" of the class "MyCollection"
>     automatically via delegation has access to the new method.
>
>     And regarding classes, I think it's not acceptable not to have
>     inheritance and to create also own properties. Even if will be a
>     good VM optimization, the dynamics of the languages is lost -- if
>     we add a new method to a class -- will all instances see this new
>     method? That exactly I'm asking.
>
>
> Ruby's modules and instance-class relationships indeed work via 
> delegation and enable changes to all live instances by changing 
> modules/classes, and per-instance customization. OTOH, because they 
> chain together objects/classes/modules via delegation instead of 
> flattening the properties, they don't provide the early conflict 
> detection properties of traits. I think you can get either one or the 
> other, but not both from 1 language feature. Javascript already has 
> prototype delegation. Adding traits would allow us to express object 
> composition in different ways, with different tradeoffs.

Yes, that exactly I was asking Mark. So the "issue" is that's not 
possible to have at the same time delegation base traits+protos and 
early conflict detection. I see. Don't know what is better. Such 
conflicts appear not often (and usually consciously are made by a 
programmer, i.e. "yes, I knew that previous mixin/trait also has such 
method, and I want to use the method from the second module. Therefore, 
I include first this module, and then -- the second one. If I wanted 
vice-versa, I'd included first the second module, and the the first one" 
-- that what is Ruby uses).

>>
>     Just to clarify and not to confuse terminology/meanings, do I
>     understand correctly: "low integrity " -- non-|this|-bounded
>     inherited functions, and "high integrity" -- vice-versa --
>     statically bound |this| and own properties? Thanks.
>
>
> I think this is the case + low-integrity = non-frozen object and high 
> integrity = frozen object.
>
>     By the way, the phrase "a trait instantiate" in general case
>     contradicts to traits concept. Because in many (all?)
>     implementations and some general definitions of traits -- traits
>     in contrast with classes cannot instantiate objects. They are just
>     additional modules (to add small traits for the instance).
>
>
> Indeed, for original traits, "instantiating a trait" makes little 
> sense. Original traits need classes to provide state. As indicated 
> above, generative traits can capture state, and if they don't have any 
> missing required properties, it could make sense to instantiate them.
>
Yes, now I see the feature of the implementation.

> In MarkM's proposal, a "trait class" is a function that, when called, 
> returns a property descriptor map. A "class" is a function that, when 
> called, returns an object created from a property descriptor map. 
> While technically you can "instantiate" a trait by calling a trait 
> class function, you will end up with a property descriptor map, not an 
> instance. From the end-user's point of view, it still only makes sense 
> to instantiate class functions.
>

Yes, I see. And then this property descriptor (an instantiated trait) is 
merged with the original object.

OK, Tom, thanks for your clarifications, it was useful.

The idea of "non-traditional available for instantiation traits" sounds 
interesting. However, the issue with non-inheritable classes is have to 
avoided. I may ask any JS-programmer -- whether he needed classes in JS 
additionally to the (already available "classes" as 
"constructor+prototype" pair) prototypes, and moreover, these classes 
/cannot inherit/ and every object created from a class will have /own 
methods/ (but delegated ones) -- they will answer -- "No".

Dmitry.

> Cheers,
> Tom

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


More information about the es-discuss mailing list