"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
>> 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
> 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
>> 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
>> 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
> 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".
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the es-discuss