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