Proxies: get+fn vs. invoke

Dmitry A. Soshnikov dmitry.soshnikov at
Wed Oct 20 12:10:50 PDT 2010

  (I answer both -- Brendan's and Mark's letters for convenience in here).

On 20.10.2010 21:06, Brendan Eich wrote:
> On Oct 20, 2010, at 9:16 AM, Mark S. Miller wrote:
>> On Wed, Oct 20, 2010 at 7:10 AM, Dmitry A. Soshnikov 
>> <dmitry.soshnikov at <mailto:dmitry.soshnikov at>> wrote:
>>     OK, I'd like nevertheless to continue the discussion with
>>     possible decisions.
>>     Here is a description of how [[Get]] method can work of a
>>     trapping proxy object (I took the basis from the proxy semantics
>>     page --
>>     [[Get]] (P)
>>     When the [[Get]] internal method of a trapping proxy O is called
>>     with property name P, the following steps are taken:
>>        1. Let handler be the value of the [[Handler]] internal
>>     property of O.
>>        2. Let get be the result of calling the [[Get]] internal
>>     method of handler with argument “get”.
>>        3. If get is undefined, return the result of performing the
>>     default behavior, by consulting handler’s “getPropertyDescriptor”
>>     trap.
>>        4. If IsCallable(get) is false, throw a TypeError exception.
>>        5. Let getResult be the result of calling the [[Call]]
>>     internal method of get providing handler as the this value, O as
>>     the first argument and P as the second argument.
>>        6. if getResult is undefined and [[HasProperty]](P)
> [[HasProperty]] takes O as its receiver (this) parameter, so doesn't 
> this break double-lifting? 
> (

Hm.. Actually, I don't completely understand what is "double lifting" (a 
proxied proxy?) as well as the whole membrane example (to many 
wrap-wrap-wrap and a every time a creating of a new function), but I 
think I should carefully read Mark's "markm-thesis.pdf" paper. Though, 
your example from slides is simpler than on the strawman page (but there 
too -- wrap calls wrapFunction, which in turn calls wrap again -- not so 
easy to get from the first glance, but I'll look closer).

However, if someone can clarify it (the main and basic idea in "two 
words" and regarding "double lifting" too) I'll be very thankful, 'cause 
it's interesting.

Anyway, is there an alternative how [[HasProperty]] can work correctly? 
I just though, that it's called by the "has" hook of a handler, which in 
turn can "lie" of course (i.e. return true when a property does not 
exist -- in such a case, noSuchMethod won't be called). For instance:

var object = {x: 10, bar: function () {}};

handler.get = function (r, name) {
   if (name == "foo") {
     return function () {};
   return object[name];

handler.has = function (name) {
   if (name == "baz") {
     return true;
   return name in object;

handler.noSuchMethod = function (name, args) {
   return delegate[name].apply(object, args);

Thus we have:

object.x; // get x, 10; // get bar, normal own real function call; // get foo, normal ad-hoc (returned by get) real function call
object.test(); // get test -> has test -> noSuchMethod test
object.baz(); // get test -> has baz "lied" returned true, noSuchMethod 
isn't called -> TypeError

Possibly here there are some subtle cases which I don't see yet.

>>     is false and [[Get]] is activated with production CallExpression
>>     : MemberExpression Arguments then
>>            6.a Let noSuchMethod be the result of calling the [[Get]]
>>     internal method of handler with argument “noSuchMethod”.
>>            6.b If IsCallable(noSuchMethod) is true then
>>                6.b.I Let argList be the result of evaluating
>>     Arguments, producing an internal list of argument values.
>>                6.b.II Return the result of calling the [[Call]]
>>     internal method of noSuchMethod providing handler as the this
>>     value, O as the first argument and P as the second argument,
>>     argList as the third argument
>>        7. Return getResult.
> Just because the handler's get trap returns undefined does not mean a 
> noSuchMethod trap should be tried. A get trap returning undefined is 
> the way to satisfy a get on a proxied property whose value happens to 
> be undefined.
Oh, I had to wrote [[HasProperty]] on the first place, my fault that it 
turned out confusing. However, first the result of get is considered 
anyway. It may return normally undefined. But at the same time a 
property may or may not exist. Thus, of course [[HasProperty]] of a 
proxy can "lie". What I meant is (the case when noSuchMethod is called):

if (getResult == undefined) *&&* ([[HasProperty]] (P) is *false*)* &&* 
(get is activated via: /CallExpression : MemberExpression Arguments/ ) {
   call noSuchMethod

Possibly there is a better scheme, don't see it yet. Maybe someone else 
will propose.

>>     Thoughts?
> Breaking double-lifting is a deal breaker.

Unfortunately, as I mentioned, I'm not completely understand what it is. 
Clarification will be appreciated.

>>     P.S.: And I want to ask to vote everyone on this list about
>>     whether this additional hook for proxy handlers is needed. My
>>     voice is first -- "yes".
> This list is not set up for voting. It's not productive to try. Let's 
> stick with technical issues such as breaking double-lifting.

OK. I completely agree, and this try wasn't just an appeal "let's vote 
without technical discussion". However, I agree, let's avoid such an 

>>     P.S.[2] In case of "no", we should back to all broken invariants
>>     and find the solutions.
> Dmitry: I'm not sure what you mean here.

I mean the issues I described before in this thread. They may be found 
in complete view here: 
(the case with an external lib for which I write a patch); 
(the previous one detailed lengthy description)

But if to summarize shortly then:

1. A proxy always returns a function (there is no ability to distinguish 
a non-existing properties); -- this is the main issue, which described 
in the first link above;
2. There is no ability to handler both: just a reading of a property vs. 
reading with a calling -- a.length vs. a.length() and to handle them 
3. To many additional code to maintain cache and invalidate it - for 
just to have a noSuchProperty.

Thus, the main, repeat, first two. And the major is - the first one, 
when foo.blaBla is always a function which breaks the checks if 
(!foo.forEach) { .. an own implementation }

>> I vote "no", but I do have some sympathy for some of the goals.

Thanks. I accept the voice and appreciate it.

>> The only proposal along these lines I've seen that I like[*] is to 
>> provide an additional flag parameter to the get trap. When false, or 
>> if the get trap ignores the flag parameter, everything operates as in 
>> the current Proxies proposal. When a method call is performed on a 
>> proxy, ( <>(args) or proxy[expr](args)), 
>> then the get trap is invoked with the flag set to true. Given that a 
>> handler is only accessible from proxies, when a handler's get trap is 
>> invoked with the flag set to true, the following invariants are 
>> guaranteed:
>> * The value returned by the get trap will be [[Call]]ed with its this 
>> binding will be identical to the rcvr parameter of the get trap.
>> * The value returned by the get trap will only be [[Call]]ed, and 
>> will not otherwise escape.

Yes, it's possible to have such a flag. The only difference from 
noSuchMethod, that in the case of a flag -- we have a sort of *invoke* 
-- i.e. hook for catching *all* invocations (meanwhile, noSuchMethod -- 
only missing). Also, it will complicate the *get* hook with /if-else/ 

OTOH, we still have a funargs/apply invariant with such a scheme. So, it 
of course (if not a noSuchMethod) can be considered. Anyway, an ability 
to differentiate in a catcher cases a.length and a.length() is /very 
convenient and needed/. The implementation (whether get+flag (== invoke) 
or just a noSuchMethod) it's a derived question.

>> IIRC, this proposal died on overhead it would impose on non-proxy 
>> calls on JSC. As the JSC implementation evolves, perhaps this 
>> constraint may ease. Let's keep our eyes open. But if not, I still 
>> vote "no".
> I bet V8 peeps would object as well, but let's hear from them directly 
> if possible.
>> [*] I forget from who. If someone knows, please post. Thanks.
> IIRC the third parameter to the get trap, telling whether get trapped 
> from a callee context, was from Dmitry.

I just support it (in case if noSuchMethod will be revoked) as a "at 
least" variant. First it was from Faisal Vali 
However, I think that having combined get + flag will make get handling 


> /be
>>     P.S.[3]: I understand that the committee discussed it before (as
>>     Brendan mentioned). But as we saw, _the things have changed_ and
>>     we have many holes in the only scheme "get+fn". So in addition to
>>     this scheme it will be good to JS has noSuchMethod hook for
>>     proxies. Thus, if the "get" will return a function, the complete
>>     scheme "get+fn" is still working (and we get right the step 7
>>     after 6), i.e. we lose nothing, but gain in addition a convenient
>>     hook which avoids broken invariants. And since the things have
>>     changed, I think the decision (even it was already discussed
>>     some-when before) should be reconsidered too.
>>     Thanks,
>>     Dmitry.
>>     On 18.10.2010 12:25, Dmitry A. Soshnikov wrote:
>>>     On 18.10.2010 8:30, Tom Van Cutsem wrote:
>>>>     I understand you are arguing for noSuchMethod in addition to
>>>>     the existing get trap, and I think we all agree that proxies
>>>>     could support both get + noSuchMethod.
>>>     Yes. At least that already all agree is a progress. I glad to
>>>     hear it, since I'm also interested in the JS design and want to
>>>     have it convenient and elegant in it's abilities (how is that
>>>     was?: "Languages are tools, they should evolve to serve their
>>>     users", for what I add -- but not to bother their users with
>>>     undetermined results in respect of broken invariants).
>>>>     The point that Brendan and I seem to have converged to is that
>>>>     enabling *both* of them allows the creation of (meta-)programs
>>>>     with semantics that we'd rather avoid (the ability to create
>>>>     invoke-only methods).
>>>     Tom, I specially described several times (and in the recent
>>>     letter -- described in detail) the conceptual difference between
>>>     these two approaches. Once again, try to see on it from the
>>>     _notifying_ a hook about the missing method _event_, but not
>>>     working with some mystic "non-existing" (but which actually
>>>     always exists) method.
>>>     I repeat once again:
>>>     function handleNoSuchMethodEven(base, name, args) {
>>>       console.log(name, args);
>>>     }
>>>     if (typeof != "function") {
>>>       handleNoSuchMethodEven(foo, "bar", [1, 2, 3]);
>>>     }
>>>     if (typeof foo.baz != "function") {
>>>       handleNoSuchMethodEven(foo, "baz", [4, 5, 6]);
>>>     }
>>>     And for not to repeat every time this desugared code, this hook
>>>     should be added. But notice, that we deal with just a _check for
>>>     presence_. Still `` and `foo.baz` as were before
>>>     `undefined` properties (i.e. *really* non-existing "methods"),
>>>     so far they stay the same -- correctly `undefined` after the
>>>     check applied and the handler is called. Thus, all invariants
>>>     are working.
>>>     Also, let me notice, still, I'm not asking to add the hook. I
>>>     propose to add the hook with providing sound arguments. And
>>>     still, since I'm interested in the JS design and want to see it
>>>     logical and with some convenient powerful tools which will
>>>     "serve their users", I logically insist on adding this hook.
>>>     Then we'll have both schemes.
>>>     Besides the committee which I'll ask to vote (considering the
>>>     _current state_ of proxies i.e. considering all the issues I
>>>     found during was playing with proxies), I can bring a huge
>>>     "army" of JS programmers (the users for which JS should server!)
>>>     behind me to vote too.
>>>     So please understand me correctly -- I here with only positive
>>>     and constructive thoughts which forward to the improvement of
>>>     JS. And since it's just the beginning of ES6, it's very good
>>>     that we found these issues _now_ (and can take actions), but not
>>>     _after_ the spec will be publish.
>>>     Once again, I am very glad that you and Brendan are are
>>>     discussing it professionally and objectively since now we see
>>>     the issues. And I hope on the same cooperation with accepting
>>>     the decision.
>>>     Dmitry.
>>>>     Cheers,
>>>>     Tom
>>>>     2010/10/15 Dmitry A. Soshnikov <dmitry.soshnikov at
>>>>     <mailto:dmitry.soshnikov at>>
>>>>         On 14.10.2010 22:57, Tom Van Cutsem wrote:
>>>>>             ... All do work. I.e. any missing property, for you,
>>>>>             is a method. Do whatever you want with it. Call e.g.
>>>>>             your noSuchMethod function inside it.
>>>>>             - Hm, but how can I test whether a some method (or a
>>>>>             property) exists on my object?
>>>>>             Obviously, the approach:
>>>>>             if (!o.n) {
>>>>>               o.n = function () {};
>>>>>             }
>>>>>             or even so:
>>>>>             if (typeof o.n != "function") {
>>>>>               o.n = function () {};
>>>>>             }
>>>>>             won't work. Why should I get always a "function" for
>>>>>             every reading of a (non-existing) property?
>>>>>         Ok, I finally see what issue you are addressing. I will
>>>>>         try to summarize (for you to see if I get it right)
>>>>>         - o is a proxy that proxies for another object o2, but in
>>>>>         addition, it wants to treat missing methods on o2
>>>>>         specially (e.g. return a no-op function to prevent errors
>>>>>         or return a method of some other object)
>>>>>         - its get method would look something like:
>>>>>         get: function(r, name) {
>>>>>           var prop = target[name];
>>>>>           if (prop) return prop;
>>>>>           // else deal with the missing method, probably by
>>>>>         returning a function
>>>>>         }
>>>>>         - your feature-test using !o.n would fail because o.n
>>>>>         returns a function, so the then-branch of the if-statement
>>>>>         will not trigger.
>>>>         Yes.
>>>>>         - what you would like to do is to return 'undefined' from
>>>>>         the 'get' trap if the missing property is only accessed,
>>>>>         and return a function only when the property is invoked.
>>>>>         First: good point. AFAICT, this can't be done using the
>>>>>         current proxy API, and adding a flag to `get` or another
>>>>>         trap would make this possible.
>>>>>         It is, however, debatable whether it is appropriate to
>>>>>         override `o.n` with the external function just because it
>>>>>         does not exist on o2. After all, the proxy can handle
>>>>>         missing methods. Presumably, the code in the else-branch
>>>>>         is going to make use of `o.n` (either as a funarg, or it
>>>>>         may call it as a method `o.n(...)`. This will not crash,
>>>>>         the proxy will deal with it. It's not clear that
>>>>>         overriding the `n` method with the function of the
>>>>>         then-branch is the right thing to do. Normally such
>>>>>         feature-testing is done to make sure that later calls to
>>>>>         `o.n(...)` won't crash. When using proxies that deal with
>>>>>         missing methods, calling `o.n(...)` won't crash the code,
>>>>>         so why should the method be replaced?
>>>>         That's the main thing and the issue -- you say: "When using
>>>>         proxies that deal with missing methods". However, you miss
>>>>         the very major word -- "only": "When using proxies that
>>>>         deal *only* with missing methods" and then addition:
>>>>         "...because we can't have at the same time dealing with
>>>>         missing properties and missing methods" (thus, "missing
>>>>         methods" means "missing properties with a call expressions
>>>>         at call-sites").
>>>>         The use case is:
>>>>         1. One lib provides some object `foo`;
>>>>         2. In terms and principles of an abstraction I _shouldn't_
>>>>         care _how_ this `foo` is implemented and which internal
>>>>         structure it has (i.e. whether it's a proxy (with possible
>>>>         implementation of noSuchMethod) or not -- _does not matter_
>>>>         for me as a user of the lib);
>>>>         3. I invent a good patch for the lib and in particular for
>>>>         the object `foo`. I inform about it an author of the lib
>>>>         (or possibly don't inform, he'll new it later himself, when
>>>>         the patch will be de-facto standard -- yeah, hello,
>>>>         `Function.prototype.bind`). However, the author will
>>>>         implement it _not soon_ (the simplest example -- patches
>>>>         for array extras are existed for years in any framework,
>>>>         but only now authors of the engines provide them, in order
>>>>         to conform ES5 spec).
>>>>         4. I don't wanna wait 5 years, I write my own patch. Using
>>>>         the best practice patterns, I provide a check for the
>>>>         native implementation and do not create my own if the
>>>>         native already implemented (actually, the casual and
>>>>         everyday situation -- in any framework):
>>>>         if (!foo.forEach) {
>>>>           // OK, let's go
>>>>         }
>>>>         Result: (we don't go to `then` branch): damn, seems I
>>>>         underestimated the author and he implemented it not after 5
>>>>         years, but already now. Let's use the native then... Hey,
>>>>         hold on! It's not `forEach` I expect, it's some strange
>>>>         anonymous function instead. WTH? Where it comes from. Hey,
>>>>         wait the second!:
>>>>         foo.blaBlaBla
>>>>         foo.WTF
>>>>         foo.heyIDontUnderstandWhatsGoingOn
>>>>         All of them are _some strange functions_? What happened
>>>>         here? Every "non-existing" property _does_ exist! Don't
>>>>         know how to program then... the logic is corrupted.
>>>>         So obviously, distinction of existing and non-existing
>>>>         property is needed. And invariant "forEach" in foo == false
>>>>         && foo.forEach === undefined should be _the invariant_ (i.e
>>>>         shouldn't be broken).
>>>>         (I understand, that working with proxies -- we can break
>>>>         any rules, as e.g. implementation specific host object.
>>>>         I.e. we can "lie" in `in` operator or in `hasOwnProperty`
>>>>         check, but at the same time return some stuff from the
>>>>         `get`. However, the situation takes place and we should
>>>>         think how to solve it. If we admit that proxies have
>>>>         complete right to break every logic like host objects -- it
>>>>         may be leaved for the conscience of a proxy developer).
>>>>>             - Another minor thing -- `delete` does not really delete.
>>>>>             delete;
>>>>>   ; // function
>>>>>         Well, it depends on how you implement the proxy. It could
>>>>>         keep track of deleted property names (I agree this would
>>>>>         be cumbersome).
>>>>         Yeah, count, how many already additional code (including
>>>>         caching/invalidating the cache) this implementation is
>>>>         required. Again, from the abstraction viewpoint -- if all
>>>>         this will be correctly encapsulated from a user -- then
>>>>         there is no difference how it is implemented. If the end
>>>>         result is the same by semantics, from the semantics
>>>>         viewpoint all implementations are equal.
>>>>>         But would a separate `noSuchMethod` trap really help here?
>>>>>         Consider:
>>>>>         delete;
>>>>>; // I expect this to crash now, but it will
>>>>>         still call `noSuchMethod`
>>>>         I specially mentioned, there the case with `delete` is not
>>>>         essential (the words: "What are you trying to delete?
>>>>         Non-existing property? It doesn't exist from the
>>>>         beginning"). However, in case of using noSuchMethod, this
>>>>         invariant doesn't broken, since either with applying
>>>>         `delete` or without it -- correctly `undefined` is returned
>>>>         at reading. In case of `get+fn` always a function is
>>>>         return. So this minor step I think can be reduced to the
>>>>         first issue described above -- a reading a "non-existing"
>>>>         property, which actually _does_ always exist and is a function.
>>>>         Regarding your expectation, no, there should be no any
>>>>         crash, because "bar" _did not exist before, and it does not
>>>>         exist now_.
>>>>         Actually, I see the issue of why there is a discussion of
>>>>         "just invoking phantoms" vs. "real funargs". It's because
>>>>         of the _same syntax_ for _calling a real function_ and
>>>>         _informing the hook_ that there is no such method on an
>>>>         object. I said several times before, and repeat it now
>>>>         again: there is a conceptual difference between these two
>>>>         approaches.
>>>>         1. With handling the situation using noSuchMethod hook we
>>>>         deal exactly with the _situation_, with the _event_ that
>>>>         something goes wrong. And we have a special hook for that.
>>>>         We may don't wanna deal with a (some?) function. In this
>>>>         case, we just _notify_ our handler about this _fact_ (about
>>>>         this _even_, a _situation_). And exactly in this case there
>>>>         is contradiction because for notifying our handler, we use
>>>>         a _call expression_, which also is used to call _real
>>>>         function_. In fact, this case is equivalent to the check:
>>>>         if (typeof != "function") {
>>>>           // the code of noSuchMethod passing "bar" and args
>>>>         }
>>>>         And just for not repeating this each time, it can be
>>>>         encapsulated for a some sugar, e.g. with using call
>>>>         expression for it:
>>>> // which desugars to the mentioned above code
>>>>         This is the main problem. If there where used other syntax
>>>>         for this sugar, e.g.:
>>>>, 2, 3):defaultMethod("bar", [1, 2, 3])
>>>>         then there were no this philosophical dilemma with
>>>>         funargs/apply.
>>>>         2. The approach with supporting funargs/apply assumes that
>>>>         we deal not with _nonflying_ the handler about the _missing
>>>>         method even_, but with some newly created function. But
>>>>         repeat, possibly a user didn't mean at all the work with a
>>>>         function. I.e. this scheme assumes the same desugarred
>>>>         code, but with previous creation of a function:
>>>>         if (typeof != "function") {
>>>>  = function () {
>>>>             return noSuchMethod.apply(...);
>>>>           };
>>>>         }
>>>>         (Notice, this typeof check is assumed on the lower
>>>>         implementation level; i.e. at higher level of abstraction
>>>>         which uses a user typeof already always return `false` for
>>>>         the check since the method is already created by the proxy,
>>>>         i.e. by the lower abstraction level).
>>>>         Theoretically, both approaches are acceptable. It'd be
>>>>         great though, to have invariants with funargs/apply. But.
>>>>         Only if reading of non-existing properties are fixed, i.e.
>>>>         does not return always a function for every non-existing
>>>>         property. However, this is a _vicious circle_. On one hand
>>>>         -- we have always a function at reading non-existing
>>>>         property (that seems broken behavior). On the other hand --
>>>>         it's required to do the first case to have funargs!
>>>>         That's it.
>>>>>             - OK, and what about the prototype chain? Where should
>>>>>             I put this proxy object in order to prevent of
>>>>>             catching of all my missing properties (because I want
>>>>>             to catch them from other objects in the prototype
>>>>>             chain, to which these properties belong)?
>>>>>    = 10;
>>>>>             "foo" in o // true, OK
>>>>>   ; // but it's a _function_, not 10
>>>>>         If o is a proxy that first queries another target object
>>>>>         (like the noopHandler does), it will find 'foo' and it
>>>>>         will return 10.
>>>>         Yeah, this case seems OK. Just a correct `in` check is
>>>>         required before returning a functions.
>>>>>             What about to have `noSuchMethod` _additionally_ to
>>>>>             the `get`? It will catch only missing properties, but:
>>>>>             not _just_ missing properties, but missing properties
>>>>>             which use a call expressions at call-sites. Thus, we
>>>>>             can combine two approaches allowing a user to choose
>>>>>             how to handle the case of missing _method_.
>>>>>             handler.get = function (r, name) {
>>>>>               if (name == "baz") {
>>>>>                 return function () { ... }; // and cache "baz"
>>>>>             name if you wish
>>>>>               }
>>>>>               // other cases
>>>>>               return object[name];
>>>>>             };
>>>>>             handler.noSuchMethod = function (name, args) {
>>>>>               return this.delegate[name].apply(this, args);
>>>>>             };
>>>>>         Could you specify when noSuchMethod is called? I think the
>>>>>         algorithm inside the proxy's [[Get]] method would look
>>>>>         something like:
>>>>>         If the "get" trap on the handler returns undefined AND the
>>>>>         handler defines a "noSuchMethod" trap AND the [[Get]] was
>>>>>         triggered by a call expression, then instead of returning
>>>>>         undefined, return the result of calling the "noSuchMethod"
>>>>>         trap.
>>>>>         Correct?
>>>>         Yes, absolutely correct. And having such a scheme, I don't
>>>>         see what do we lose? Obviously -- nothing. But just gain.
>>>>         I.e. the scheme with "get+fn" is _still here_ (I repeat it
>>>>         again and already repeated several times -- nobody ask do
>>>>         not use it! Please, use) -- please, use it with all
>>>>         mentioned pros and cons. However, in _addition_, a
>>>>         noSuchMethod hook for a _proxy handler_ can be provided.
>>>>         Who don't need it / don't like it -- they won't use it and
>>>>         will use the scheme with "get+fn". Other -- will use it. I
>>>>         don't see any issues here. Is it hard just to add this hook
>>>>         in addition? And then it will be fair to check which of
>>>>         these two approaches will be used more often by users and
>>>>         what do they *really* need in mostly cases.
>>>>         It's the ideal variant seems -- wanna work with partial
>>>>         applications passing funargs? -- No problem! -- Use get+fn
>>>>         scheme! Wanna just simple notification of a missing method
>>>>         event? -- Also no problem! -- We have additionally
>>>>         noSuchMethod hook for a proxy.
>>>>         Dmitry.
>>>>>         Cheers,
>>>>>         Tom
>>     _______________________________________________
>>     es-discuss mailing list
>>     es-discuss at <mailto:es-discuss at>
>> -- 
>>     Cheers,
>>     --MarkM
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at <mailto:es-discuss at>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the es-discuss mailing list