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. 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).
Cheers, Tom 2010/10/15 Dmitry A. Soshnikov <[email protected]> > 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 [email protected] https://mail.mozilla.org/listinfo/es-discuss

