On Sep 21, 2013, at 2:51 AM, Tom Van Cutsem wrote:
> 2013/9/20 Allen Wirfs-Brock <[email protected]>
> 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
obj.m()
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
obj.valueOf()
is equivalent to
obj.valueOf.call()
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
original_push.call.(unknownObject,...args).
>
> 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.
Allen
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss