"Syntax for Efficient Traits" is now ready for discussion
jdeville at microsoft.com
Sun Oct 10 20:26:37 PDT 2010
From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Dmitry A. Soshnikov
Sent: Sunday, October 10, 2010 3:58 AM
To: Tom Van Cutsem
Cc: Mark S. Miller; es-discuss at mozilla.org
Subject: Re: "Syntax for Efficient Traits" is now ready for discussion
On 07.10.2010 22:21, Tom Van Cutsem wrote:
Hi MarkM and Tom, possibly it will be interesting to you: http://gist.github.com/613924 -- a delegation based mixins implemented using Harmony proxies.
- working in two modes: traits (naming conflict predicting at early stage), or simple linearization (mixins mode, there is no warning about naming conflicts);
- "in" operator is working on object (e.g. "foo" in bar is true if either bar has own property "foo", or any of mixed objects have the property (including consideration of their prototype and mixin chains), or if the property is found in the prototype chain of the bar).
Cons: possible overhead on reading / or testing for "in". Should be avoided at lower level implementation.
TODO: syntactic sugar (possibly for CoffeeScript).
It's an interesting experiment. On first sight, your inheritance mechanism looks very similar to Ruby's modules.
Yes, Ruby also uses hidden proxy-class to communicate with a mixin module.
Although one big difference is that in your experiment, your mixins can also have their own prototype chain (IIRC in Ruby, modules can't inherit).
Yeah, not for user-defined inheritance. But modules there inherit though form some general built-in classes (i.e. Object, BasicObject, Class, etc).
For Ruby, BasicObject is the base type, Object inherits from it, and everything else inherits from Object. Object also mixes in Kernel.
Module derives directly from Object, but that's because Module is a class. (oddly enough, Module.superclass == Object, Module.class == Class, and Class.superclass = Module).
Modules themselves do not inherit from anything, because they are instances of the Module class (this is why Kernel.ancestors == [Kernel]). Note that modules do have singleton_classes.
I think that may be a bit too flexible, leading to even more complex lookups than is the case with multiple inheritance. If you would ignore the prototype of the mixins, you end up with something called "comb inheritance" (at each level, check the object and the mixins horizontally, if not found go up one level on the prototype chain and try again) which is a bit more tractable for programmers to deal with, I think.
Yes, such implementation is also possible. Moreover, implementation is very easy -- Module.new will be just Object.create(null). On the other hand, it's not required to be a complete clone of Ruby's modules. It may be better, though ;) But it prototypes of modules bring inconvenience and confusion, I'll make them prototype-less.
Your experiment does show that it's possible to combine conflict detection + delegation to some extend. However, I wouldn't call your conflict detection mode "trait mode".
Of course, because it's a similar to Ruby's module system though -- with liniarization, but not traits in respect of conflict predicting; I added "traitsMode" just to enable this small ability to warning an issue. And again, backing to classical traits, e.g. Ruby's Enumerable module -- is a trait: (1) it doesn't not have a state, (2) it does not break the state of classes/objects to which it mixied/include and (3) it does brings additional functionality based on the sate of an object (possibly, state provided by the class definition -- including needed methods and properties). And that this system does not provide naming conflict detection -- well, it's just one of possible implementations. It choosen linearzation though, to keep dynamics and reusing. I combined it providing a very basic of naming conflict detection.
First: the system is not conflict-proof: if some code adds a property to a mixin object or to the target object at a later point in time, potential conflicts will go undetected.
Theoretically it's possible to implement (e.g. store for a module list of objects/classes to which it included, and trap all [[Set]] generic set with checking, whether the new name is in some object. The same for objects). But practically it will be unnecessary overhead.
Second, even in "trait mode" the ordering of the mixins matters (trait composition is commutative, ordering is not important, at any level of composition).
Of course, if a composed (from several static modules/traits/mixins) "holy" object has all of those properties/methods as own, the order is not essential. But if to consider exactly reusing of the code, with inheritance, the order does matter. Also, since you freeze your traits, it moves to statics anyway (hello, Java) and you can't extend a trait anyway -- yeah, no extension, no problem with later naming conflict detection.
Third, there seems to be no obvious way to resolve/avoid a signaled conflict (traits have aliasing or overriding to deal with this).
Personally, I think that as long as it is well-defined (i.e. last defined method "wins") it will be fine.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the es-discuss