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
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss