> > 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. >
Linearization is indeed a well-known approach to resolve the diamond, but it doesn't adequately 'solve' the problem from a software evolution point of view: small changes in one part of a large code base (i.e. changing the order of two 'include' or 'extends' statements) may end up impacting a totally different part of the code base - usually without warning. I can recommend Alan Snyder's classic 1986 OOPSLA paper titled "Encapsulation and Inheritance in Object-oriented Programming Languages", where he explains this and related issues with inheritance. > 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? > Yes, you are right: original traits do not define instance properties. What I meant is that, if an original trait's provided methods _depend_ on instance properties, then in principle the trait should _require_ a getter/setter to manipulate those instance properties. This forces a class that uses an original trait to provide a (usually public) getter/setter method such that the trait can access the instance property. So actually I was incorrect: it's not the trait's public API that is polluted, but the class that uses trait's API (if the property was not intended to be visible to external clients in the first place). > 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). > Indeed. Although I would stick with the more straightforward term 'trait generator'. The original Squeak Smalltalk traits implementation actually defined the concept of a MetaTrait (its role is to traits what metaclasses are to Smalltalk classes, but let's not go there ;-) > 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? > Correct. > > 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). > I agree, but I wish things were that simple. It all depends what your point of view is: from a software maintenance point of view, explicit conflict resolution (rather than implicit overriding) would be the better feature. I'm not denying that there is tradeoff here: traits are a more complex language feature than single inheritance, and don't cater to the 'live updating' feature of prototypal inheritance, but they support "safe" multiple inheritance and early conflict detection. > 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". > While I see your point, don't Javascript programmers also use alternatives to prototypal inheritance, such as "mixing in" properties of one object by copying them into other objects? Traits would provide more direct support for these use cases. Cheers, Tom
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

