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.soshni...@gmail.com> 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 -- 
> http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics#semantics_of_proxies):
> 
> [[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? 
(http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane)


> 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.


> 
> Thoughts?

Breaking double-lifting is a deal breaker.


> 
> 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.


> 
> 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 vote "no", but I do have some sympathy for some of the goals. 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, (proxy.name(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.
> 
> 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.

/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 foo.bar != "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 
>> `foo.bar` 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.soshni...@gmail.com>
>>> 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 foo.bar;
>>>> foo.bar; // 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 foo.bar;
>>>> foo.bar(); // 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 foo.bar != "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:
>>> 
>>> foo.bar() // which desugars to the mentioned above code
>>> 
>>> This is the main problem. If there where used other syntax for this sugar, 
>>> e.g.:
>>> 
>>> foo.bar?(1, 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 foo.bar != "function") {
>>>   foo.bar = 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)?
>>>> 
>>>> Object.prototype.foo = 10;
>>>> 
>>>> "foo" in o // true, OK
>>>> o.foo; // 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@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
> 
> 
> 
> 
> -- 
>     Cheers,
>     --MarkM
> _______________________________________________
> es-discuss mailing list
> es-discuss@mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to