[Harmony proxies] Proxies, prototype chain and inheritance

David Bruant david.bruant at labri.fr
Tue Mar 22 06:19:52 PDT 2011


Le 22/03/2011 02:22, Mark S. Miller a écrit :
> Hi David, are you aware of
> <http://wiki.ecmascript.org/doku.php?id=strawman:proxy_instanceof>?
Yes, I am. This strawman suggests to trap if you're a constructor from
the right hand side of the expression. My idea is to try to find a
trade-off to offer some liberty (not a trap) to observe inheritance for
the left hand side as a proxy.

> I just heard from Brendan today that Andreas Gal has encountered some
> DOM emulation issues that might make us want us to consider this
> sooner rather than later.
Just to make sure we're trying to solve the same problem, I'm going to
describe it.
Within the DOM interface as used in browsers, an HTML element implements
both the Element and the EventTarget interfaces. With the ECMAScript
prototypal inheritance mechanism, having "(e instanceof Element && e
instanceof EventTarget) === true" would need to mean that all Element
instances are also EventTarget or the opposite, both being inaccurate
because the two are orthogonal concerns. The problem with ECMAScript as
it is is that it doesn't allow "orthogonal inheritance" (aka multiple
inheritance)

So we're trying to find a way to offer "(e instanceof Element && e
instanceof EventTarget) === true" without imposing anything on the
prototype chain.

If I understand the strawman correctly, the approach is to work on the
right hand side, add a hasInstance trap to the constructor.
The approach I describe in the initial message is to provide to the
left-hand side a list of objects which describes the list of
[[Prototype]] objects used in the native internal [[HasInstance]]
algorithm. One of the main advantage I see with what I have described is
that all instances of the same class can communicate through their
prototype and still dynamically inherit from the "same" object (same or
a proxy forwarding to it. Which is the same thing when it comes to
inheritance, but which is different if you really care about object
identity).


