"Syntax for Efficient Traits" is now ready for discussion

Dmitry A. Soshnikov dmitry.soshnikov at gmail.com
Sun Oct 10 03:57:47 PDT 2010


  On 07.10.2010 22:21, Tom Van Cutsem wrote:
> Hi Dmitry,
>
>     Hi MarkM and Tom, possibly it will be interesting to you:
>     http://gist.github.com/613924 -- a delegation based mixins
>     implemented using Harmony proxies.
>
>     Features:
>
>       - 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).

> 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).
>
>
>     P.S.: it's good to have /noopHandler/ for proxies as a built-in
>     function, it's a useful sugar (possibly even to make it as a
>     default handler (if some handler's properties are not specified),
>     though it may be unnecessary overhead).
>
>
> Yes, I agree! We've talked about this in the past but haven't yet 
> settled on a standard API. Perhaps we should.
> I think Proxy.noopHandler(target) may be a bit obscure. Other suggestions:
> Proxy.createHandler(target)
> Proxy.forward(target)
> Proxy.handle(target)

Yeah, also good names, I just used /noopHandler/ as first came to mind, 
since the section of the proxies article is called the same.

>     P.S.[2]: proxies are nice ;)
>
>
> Thanks! Keep on experimenting :-)
>

Yes, I do. The latest is "magic methods" directly on objects (possibly 
you saw -- http://gist.github.com/617338; I don't know what again with 
es-archive server, but it doesn't display the lettter which I send about 
"get+fn vs. invoke" -- 
https://mail.mozilla.org/pipermail/es-discuss/2010-October/thread.html 
-- there only answer on the letter with correcting the typo).

Maybe you can answer on this question regarding proxies? How can we 
handler in one /get/ of a proxy handler both cases of a call-site:

foo.bar

and

foo.bar()

?

Having /invoke/, we handler foo.bar(). However, the case when the /get/ 
returns should look (unfortunatelly?) like (foo.bar)(). Also of course 
foo.bar does not extracted as a functional object (including invariants 
foo.bar.call/apply). But I think, is it the case that user wants to used 
them exactly as funargs or he wants more to handle just access (exactly 
/calling/ of) to non-existing methods? P.S.: to avoid off-topic 
regarding this topic devoted to traits/mixins, it's better to answer to 
that trhead -- "get+fn vs. invoke"/.

/
> Cheers,
> Tom

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20101010/3de75c18/attachment.html>


More information about the es-discuss mailing list