Do we really need the [[HasOwnProperty]] internal method and hasOwn trap
allen at wirfs-brock.com
Wed Nov 14 08:46:35 PST 2012
On Nov 14, 2012, at 1:22 AM, Tom Van Cutsem wrote:
> 2012/11/14 Allen Wirfs-Brock <allen at wirfs-brock.com>
> On Nov 13, 2012, at 12:25 PM, Tom Van Cutsem wrote:
>> It's dawning on me that the main reason I dislike adding flags to the fundamental traps is that it breaks the 1-to-1 symmetry with the operations that they intercept:
>> Object.getOwnPropertyDescriptor(proxy, name) // traps as getOwnPropertyDescriptor(target, name)
>> This symmetry should make it relatively easy for an ES5 programmer to pick up the handler API. The signatures look familiar (at least for all operations that are expressed as function calls). Adding extra flags breaks the symmetry.
> Interesting, the 1-to-1 symmetry I've been look for is between the internal methods and the proxy traps. To me, that is the MOP and things like Object.getOwnPropertyDescriptor are a connivence layer a level above the MOP. From that perspective, it feels fine to me to have a MOP operation (a trap or internal method), called say, CheckOwnProperty that has a flag argument that controls whether you want to get a property descriptor or a boolean result. That MOP operation can be used at the language level to implement the in operator and other language level-semantics and at the library level to implement convenience functions such as O.p.hasOwnProperty and Object.getOwnPropertyDescriptor. The MOP programmer (whether implementing ordinary objects in the implementation, host objects using low level extension points or using Proxy to implement exotic objects)r sits in the middle between those two layers. All they really need to know about is the MOP.
> I think I understand your point of view. I'll try to restate it differently:
> You are arguing for 2 levels of abstraction ("layers"):
> - Most JS developers are "base-level" programmers: they use ES5 APIs like Object.getOwnPropertyDescriptor to reflect on their objects.
> - A small fraction of JS developers will be "meta-level" programmers: these include at least proxy authors and host object authors. These programmers would use a *different* API, i.e. the MOP, which consists of methods such as a "checkOwnProperty" trap and presumably a corresponding Reflect.checkOwnProperty method. There would be a correspondence between the base-level API and the internal MOP methods, but this correspondence is not a straightforward 1-to-1 correspondence.
> Now, there are two routes to optimize for, with opposite pay-offs:
> 1) Make the MOP API correspond as much as possible to the base-level ES5 API. This benefits ES5 programmers, who can easily pick up the meta-level API as it's recognizable, at the expense of ES6 meta-level programmers, who must deal with a larger API surface (incl. the burden to keep fundamental and derived traps in sync).
> 2) Make the MOP API as minimal as possible. This benefits ES6 meta-programmers, but at the expense of increasing the learning curve for ES5 programmers to become ES6 meta-level programmers (because the difference between base-level and meta-level API will necessarily be bigger)
> Approach 1) corresponds roughly to the current setup with fundamental + derived traps.
> Approach 2) corresponds roughly to "provide only fundamental traps".
Yes, as far as you've gone here. But the MOP isn't the whole metaprogramming story. The MOP only defines the essential behaviors of objects. It really doesn't say anything about the semantics of language constructs including how they make use of the MOP.
> Now consider an ES6 meta-programmer interested in intercepting the "in"-operator.
This is an attempt to extend the language at the operator level, not an appropriate job for the MOP (ie, a Proxy). If we want to support operator overloading/extension that should be via a different mechanism.
> Given approach 1) she can look at the full list of traps (e.g. the list at the top of <http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies>) and quickly figure out that she must implement the "has" trap.
But this might have effects far beyond just the in operator and it still might not work. Unless it is explicitly specified (in an accessible place) how does she know that "in" uses "has" instead of "hasOwn" or "getOwnPropertyDescriptor" pus an explicit proto climb? Also what other language features depend upon "has"? You can't just implement "has" for the sole effect of intercept the "in"-operator.
> Given approach 2), our meta-programmer must figure out that the "in"-operator in fact calls the "getOwnPropertyDescriptor" trap instead, and tests for its result to be undefined or not.
> In effect, approach 2) requires that meta-programmers understand the default implementation of the derived traps (i.e. this code: <http://wiki.ecmascript.org/doku.php?id=harmony:virtual_object_api#non-normative_implementation>) before being able to intercept a derived operation.
In either case, successfully accomplishing the goal of intercepting "in" (and nothing else) for a particular object requires understanding of how the semantics of "in" (and potentially every other language feature) map to the Proxy MOP.
The MOP need to be sufficient for defining the essential semantics of an ECMAScript object. But a MOP programmer (an exotic object designer) can't anticipate all ways in which those semantics are going to be exercised. All the ways in which the MOP strings of an object can be pulled is open-ended. This is particularly true given Reflect.* functions that essentially provide direct ES level access to making individual MOP calls on an arbitrary object.
The MOP/Proxies are about extend the kinds of available objects, not about extending the semantics of the ES language. Exotic object implementors need to focus on fulfilling the full object semantics contract defined by the MOP. If they do that, the language should take care of itself.
> To be fair, if our meta-programmer in approach 1) implements the "has" trap, and that trap is not a simple before/after/around method that forwards to the target, then she must also override "getOwnPropertyDescriptor" to return consistent results. If she subclassed Handler (and we make the fundamental traps abstract methods again), she'll get an exception prompting her to investigate further. If she did not subclass Handler, she just shot herself in the foot. So eventually, she will have to learn about distinction between fundamental and derived methods anyway.
I think I've already said, that Handler subclassing looks like a good approach.
> The difference between approach 1) and 2) is at what point you must learn about the differences.
> Approach 1) favors ES5 programmers. Approach 2) favors ES6 meta-programmers. I think we should optimize for ES5 programmers.
I think that if you are defining a Proxy you are a ES6 meta-prorammer. No way around it. That's why it is important to make the task of defining Proxy handlers as foot-gun free as possible.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the es-discuss