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

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

Reply via email to