Notification proxies (Was: possible excessive proxy invariants for Object.keys/etc??)

David Bruant bruant.d at gmail.com
Mon Nov 26 02:49:27 PST 2012


Le 25/11/2012 12:44, Tom Van Cutsem a écrit :
> (...)
> I agree that the big benefit of notification proxies is that they get 
> rid of all the complex validation logic.
>
> However, some reservations:
>
> (...)
>
> - I think we do lose some expressiveness in the case of pure virtual 
> object abstractions that don't pretend to uphold any invariants.
I don't think this is true. As Mark said:
"The trick is that placing a configurable property on the target doesn't 
commit the handler to anything, since the handler can remove or change 
this property freely as of the next trap."
This is true for any trap I think.
I however agree that this 2-trap consistency may make the exercise of 
writing traps harder (but it'd take user testing to be 100% sure it's 
significantly harder).

I think there might be a loss in expressiveness if forwarding to the 
target throws an error. With current proxies, the trap can catch this 
exception; with notification proxies, the error is thrown after the 
trap, so it can't.
Now, it would require an in-depth analysis, but I intuit that all cases 
where an internal operation throws have a corresponding invariant checks 
(which errors aren't caught by current proxies). If that was the case, 
it would mean that there is actually no loss (or a minor loss consisting 
in loosing the ability to catch an error and rethrow it yourself)

Also, if you can only forward to target, there is no way you can define 
custom property descriptor attributes which would be a shame in my opinion.

> With notification proxies, the handler must always (even in the case 
> of configurable properties) define concrete properties on the target. 
> Any virtual object abstraction is thus forced to maintain a "shadow" 
> which must eventually be represented as a plain Javascript object.
I think that any virtual object abstraction which claims to maintain 
some sort of consistency has to store the necessary bits of this 
consistency somewhere. The target is as good as anywhere else.
I have to note that if the target is forced to be used as a regular 
object, then things like ProxyMap [1] become pointless (because the map 
is just used as a regular object, not as a map).

> (...)
> However, with notification proxies, if the properties were "virtual", 
> those properties do linger as "concrete" properties on the target. 
> Yes, the handler can delete them later, but when is later? Should the 
> handler schedule clean-up actions using setTimeout(0)? This somehow 
> does not feel right.
According to Mark's comment, "later" is "next trap". It's possible to 
maintain consistency, but writing the traps is a bit less easy.

> I like the simplicity of notification proxies, but we should think 
> carefully what operations we turn into notifications only.
>
> More generally, notification proxies are indeed 
> "even-more-direct-proxies". They make the "wrapping" use case 
> (logging, profiling, contract checking, etc.) simpler, at the expense 
> of "virtual objects" (remote objects, test mock-ups), which are forced 
> to always "concretize" the virtual object's properties on a real 
> Javascript object.
I have what I think to be a interesting middleground allowing to bypass 
invariant checks while keeping direct proxies as they are.
My experience with writing traps with direct proxies is that traps (for 
non-virtual cases) almost all and always look like [2]:

     trap: function(...args){
         // do something

         return Reflect.trap(...args); // or equivalent built-in/syntax
     }

Let's take the Object.getOwnPropertyDescriptor trap as example. With 
current proxies, the trap would often end with 
Reflect.getOwnPropertyDescriptor(target, name) (or "Object.", whatev's), 
after the call on the target is finished, then, the engine checks the 
result against the target... but that's a bit dumb.
We know we wanted to get the target result and we made the most 
straightforward thing to do that guarantees invariants, but the engine 
still has to check, because it can't know the result comes from calling 
the Reflect operation on the target.
We could define a symbolic value (like StopIteration for iterators) that 
would mean "forward to target". By essence of what forwarding to the 
target means, there would be no need to perform the least invariant 
check. We can call it ForwardToTarget :-)
Then traps involved in proxies preserving some consistency would look like:

     trap: function(...args){
         // do something

         return ForwardToTarget;
     }

The rule would become: "if you want to do something simple, 
ForwardToTarget at the end of your trap and the invariant check cost is 
gone. If you want to use direct proxies freedom (virtual objects), you 
have to pay the cost of invariant checks to make sure you don't go too 
wild with your freedom"

Best of both worlds for the cost of a symbol.

As a followup to my above comment about custom property descriptor 
attributes, it would be nice to be able to return something like 
ForwardToTargetAndCombineWith({custom1: val1, custom2:val2}) for the 
getOwnPropertyDescriptor trap. More thoughts is necessary for this case.

David

[1] https://gist.github.com/3918227
[2] 
https://github.com/DavidBruant/HarmonyProxyLab/blob/EventedObjectsOnDirectProxies/EventedObject/EventedObject.js#L144


More information about the es-discuss mailing list