[[Invoke]] and implicit method calls
allen at wirfs-brock.com
Sat Sep 21 10:26:29 PDT 2013
On Sep 21, 2013, at 2:51 AM, Tom Van Cutsem wrote:
> 2013/9/20 Allen Wirfs-Brock <allen at wirfs-brock.com>
> BTW, I would want to use [[InvokeFunction]] for both directly obj.method() and in the internals of F.p.call/apply
> As I mentioned to your reply at the time, I believe the latter would break the expectations of existing code.
There is an even stronger expectation among existing code that
is equivalent to
var f=obj.m; f.call(obj)
with the possibility of other computation being inserted between initializing f and calling it.
More concretely, existing code expects that
is equivalent to
but if obj is a transparent forwarding proxy on a map instance (or most other built-ins) the first form will work as expected and the second form will throw unless something like [[InvokeFunction]] is used within F.p.call.
The re-specification of call/apply in terms of [[InvokeFunction]] would be semantically identical to the current specification in all cases where the passed this value is not a Proxy. (or some other exotic object that redefined [[InvokeFuntion]])
Using [[InvokeFunction]] within F.p.call/apply would still retain their existing behavior for all normal situations where the this value is not a Proxy, so there would be no observable difference for existing code that is not designed to deal with proxies. Plus that existing code would continue to observe the obj.m() :: obj.m.call(m) equivalnce even when obj is a Proxy.
> Code that uses F.p.call/apply to apply a function it acquired through manual lookup typically expects it's going to execute the original behavior, and nothing else:
> var original_push = Array.prototype.push;
> original_push.call(unknownObject, ...args); // programmer expects this to either do the specced push() behavior or throw a TypeError (assuming original definition of 'call')
Using this specific example, you are saying the programmer expects the above to throw if unknownObject is a transparently forwarding proxy over an Array instance. I doubt if that is the actual expectation. I think the expectation is that the orginal_push will be invoked as if was the function retrieved via unknownObject.push. In other words, it is not trying to change the normal this binding semantics of obj.push(), it is only trying to force a local bind of obj.push to a specific value (ie, circumvent obj.[[Get]]("push")).
I'm skeptical that there are many (any?) existing situations where F.o.call/apply is used with the failure expectation WRT transparent proxies that is implicit in your example. Do you know of any?
For situations that really want to do a naked [[Call]] without any proxy-based intercession the way to do it would be Reflect.call(original_push,unknownObject,...args) rather than
> JS fundamentally decouples property lookup from method call and thus has the ability to express non-polymorphic function calls. We shouldn't virtualize [[Call]].
Are you suggesting that we should not have a Proxy "apply" trap?
But, more to your point, the existence of the this argument to call/apply means that they are inherently polymorphic calls in the sense you are talking about. If the author of a function doesn't want it to be polymorphic on its this value then they should not write any references to |this|.
> If a proxy wants to intercept method calls, it can return a wrapper function from its "get" trap and override "invoke". I'm pretty sure virtualizing [[Call]] will be a bridge too far.
If this is the solution, then ForwardingHandler should do exactly that within its default "get" trap. However, that is going to also be wrong some of the time. You can't generically say that all [[Get]]'s that return a function value can replace that value with some other function and not expect to break something.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the es-discuss