> How well would it also address the cases you have in mind?
Actually, we're trying to solve the same problem with two different
approaches, so I would answer yes to this question.
Here are a couple of issues I see with working on the right hand side
(constructor) though:
- You may have to keep references of all the instances you want to be
say true to (I currently do not see how you can do differently) which
may cost a lot of memory.
- If you want to define multiple inheritance with your own classes (not
DOM), you have to define all your classes as a Proxy.
- instanceof will be inconsistent with Object.getPrototypeOf
-- Your object can be "instanceof" one thousand constructors while
Object.getPrototypeOf returns null.
-- Another problem is that if a1 instanceof A && a2 instanceof A, you
would wish a way to make a1 and a2 communicate through A.prototype
(shared prototype properties, for instance). Since Object.getPrototypeOf
isn't of any help reaching A.prototype, a1 is disconnected from a2
(which is weird since they're supposed to inherit from the same class).
You cannot retrieve the prototype to create other object inheriting from
the same prototype (var a3 = Object.create(Object.getPrototypeOf(a1)) is
not possible)

I have tried to provide an idea (once again, it's incomplete as I have
exposed it) on how to keep instanceof and Object.getPrototypeOf
consistent with each other to some extent. Of course, it comes at a
couple of costs (like the solution in the strawman), but I have kept
communication between all object inheriting from the same prototype.

Once again, I understand that it's too dangerous to provide traps to
Object.getPrototypeOf (which could return an object with cyclic
references) or instanceof (which would provide the ability to the
handler writer to keep a reference to the constructor and to create
instances of the tested constructor). However, when it comes to emulate
new and different inheritance mechanism, I think that proxies should
provide something to emulate these new inheritance mechanisms on
instanceof and Object.getPrototypeOf, otherwise, you will always face
the issues I have raised above under "instanceof will be inconsistent
with Object.getPrototypeOf".

Cheers,

David

>
> On Mon, Mar 21, 2011 at 4:08 PM, David Bruant <david.bruant at labri.fr
> <mailto:david.bruant at labri.fr>> wrote:
>
>     Hi,
>
>     Proxies can be helpful to emulate multiple inheritance
>     (http://journal.stuffwithstuff.com/2011/02/21/multiple-inheritance-in-javascript/).
>     Long story short, with the get and set traps, you can emulate this
>     multiple inheritance without having the required prototype chain.
>     However, since "Object.getPrototypeOf" and "instanceof" cannot be
>     trapped, these cannot be used to observe inheritance. There are
>     perfectly valid reasons for which they cannot be trapped, I won't
>     question this. My present suggestion is a mechanism to unable
>     inheritance observation while keeping a couple of language
>     properties safe.
>
>     One ECMAScript invariant when dealing with objects is that the
>     prototype chain of an object is set once for all at object
>     creation. Consequently, for all function F, the ordered list of
>     objects traversed while calling F.[[HasInstance]] (ES5 15.3.5.3
>     step 4 a) will always be the same throughout the entire object
>     lifetime. My idea consists of being allowed to provide this such a
>     list at proxy creation time. With this list, multiple inheritance
>     can be observed if 'instanceof' calls looks at this list. This is
>     the idea, then comes "how to achieve it?". Everything said from
>     now on is just thoughts of how the idea could be achieved, neither
>     definitive thoughts nor exhaustive.
>
>     The idea can be achieved at different degrees of consistency with
>     how objects currently work. Here are the questions that I have
>     asked myself while thinking about the topic:
>     At initialization, should this list replace the second prototype
>     argument of Proxy.create or should it be provided as a complement
>     just for the purpose of instanceof?
>     What level of consistency with Object.getPrototypeOf?
>     Let's imagine for a minute that the list replaces the prototype
>     object as second argument:
>     ---------------
>     // a, b, c are already existing object
>     function A(){} A.prototype = a;
>     function B(){} B.prototype = b;
>     function C(){} C.prototype = c;
>
>     var p = Proxy.create(handler, [a,b,c]);
>     // p instanceof A && p instanceof B && p instanceof C === true
>     --------------
>     Instead of returning "a", "Object.getPrototypeOf(p)" could return
>     something like: "Proxy.create(forwardingHandler(a), [b,c]);".
>     Please notice the recursivity. If the list is empty,
>     Object.getPrototypeOf returns null, obviously.
>     One little issue is that "Object.getPrototypeOf(p) !==
>     Object.getPrototypeOf(p)" since Proxy.create creates a new object
>     (new identity) each time, but a cache will solve the issue easily
>     and the same object can be returned each time. This cache trick
>     will preserve the invariant that an object has a unique prototype
>     chain.
>     Two invariants (at least) are broken, though:
>     - p instanceof A iff there exists an n such that A.prototype ===
>     (Object.getPrototypeOf)^n(p)
>     -if two objects (o1, o2) inherit from the same two object (a, b)
>     in the prototype chain, then there is a unique partial prototype
>     chain going from a to b or b to a.
>     However, thanks to the forwarding proxy we keep a way for all "A"
>     instances (created through "new A()" or with my proxy mechanism)
>     to communicate and all still inherit (actually or virtually) from
>     the "same" "A.prototype" object.
>
>     The idea is a bit new, so there are certainly plenty of other
>     questions I haven't thought of. And once again, this was just one
>     possible beginning of how to achieve my idea above.
>
>     Anyway, through the "getPropertyDescriptor", "getPropertyNames",
>     "has", "get", "set" and "enumerate" traps (let's call them
>     "prototype-climbing traps" since there behavior on regular object
>     is to climb the prototype chain), there are already plenty of ways
>     to break ECMAScript inheritance invariants. My personal opinion is
>     that "Object.getPrototypeOf" and "instanceof" are currently
>     overprotected with regard to the already broken invariants. I
>     agree that trapping them is too dangerous, because it would
>     provide too much freedom (and there is the security issue with
>     instanceof). However, I think there is room in between to provide
>     some power to the proxy writer while keeping things under control.
>     I have tried to show one such idea and one way to partially
>     achieve this idea, there are different achievement tracks for the
>     same idea and certainly other ideas.
>
>
>     Another way of solving the inconsistency I see in providing the 6
>     "prototype-climbing" traps ("getPropertyDescriptor",
>     "getPropertyNames", "has", "get", "set" and "enumerate") and
>     protecting Object.getPrototypeOf and instanceof could be to
>     provide a softer version of proxies where these traps wouldn't be
>     provided. Prototype-climbing internal methods would be delegated
>     to the ECMAScript engine with the default behavior they have on
>     objects (but still calling own traps when their algorithm requires
>     them to). By design of this softer version, proxy writers would be
>     ensured that inheritance will be done properly for them and they
>     don't have to worry about it. They would just have to take care of
>     the own properties "layer". (This paragraph is a slightly
>     different topic. If you feel it requires its own thread, feel free
>     to fork)
>
>     David
>
>     _______________________________________________
>     es-discuss mailing list
>     es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
>     https://mail.mozilla.org/listinfo/es-discuss
>
>
>
>
> -- 
>     Cheers,
>     --MarkM

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


More information about the es-discuss mailing list