Proxies: get+fn vs. invoke

# Dmitry A. Soshnikov (14 years ago)

On 10.10.2010 0:44, Dmitry A. Soshnikov wrote:

Yes, there are some cons: a non-existing method can be extracted as a funarg for later use. It also do not break invariant with call/apply methods applied for the method:

Sorry, typo, not cons, but pros of course.

Dmitry.

# Tom Van Cutsem (14 years ago)

This is odd, I didn't see the original message you were replying to.

To respond to the question you posed in the "syntax for efficient traits"-thread:

Yes, I do. The latest is "magic methods" directly on objects (possibly you

saw -- gist.github.com/617338; I don't know what again with es-archive server, but it doesn't display the lettter which I send about "get+fn vs. invoke" -- esdiscuss/2010-October/thread -- there only answer on the letter with correcting the typo).

Hmm, your API defines all the handler methods directly on the object, thus explicitly ignoring the stratification afforded by the original API. Granted, your implementation filters out magic properties in appropriate places, so they won't show up in e.g. for-in loops, but the magic methods are still easily accessible directly from the object. I understand that people just want to write:

myObject.get = function() { ... }

instead of:

var handler = Proxy.noopHandler(myOriginalObject); handler.get = function() { ... } myObject = Proxy.create(handler, Object.getPrototypeOf(myOriginalObject));

I agree that the current API is too bulky if all you want is noSuchMethod, but I'd much rather see a wrapper library that allows me to write something like this:

var [myObject, handler] = wrap(myOriginalObject); handler.get = function() { ... }

or just:

var myObject = wrap(myOriginalObject, { get: function() { ... } })

This still keeps all the meta-functionality of an object in a separate object. Code that only has access to "myObject" can't mess around with its meta-level behavior. 'magic' methods don't prevent a client from inadvertently overriding an object's traps.

Maybe you can answer on this question regarding proxies? How can we handler

in one get of a proxy handler both cases of a call-site:

foo.bar

and

foo.bar()

?

Having invoke, we handler foo.bar(). However, the case when the get returns

should look (unfortunatelly?) like (foo.bar)(). Also of course foo.bar does not extracted as a functional object (including invariants foo.bar.call/apply). But I think, is it the case that user wants to used them exactly as funargs or he wants more to handle just access (exactly calling of) to non-existing methods? P.S.: to avoid off-topic regarding this topic devoted to traits/mixins, it's better to answer to that trhead -- "get+fn vs. invoke".

In short: the 'get' trap can't distinguish the above two cases. This is a pity, and I agree it would be useful for 'get' to have that information sometimes. There has previously been talk on this list of parameterizing 'get' with an additional flag to detect get+call vs invoke, but it turned out that not all implementations would be able to support it: < esdiscuss/2010-May/011062>

In retrospect it may not be so bad that this feature isn't supported. I can imagine it could lead people to write code like:

get: function(receiver, name, args) { // suppose that args === undefined implies that the property was only queried, not invoked if (args === undefined || args.length === 0) { return 'a'; } else { ... } }

So that obj.name and obj.name() would both return 'a'. This could lead to a lot of confusion, since it's hard to quantify what "obj.name" denotes. If it's supposed to be a method, then obj.name should return a function. If it's supposed to be a getter, then obj.name() should call the thing returned by the getter (which, in this case, should raise a type error since strings are not callable)

Cheers, Tom

2010/10/9 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com>

# Dmitry A. Soshnikov (14 years ago)

On 11.10.2010 18:18, Tom Van Cutsem wrote:

This is odd, I didn't see the original message you were replying to.

Yes, really odd. It's not the first time when the archive-server looses letters.

OK, here is original letter sent before (the main question is not in "magic methods" directly on object, it's just a academic curiosity, but the main question is -- how in one /get/ method of the handler to manage missing properties and missing methods?) :

# Tom Van Cutsem (14 years ago)

The most interesting question for me is mentioned several times (the last one was by Brendan in his slides from the lates JS Conf) "get + defining Fn" vs. invoke trap.

Yes, there are some cons: a non-existing method can be extracted as a funarg for later use. It also do not break invariant with call/apply methods applied for the method:

var foo = proxy.nonExistingMethod;

Thus, foo is a real function and also proxy.nonExistingMethod() is the same as proxy.nonExistingMethod.call(undefined).

However, I don't see how can I implement a generic get method in the proxy handler? I.e. I'd like to trap at the same time both: reading simple properties and also invocation of methods. In my script these are: * get* (traps reading of all properties), noSuchProperty (traps reading only of missing properties) and noSuchMethod (traps methods invocations).

Ok, so all of this is inspired by Smalltalk's "doesNotUnderstand:" trap, and derivatives, right?

The thing is: in Smalltalk, there is no property access, only message sending (method invocation). Thus, there can be only missing methods, not missing properties. In Javascript, there is only property access, no true method invocation. Thus, there can be only missing properties, not missing methods.

So I think the simple answer is: you can't have both. In Javascript, noSuchProperty trumps noSuchMethod

It would be analogous to asking for a "doesNotUnderstandProperty" trap in Smalltalk: such a trap would never get triggered, since "obj foo" is a message send in ST, not a property access.

Yes, everything correct with stratification of the meta- and normal- levels; I know that this is the main reason. As I said (and repeat it again) -- my implementation with Ugly?PythonsNames is just an academic curiosity -- to play with proxies.

Sorry, I didn't know what your intentions were. Academic curiosity is good ;-)

And was playing with them, I backed to the ideological dilemma which was mentioned also several times: "get + fn vs. noSuchMethod/invoke". This is what Brendan mentioned before and also in the last slides of the recent JS Conf (I saw only slides, I didn't see yet video of Brendan's talk -- maybe he already answered this question? However, I didn't find the answer in slides).

The main (42? ;)) question is: how having one get method in a proxy's handler to handle both cases of a call-site -- a property reading and a method invocation ?

foo.bar

and

foo.bar()

Currently I understand, that implementation of the noSuchMethod described in the strawman article is just wrong -- because get method of the handler always returns a function. That means, foo.bar - is a function, foo.baz, foo.whatTheHack - is also a function. How a user will differentiate accesses to a non-function properties to a function properties?

As I understand it, even spidermonkey's noSuchMethod does not currently allow one to tell the difference, so I don't think the strawman implementation is "wrong" in this regard. With the current noSuchMethod, non-existent properties will show up as "undefined" values when queried via property access. In contrast, the strawman implementation would return a function. Neither is satisfactory if the user expects a non-function value.

What you are asking for is a hook that returns a different value depending on its context of use (property access vs method call syntax). As it stands, the Proxy API does not support this use case.

And exactly the last case can be trapped with noSuchMethod. I can even implement it in my script (e.g. catching onError with debugger service, or even simple window.onerror -- though will be hard with arguments). But it can be additional trap for a proxy:

noSuchMethod: function (name, args) { console.log(name, args); }

then:

proxy.nonExisting(1, 2, 3); // get "nonExisting" -> call-site contains call expression -> noSuchMethod("nonExisting", [1, 2, 3])

The issues are also known:

proxy.nonExisting(1, 2, 3); is not the same as proxy.nonExisting.apply(null, [1, 2, 3]) and we can't extract proxy.nonExisting as a function ('cause obviously it's not a function). But what is more convenient for a user -- to catch such methods or to keep invariants with apply/funargs? Also, as I mentioned, even if it is done via get, then to keep === invariant, get should return always the same missing function for a property (which means to keep some dispatch table). So, anyway, some invariants will be lost anyway (or will be complex/inconvenient in implementation) and it's needed to choose what is better for a user. I think it will be good to have additionally for proxies noSuchMethod trap.

I think this proposal can be reduced to the previous proposal of parameterizing the 'get' trap with a flag that indicates context of use. Since we did not make progress on that feature, I'm afraid a dedicated 'noSuchMethod' hook will not fundamentally change things.

# Dmitry A. Soshnikov (14 years ago)

On 11.10.2010 20:07, Tom Van Cutsem wrote:

The most interesting question for me is mentioned several times
(the last one was by Brendan in his slides from the lates JS Conf)
"get + defining Fn" vs. invoke trap.


Yes, there are some cons: a non-existing method can be extracted
as a funarg for later use. It also do not break invariant with
call/apply methods applied for the method:

var foo = proxy.nonExistingMethod;

Thus, /foo/ is a real function and also
/proxy.nonExistingMethod()/ is the same as
/proxy.nonExistingMethod.call(undefined)/.

However, I don't see how can I implement a generic /get/ method in
the proxy handler? I.e. I'd like to trap at the same time both:
reading simple properties and also invocation of methods. In my
script these are: /__get__/ (traps reading of all properties),
/__noSuchProperty__/ (traps reading only of missing properties)
and /__noSuchMethod__/ (traps methods invocations).

Ok, so all of this is inspired by Smalltalk's "doesNotUnderstand:" trap, and derivatives, right?

The thing is: in Smalltalk, there is no property access, only message sending (method invocation). Thus, there can be only missing methods, not missing properties.

Yeah, the same as in ruby with its /method_missing/ -- there also public API of an object is a set of accessor methods (and there are only methods) and instance variables are not available from the outside without having an accessor.

In Javascript, there is only property access, no true method invocation. Thus, there can be only missing properties, not missing methods.

Yeah, I know.

So I think the simple answer is: you can't have both. In Javascript, noSuchProperty trumps noSuchMethod

Yes, I know and understand it. Though, was asking about possible solution (with analyzing a call-site context).

It would be analogous to asking for a "doesNotUnderstandProperty" trap in Smalltalk: such a trap would never get triggered, since "obj foo" is a message send in ST, not a property access.

Yes, everything correct with stratification of the meta- and
normal- levels; I know that this is the main reason. As I said
(and repeat it again) -- my implementation with
__Ugly?PythonsNames__ is just an academic curiosity -- to play
with proxies.

Sorry, I didn't know what your intentions were. Academic curiosity is good ;-)

And was playing with them, I backed to the ideological dilemma
which was mentioned also several times: "get + fn vs.
noSuchMethod/invoke". This is what Brendan mentioned before and
also in the last slides of the recent JS Conf (I saw only slides,
I didn't see yet video of Brendan's talk -- maybe he already
answered this question? However, I didn't find the answer in slides).


The main (42? ;)) question is: how having /one get/ method in a
proxy's handler to handle both cases of a call-site -- a /property
reading/ and a /method invocation/ ?


foo.bar

and

foo.bar()

Currently I understand, that implementation of the
__noSuchMethod__ described in the strawman article is just wrong
-- because /get/ method of the handler /always/ returns a
/function/. That means, /foo.bar/ - is a function, /foo.baz/,
/foo.whatTheHack/ - is also a function. How a user will
differentiate accesses to a non-function properties to a function
properties?

As I understand it, even spidermonkey's noSuchMethod does not currently allow one to tell the difference,

No, it does allow. It exactly analyzing the context of a call-site (whether it is a simple property reading or besides the reading the property there is also a call expression). I can simply add /noSuchMethod/ to my implementation and the method will automatically be called (btw, have just added, you may test it). First of course will be called the /get/ method of the handler with property name /noSuchMethod/ and if an object has such a function, it will be called with needed method name and needed arguments.

As I said, I can even implement it (again -- without a big practical need, but just 'cause of academic curiosity) without native SpiderMonkey's /noSuchMethod/ (e.g. naming it /methodMissing/)

# Tom Van Cutsem (14 years ago)

Where this proposal is described? I didn't see it. I'll support it. Even if the committee won't agree on noSuchMethod, it'd at least will be great to have a parametrized get. Though, repeat, IMO, a separated method for this case is better.

I think the thread starts here: < esdiscuss/2010-May/011060>

# Dmitry A. Soshnikov (14 years ago)

On 11.10.2010 22:45, Tom Van Cutsem wrote:

Where this proposal is described? I didn't see it. I'll support
it. Even if the committee won't agree on noSuchMethod, it'd at
least will be great to have a parametrized get. Though, repeat,
IMO, a separated method for this case is better.

I think the thread starts here: esdiscuss/2010-May/011060

i agree with Faisal Vali in that thread (actually, I see that he talked about similar lacks of of proxies get). I remember that thread, though I included later, when was asking Mark "why are exactly proxies but not magics?".

Also I think if some ideological sound design decision will be made, some concrete implementation doesn't matter much (taking into account the fact that the talk is about /new future feature/ -- which JavaScriptCore and in particular Oliver Hunt will implemented (with rewriting the logic of VM machine if needed) -- in order to conform ES specification). I can't believe that JS programmers will hear the answer on the question: "Why does JS has no such feature in the design?" as

# Dmitry A. Soshnikov (14 years ago)

On 11.10.2010 18:18, Tom Van Cutsem wrote:

In short: the 'get' trap can't distinguish the above two cases. This is a pity, and I agree it would be useful for 'get' to have that information sometimes. There has previously been talk on this list of parameterizing 'get' with an additional flag to detect get+call vs invoke, but it turned out that not all implementations would be able to support it: esdiscuss/2010-May/011062

In retrospect it may not be so bad that this feature isn't supported. I can imagine it could lead people to write code like:

get: function(receiver, name, args) { // suppose that args === undefined implies that the property was only queried, not invoked if (args === undefined || args.length === 0) { return 'a'; } else { ... } }

So that obj.name, obj.name and obj.name, obj.name() would both return 'a'. This could lead to a lot of confusion, since it's hard to quantify what "obj.name, obj.name" denotes. If it's supposed to be a method, then obj.name, obj.name should return a function. If it's supposed to be a getter, then obj.name, obj.name() should call the thing returned by the getter (which, in this case, should raise a type error since strings are not callable)

Sorry, this part of your reply was formatted with a small text -- the same as quote of my letter, so I completely lost it and didn't see at all. Just saw it now in es-archive (there is no font size formatting). Actually, you answered in this replay on the question I was asking in the next letters (including a parametrized get).

Regarding this example with undefined args: I think you agree that correct check should test arguments.length to see whether args is here. However, as I mentioned, it may be more convenient to separate in own place, i.e. to the noSuchMethod hook of a proxy handler.

Also I think now, that what was named as pros, i.e. ability to have funargs and call/apply invariants, in real, not so pros. Because users more likely want to catch exactly missing methods (if you don't like the word "methods", since there're no methods, there are properties, let's say -- missing properties which ended with call expression at the call-site). And funargs/apply invariants should be leaved for real functions (existing or ad-hoc, explicitly returned from the get). Moreover, as it has been mentioned, such returning has broken === invariant anyway (and also broken invariant with non-existing properties).

So I still propose to include to proxy handlers either this parametrized get with the third args parameter, or a separated noSuchMethod/methodMissing hook, and do not bother about funargs/apply scheme (because, by logic this scheme is even not so correct; again

# Tom Van Cutsem (14 years ago)

Sorry, this part of your reply was formatted with a small text -- the same as quote of my letter, so I completely lost it and didn't see at all. Just saw it now in es-archive (there is no font size formatting). Actually, you answered in this replay on the question I was asking in the next letters (including a parametrized get).

Apologies for the bad formatting. I blame my gmail editor ;)

Regarding this example with undefined args: I think you agree that correct

check should test arguments.length to see whether args is here. However, as I mentioned, it may be more convenient to separate in own place, i.e. to the noSuchMethod hook of a proxy handler.

Hold on, the fact that a property is accessed either as o.n or o.n(...) has nothing to do with noSuchMethod. Isn't "noSuchMethod" related to whether or not n exists in a proxied object? Anyway, it does not make sense for proxies since a proxy is an empty object and as far as the proxy is concerned, all of the properties that it traps are in a sense "missing".

Also I think now, that what was named as pros, i.e. ability to have funargs

and call/apply invariants, in real, not so pros. Because users more likely want to catch exactly missing methods (if you don't like the word "methods", since there're no methods, there are properties, let's say -- missing properties which ended with call expression at the call-site). And funargs/apply invariants should be leaved for real functions (existing or ad-hoc, explicitly returned from the get). Moreover, as it has been mentioned, such returning has broken === invariant anyway (and also broken invariant with non-existing properties).

I don't fully understand. Why should funargs/apply invariants be maintained for real functions only?

The === invariant is indeed broken, but in the case of an invocation (as in o.n(...)) it shouldn't matter, since the function returned by o.n is immediately applied anyway and is not made visible to the caller.

So I still propose to include to proxy handlers either this parametrized get with the third args parameter, or a separated noSuchMethod/methodMissing hook, and do not bother about funargs/apply scheme (because, by logic this scheme is even not so correct; again -- it should be leaved for real functions of the get). I am sure, JavaScriptCore can manage this case.

I cannot assess the impact of this change on JavaScriptCore so I won't make any statements about that. In any case, a noSuchMethod/methodMissing hook does not make sense on proxies since proxies can be used even when there is no target object from which a method could be missing.

# Dmitry A. Soshnikov (14 years ago)

On 13.10.2010 23:33, Tom Van Cutsem wrote:

Regarding this example with undefined args: I think you agree that
correct check should test `arguments.length` to see whether `args`
is here. However, as I mentioned, it may be more convenient to
separate in own place, i.e. to the `noSuchMethod` hook of a proxy
handler.

Hold on, the fact that a property is accessed either as o.n or o.n(...) has nothing to do with noSuchMethod. Isn't "noSuchMethod" related to whether or not n exists in a proxied object?

Yes, I wonted to mention it too. What is proposed in the view of parametrized get, is invoke -- i.e. catch for all calls. It can be considered also (again, depending on a call-site context), however, the most interest consists in exactly missing property. And not just a missing property, but the missing property with a call expression at a call-site. This is exactly (and correctly, and needed) how noSuchMethod of SpiderMonkey works.

If a property exists, and we have a call expression call-site, noSuchMethod shouldn't be called. Since this is just a normal property, which can be, or can not be a function (in first case we have a function call, in the later -- a TypeError "not a function"). It can be a normal property, or an ad-hoc, specially returned by the get hook (and again, if the later, it can be a function or not). For this normally should work funargs/apply.

Anyway, it does not make sense for proxies since a proxy is an empty object and as far as the proxy is concerned, all of the properties that it traps are in a sense "missing".

The main purpose of a proxy is to proxy an object. It doesn't matter whether it's empty or not. It have special hooks. One of such, additional, hook -- noSuchMethod, would be nice to have.

Also I think now, that what was named as pros, i.e. ability to
have funargs and call/apply invariants, in real, not so pros.
Because users more likely want to catch exactly missing methods
(if you don't like the word "methods", since there're no methods,
there are properties, let's say -- missing properties which ended
with `call expression` at the call-site). And funargs/apply
invariants should be leaved for _real functions_ (existing or
ad-hoc, explicitly returned from the `get`). Moreover, as it has
been mentioned, such returning has broken === invariant anyway
(and also broken invariant with non-existing properties).

I don't fully understand. Why should funargs/apply invariants be maintained for real functions only?

Because, repeat, more likely JS programmers like to have and ability to catch exactly call expressions on non-existing properties. A non-existing method in this ideology shouldn't behave as a normal existing property (for this purpose, repeat, programmers have real existing or ad-hoc get's methods). The presence of noSuchMethod with catching such methods does not assume that the method should somehow be alive. It's just a signal to do something else ("I cannot reply the message, do something!"), e.g. to change dispatching by alternative prototype chain (no matter). It's not required that this non-existing method become existing.

And again, since === invariant is broken, funargs/applys also can be easily broken. It just seems that it's a great innovation to have an ability of funargs/apply, but repeat, most of programmers want first to catch exactly call expressions on missing properties and then maybe (maybe!) such (sic!) broken anyway "invariants" (yeah, they are even not invariants, 'cause they are broken).

The === invariant is indeed broken, but in the case of an invocation (as in o.n(...)) it shouldn't matter, since the function returned by o.n is immediately applied anyway and is not made visible to the caller.

Besides === invariant, sorry for repeating again, the invariant with if (!o.n) {o.n = ...} is also broken. This is absolutely enough to cancel this implementation of noSuchMethod.

So I still propose to include to proxy handlers either this
parametrized  `get` with the third `args` parameter, or a
separated noSuchMethod/methodMissing hook, and do not bother about
funargs/apply scheme (because, by logic this scheme is even not so
correct; again -- it should be leaved for _real_ functions of the
`get`). I am sure, JavaScriptCore can manage this case.

I cannot assess the impact of this change on JavaScriptCore so I won't make any statements about that.

I too. How do you think, would it be good to ask Oliver Hunt this question?

In any case, a noSuchMethod/methodMissing hook does not make sense on proxies since proxies can be used even when there is no target object from which a method could be missing.

Does not make sense in any case?

OK, let me understand your position first. Are you ideologically against this hook? Because analyzing the talk (and previous talks) I see that your position changes on some minor aspects -- first "it's a useful think, but, unfortunately, JavaScriptCore...", then "it does not make sense in any case" -- just as you would against even an idea of noSuchMethod.

I may ask any JS programmer does he need noSuchMethod and in which way? Brendan implemented it before (a native noSuchMethod), Brendan also was agree that this is an interesting idea (when the parametrized get was discussed first). I've heard from many other programmers, "why the heck, SpiderMonkey the only which supports noSuchMethod?" "It would be good to have noSuchProperty also!" - they said.

And now I don't understand, why are you categorically against and say that there is no any sense? Because some steps later you analyzed it from the position that we can either imitate it with get of a proxy, or "JavaScriptCore has some issues with its VM". But unfortunately, (1) we can't imitate it, because this implementation is broken and wrong (despite that it looks brightly working and even with supporting funargs/apply "invariants" -- repeat, it does not support them, because they are also broken because of === is broken). And also the main invariant with non-existing property is broken; (2) I really believe that JavaScriptCore devs can make everything possible and fix the VM as needed.

However, since this is a committee, I think the decision should be made by the committee and with a voting. With analyzing pros and cons. And before that -- with analyzing of what JS programmers are really want.

What is wrong if this hook will be added? You don't like it ideologically? -- No problem, just don't use it (don't defined on a handler), use your approach with get returning each time a new function. Get your broken tests with if (!o.n) {...}. Do not inherit from deeper object, just put your proxy which always returns a new function as a prototype. Get your funargs, apply them. Repeat it twice with saving previous funargs, test them for === on the next step. Nobody can prevent from this actions -- if user will won't, he will do it. But other users will be glad to use noSuchMethod hook on a proxy handler and catch missed properties with a call expression on the call-site. And this fact possibly won't be mean for them that this missed property is some alive function, no they just want to handle the case, the don't need this alive function.

This is really an important question and I ask the committee to consider it. With the voting. It cannot be that the decision is just made, because someone personally just don't like it or there are some issues of an implementation.

Thanks, Dmitry.

# Brendan Eich (14 years ago)

On Oct 13, 2010, at 3:28 PM, Dmitry A. Soshnikov wrote:

I too. How do you think, would it be good to ask Oliver Hunt this question?

bugs.webkit.org/show_bug.cgi?id=18058 is resolved WONTFIX.

I have another reply under way that makes a strong case against noSuchMethod (which I added to SpiderMonkey many years ago). Should be out today.

# Brendan Eich (14 years ago)

On Oct 13, 2010, at 6:56 AM, Dmitry A. Soshnikov wrote:

Also I think now, that what was named as pros, i.e. ability to have funargs and call/apply invariants, in real, not so pros. Because users more likely want to catch exactly missing methods (if you don't like the word "methods", since there're no methods, there are properties, let's say -- missing properties which ended with call expression at the call-site).

That's not our experience with E4X (ECMA-357), which specifies XML methods as invoke-only. They seem to be normal function-valued properties of XML.prototype, but getting one by name on an XML instance in a non-callee expression context instead tries to find an XML child element or elements of the method's name, returned as a list.

Some of this is peculiar to E4X, but the invoke-only nature of the methods, per-spec, is not. And it breaks apply and functional programming, so we extended E4X with the function:: pseudo-namespace to allow one to extract methods from XML instances.

Others using noSuchMethod are happier as you say, because (for example) they are Smalltalkers (Bill Edney is on this list) who pretend there are only method calls (message sends), never properties or first-class functions.

But that happiness is not universal, so your "not so pros" judgment is not true for everyone.

Should we support everyone even if it makes the Proxy system more complicated and allows for not-quite-function methods?

Our decision was "no". You're asking us to revisit to support the some (not all) developers who want to make not-quite-function methods. That's a fair request but I think you need to do more than assert that the resulting complexity is not a problem. Further below, I'll do some legwork for you.

And funargs/apply invariants should be leaved for real functions (existing or ad-hoc, explicitly returned from the get).

Why shouldn't all methods including missing ones be real functions? Why complicate the domain of discourse with real and not-quite-real functions?

Moreover, as it has been mentioned, such returning has broken === invariant anyway (and also broken invariant with non-existing properties).

Proxy implementors can memoize so === works. It is not a ton of code to write, and it gives the expected function-valued-property-is-method semantics. Here is the not-all-that-inconvenient proxy code:

function makeLazyMethodCloner(eager) { var cache = Object.create(null); var handler = { get: function (self, name) { if (!cache[name]) cache[name] = Proxy.createFunction({}, function () { return eager[name].apply(eager, arguments); }); return cache[name]; } }; return Proxy.create(handler, Object.getPrototypeOf(eager)); }

A little test code:

var o = {m1: function () { return "m1"}, m2: function () { return "m2"; }}; var p = makeLazyMethodCloner(o); print(p.m1()); print(p.m2());

Some subtle things here:

  • The missing fundamental traps in the handlers are filled in by the system. This is a recent change to the spec, implemented in SpiderMonkey in Firefox 4 betas.

  • Even p.hasOwnProperty('m1') works, because the get trap fires on 'hasOwnProperty' and clones eager['hasOwnProperty'] using a function proxy, even though that method comes from Object.prototype (eager's Object.prototype). The hasOwnProperty proxy then applies Object.prototype.hasOwnProperty to eager with id 'm1'. No get on 'm1' traps yet -- no function proxy creation just to ask hasOwnProperty.

  • Both p.m1 and p.m1() work as expected. Only one kind of function.

Now consider if you had a third parameter to the 'get' trap to signal callee context vs. non-callee context. You'd still want to proxy the functions, that doesn't get simpler just due to a change of trap parameters. You'd still want to cache for identity. But you would have made invoke-only methods.

Ok, let's give up on functional programming and cached methods. Here' s my version written to use only noSuchMethod, no proxies:

function makeLazyMethodCloner(eager) { return Object.create(Object.getPrototypeOf(eager), { noSuchMethod: { value: function (name, args) { return eager[name].apply(eager, arguments); } } }); }

9 lines instead of 13, but broken functional programming semantics -- you cannot extract p.m1 or p.m2 and apply them later, pass them around, etc.

What good would result from this? Again, our view in TC39 is "not much".

Note that I used a mechanical, consistent coding style ("JSK&R", { on same line as function, newline after {), so the comparison is apples to apples. Is the broken semantics really worth four lines of savings?

So, no fair asserting "practically it's unsoundly complicated and inconvenient". And please stop invoking "ideology" as a one-sided epithet to throw against Tom or TC39. Please do start showing examples, specifically an apples-to-apples comparison with noSuchMethod that is significantly simpler. I don't see it.

# Brendan Eich (14 years ago)

On Oct 13, 2010, at 5:14 PM, Brendan Eich wrote:

Now consider if you had a third parameter to the 'get' trap to signal callee context vs. non-callee context. You'd still want to proxy the functions, that doesn't get simpler just due to a change of trap parameters. You'd still want to cache for identity. But you would have made invoke-only methods.

s/would/could/ in the last sentence -- the point is allowing get to tell when the property is being accessed as the callee in a call expression (or new expression? hmm) does not inevitably inject invoke only methods -- it simply adds the degree of freedom that someone could use to implement them.

Since there's no savings in terms of apples-to-apples method proxying and memoization ("caching") for ===, this degree of freedom seems strictly a misfeature. Even noSuchMethod fans would agree!

Ok, let's give up on functional programming and cached methods. Here' s my version written to use only noSuchMethod, no proxies:

function makeLazyMethodCloner(eager) { return Object.create(Object.getPrototypeOf(eager), { noSuchMethod: { value: function (name, args) { return eager[name].apply(eager, arguments);

s/arguments/args/

I was testing lightly. Again, the code posted here works in a current mozilla-central or tracemonkey js shell, or (with s/print/console.log/g) in Firefox 4 betas.

# Dmitry A. Soshnikov (14 years ago)

On 14.10.2010 4:14, Brendan Eich wrote:

On Oct 13, 2010, at 6:56 AM, Dmitry A. Soshnikov wrote:

Also I think now, that what was named as pros, i.e. ability to have funargs and call/apply invariants, in real, not so pros. Because users more likely want to catch exactly missing methods (if you don't like the word "methods", since there're no methods, there are properties, let's say -- missing properties which ended with call expression at the call-site).

That's not our experience with E4X (ECMA-357), which specifies XML methods as invoke-only. They seem to be normal function-valued properties of XML.prototype, but getting one by name on an XML instance in a non-callee expression context instead tries to find an XML child element or elements of the method's name, returned as a list.

Some of this is peculiar to E4X, but the invoke-only nature of the methods, per-spec, is not. And it breaks apply and functional programming, so we extended E4X with the function:: pseudo-namespace to allow one to extract methods from XML instances.

Yes, I'm aware of it. However, you mention a similar end result (inability to extract a function with a normal (accessor) syntax), but with completely different reason. In case of EX4 you talk about the existing real methods. In case of proxies, we talk about non-existing property (which is activated with a next call expression). The difference is: in first case a user really deals with existing stuff and expect the functions to be extracted (of course in this case ECMA-357 had to do something -- provide :: -- to allow this). In the later one, at the first place, a user wants to catch the call expression.

Yeah, it's a good example, but I see that similarity of the end result is used to apply it to the different reasons (messing the concepts). And in case of the first reason -- yes, it's critical. In case of the second one -- not so or even non-critical. Because, repeat, catching such cases (missing methods) a user may not want to deal with an alive function, since it's just a signal to do to something (to handle the case). Below I provide test sources (as you asked) to complete my position (showing that implementations with 'get+fn' + 'noSuchMethod' can even co-exist -- and everyone will be happy).

Others using noSuchMethod are happier as you say, because (for example) they are Smalltalkers (Bill Edney is on this list) who pretend there are only method calls (message sends), never properties or first-class functions.

Yes, in systems which has second-class functions it's easier to handler this case (there is no need to return a function). However, it's not just because there are first-class function. E.g. Ruby also has them, but having them, it distinguishes call expression syntax in different cases: a method is called with (), a lambda is called with .call method:

global catcher for missed methods

def method_missing(name, args) p "Method: ", name, "Args: ", args end

a method "foo"

which returns a lambda --

a functional first-class object (also, a closure);

the lambda itself just prints 10

def foo lambda { p 10 } end

we call "foo" method (notice, with () syntax),

and then call with different syntax -- via .call, the

returned lambda

foo().call # 10

however this case is

caught with method_missing

nonExisting(2) # "Method: " :nonExisting, "Args: " 2

But this is just -- a "by the way", Ruby is irrelevant with ES and this mailing list (this example is just to mention that the discussed issue is not just because there are first-class functions). I like more though that ES has the same syntax for these cases.

Besides, I understand that ES has similar to Python implementation with "only properties", and moreover, Python also has no no_such_method hook, only its get and getattr (also around the Internet there are some shims of Ruby's method_missing for Python with returning every time a function from the get). But, having similar to Python implementation, JS can go further and better.

But that happiness is not universal, so your "not so pros" judgment is not true for everyone.

I understand, however I'd like to notice that I'm not judging, but objectively analyzing.

Should we support everyone even if it makes the Proxy system more complicated and allows for not-quite-function methods?

Our decision was "no". You're asking us to revisit to support the some (not all) developers who want to make not-quite-function methods. That's a fair request but I think you need to do more than assert that the resulting complexity is not a problem. Further below, I'll do some legwork for you.

OK, I understand this position quite clearly. I'll also show further below that there can be possible compromise with co-existing both approaches.

And funargs/apply invariants should be leaved for real functions (existing or ad-hoc, explicitly returned from the get).

Why shouldn't all methods including missing ones be real functions? Why complicate the domain of discourse with real and not-quite-real functions?

Assuming this, I try to see on the issue from the position that catching a missed method, a user deals with a fact, with just a signal about this situation (that a method is missing), but not with a method itself. And he can handle this situation.

What in contrast proposes the implementation with returning each time a function?

(I in advance apologize for such a simplified style of description and long text below, it's just easier for myself (and first of all -- only for myself), for not to confuse with all cases; I just try to analyze and see all available pros and cons).

  • I want to catch missing method, can you (a system) handle this situation?
  • Which missing methods? You don't have any missing method.
  • Really?
  • Yes, try it yourself:

var o = {};

o.n(); o.foo(); o.bar();

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

  • Hm... use in operator as a variant then for this case:

if (!("n" in o)) { o.n = function () {}; }

  • Yeah, right, it may help. But you conclude that the case with reading a property for such a check is broken? Btw, I saw it widely used in current scripts for providing missing stuff (e.g. if (!Array.prototype.forEach) { ... }).

  • Unfortunately. it's broken.

  • Hm, i.e. a property "exists" -- o.n (it's a function as I see), and at the same time -- does not -- "n" in o -- false?

  • Unfortunately. Yep.

  • Interesting... And what about if I want to handle both -- reading a property and calling a method in one get of a proxy?

  • No, you can't. Didn't you realized it still? -- You have always only functions in this case. Forget about non-functional properties. There is no such API. You can handle either properties, or functions via get.

  • Well, OK... Let's assume it... And what about the === operator? Is it also broken?

  • No, why is it broken? Just cache your functions by the name. Yeah, it will take a bit of code (which you possibly will repeat every time in such cases, but...)

  • Yeah, right. Fair enough (though I thought the same). Moreover, it will work with assigning to another name:

foo.bar == foo.bar; // true foo.baz = foo.bar; foo.baz == foo.bar; // also true, since foo.baz exists now (of course if we return existing properties as is)

Seems OK. Though, I see one more place which should be patched:

// a non-existing "foo.bar" foo.bar; // cache it at first reading foo.bar(); // alerts e.g. 1, first implementation

// it's existing now foo.bar = function () { alert(2); };

foo.bar(); // alerts 2

// delete it delete foo.bar;

foo.bar(); // alert 1?

So, besides that small code with caching in get, we need some invalidating cache logic in the delete trap. It seems that all this "magic" code combines in some pattern (possibly, there is a sense to encapsulate and abstract it in some sugar, don't know).

  • Another minor thing -- delete does not really delete.

delete foo.bar; foo.bar; // function

  • Right, but what are you trying to delete? A non-existing property?
  • Yes, I understand, but it just looks a bit strange -- non-existing, but still always is equal to some function. Moreover, with our caching system, I see that this is a very consistent property in it's equality invariant:

foo.bar == foo.bar; // always (correctly) true

but it always a function. I could understand that it can be for non-existing properties where undefined === undefined, but here are the "existing" functions.

  • Well...

  • 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

  • Doesn't o inherit from the Object.prototype?

  • No, it does inherit, but since you don't have a function call, you won't reach Object.prototype. Though, you can reach Object.prototype's methods (yes, with a bit overhead 'cause it's reached via our wrapper).

  • So, o.toString() calls Object.prototype.toString (in case of course I inherit toString from the Object.prototype), but at the same time o.toString !== Object.prototype.toString, right? It seems === is broken again.

  • Unfortunately.

Did I miss something?

OK, so what pros and cons we have:

Pros:

  1. we can handle call-expressions: foo.bar()
  2. functions may be applied, passed as functional values (functional WTF!): foo.bar
  3. with a little "magic" (caching by name) we can even have them equal to each other: foo.bar === foo.bar

Cons:

  1. a non-existing property is always a function: foo.bar // function
  2. at the same time, it behaves as consistently existing one, including equality: foo.bar === foo.bar (with some the mentioned "magic"); and at the same time we can't delete it.
  3. if we want to apply some patch for an object depending on existence of some property -- we can't do it using reading accessor of the property (i.e. cases with if (!foo.bar) or if (typeof foo.bar ... won't pass), only in operator may help. Yeah old scripts with testing if (!foo.bar) {...} should be rewritten (Is WEB really shouldn't be broken?)
  4. regarding the same in, a property isn't here -- "foo" in bar
# Brendan Eich (14 years ago)

On Oct 14, 2010, at 7:54 AM, Dmitry A. Soshnikov wrote:

On 14.10.2010 4:14, Brendan Eich wrote:

On Oct 13, 2010, at 6:56 AM, Dmitry A. Soshnikov wrote:

Also I think now, that what was named as pros, i.e. ability to have funargs and call/apply invariants, in real, not so pros. Because users more likely want to catch exactly missing methods (if you don't like the word "methods", since there're no methods, there are properties, let's say -- missing properties which ended with call expression at the call-site).

That's not our experience with E4X (ECMA-357), which specifies XML methods as invoke-only. They seem to be normal function-valued properties of XML.prototype, but getting one by name on an XML instance in a non-callee expression context instead tries to find an XML child element or elements of the method's name, returned as a list.

Some of this is peculiar to E4X, but the invoke-only nature of the methods, per-spec, is not. And it breaks apply and functional programming, so we extended E4X with the function:: pseudo-namespace to allow one to extract methods from XML instances.

Yes, I'm aware of it. However, you mention a similar end result (inability to extract a function with a normal (accessor) syntax), but with completely different reason. In case of EX4 you talk about the existing real methods. In case of proxies, we talk about non-existing property (which is activated with a next call expression).

Stop right there -- I think you are assuming you conclusion, or favored outcome.

For proxies we want users to reify real function objects (or function proxies which are equivalent). Not reify nothing on get, making the methods invoke-only phantoms. We are disagreeing on the premise that invoke-only methods are better.

It seems to me you favored invoke-only methods because the code to implememnt them (using noSuchMethod, an invoke trap, or even a get third argument telling context) was "simpler". Not because you wanted invoke-only methods per se. Please clarify.

I do not think invoke-only methods are a feature in any case, ignoring implementation costs of noSuchMethod vs. Proxies.

The difference is: in first case a user really deals with existing stuff and expect the functions to be extracted (of course in this case ECMA-357 had to do something -- provide :: -- to allow this)

(No, that was our function:: extension -- E4X leaves these XML prototype methods invoke-only on XML instances.)

. In the later one, at the first place, a user wants to catch the call expression.

Does the user really want only this? Or might it be "simpler" to hack with noSuchMethod, but the user would ideally want get to reify a function that could be passed around, invoked later, etc.?

I argue the latter is what users want if they think about it, or get to the point of purveying their invoke-only methods to most JS hackers, who do use partial application (apply via a helper function on a funarg), and so forth. Prototype, JQuery, etc. are full of such code. It will break badly on invoke-only methods.

This is something we should agree or disagree on before going further. Plus, I'm out of time to reply to the rest of your lengthy message. More when I can,

# Tom Van Cutsem (14 years ago)

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

  • 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?

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

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

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?

# Brendan Eich (14 years ago)

On Oct 14, 2010, at 11:57 AM, Tom Van Cutsem wrote:

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?

Tom, thanks for this, assuming it is an accurate reading of Dmitry's post, your reply clarifies something for me. When we bridged Java to JS in the '90s, we had to deal with Java objects having overloaded members. A member o.m could be an int field or a String -> int method, say. And methods could be overloaded to take different argument types.

We implemented something like proxies in C code, and for any overloaded field, we returned a proxy that could access the int field or call the String-returning method, depending on context. If called, we tried the method. If got or set with a number value, we accessed the field. For overloaded methods we tried disambiguated by JS actual argument type, but this could fail. There was an elaborate property name, accessibly only via o["m(i):S"] or some such (I forget the syntax), by which one could unambiguously name a specific method in the overload.

But in no case would one want the overloaded member to test as if it were not present. This may be where Dmitry parts company from us on TC39. Whatever happens, having methods appear only when invoked is a bug in our view.

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

It is indeed more work:

function makeLazyMethodCloner(eager) { function DELETED() {} var cache = Object.create(null); var handler = { get: function (self, name) { if (cache[name] === DELETED) return Object.getPrototypeOf(eager)[name]; if (!cache[name]) { if (name === 'hasOwnProperty') { cache[name] = Proxy.createFunction({}, function (name) { return cache[name] !== DELETED && eager.hasOwnProperty(name); }); } else { cache[name] = Proxy.createFunction({}, function () { return eager[name].apply(eager, arguments); }); } } return cache[name]; }, hasOwn: function (name) { return cache[name] && cache[name] !== DELETED; }, has: function (name) { return this.hasOwn(name) || name in Object.getPrototypeOf(eager); }, delete: function (name) { cache[name] = DELETED; return true; } }; return Proxy.create(handler, Object.getPrototypeOf(eager)); }

Running this:

js -f proxymethod2.js - m1 m2 js> p.hasOwnProperty('m1')

true js> 'm1' in p

true js> delete p.m1

true js> p.hasOwnProperty('m1')

false js> 'm1' in p

false js> o.proto.m1 = function(){return "new m1"} (function () {return "new m1";}) js> p.hasOwnProperty('m1')

false js> 'm1' in p

true js> p.m1
(function () {return "new m1";}) js> p.m1() "new m1"

Comments welcome. There's no comparison with noSuchMethod since it does not even try to fix the delete bug, as you note:

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

Exactly! Apples-to-apples comparison, the original makeLazyMethodCloner I showed compares favorably to the noSuchMethod alternative (13 lines to 9; both fail to handle delete). But you can't fix the noSuchMethod version to handle delete.

Calling that a feature in the noSuchMethod case is defining away the problem. It goes beyond invoke-only methods. These are invoke-only with a vengeance -- you can't get rid of them. They smell like a certain browser's hated host objects.

Anyway, it's useful to agree to disagree sometimes. I do not agree that invoke-only methods are a valid use-case for proxies. I waved the noSuchMethod flag hard when we discussed proxies and tried to make an invoke trap, but I'm convinced that the design you and Mark favored, with no invoke trap, is strictly better.

# Brendan Eich (14 years ago)

On Oct 14, 2010, at 12:33 PM, Brendan Eich wrote:

Tom, thanks for this, assuming it is an accurate reading of Dmitry's post, your reply clarifies something for me. When we bridged Java to JS in the '90s, we had to deal with Java objects having overloaded members. A member o.m could be an int field or a String -> int method, say.

I meant "int -> String" there, as later context suggests.

# Dmitry A. Soshnikov (14 years ago)

On 14.10.2010 19:38, Brendan Eich wrote:

On Oct 14, 2010, at 7:54 AM, Dmitry A. Soshnikov wrote:

On 14.10.2010 4:14, Brendan Eich wrote:

On Oct 13, 2010, at 6:56 AM, Dmitry A. Soshnikov wrote:

Also I think now, that what was named as pros, i.e. ability to have funargs and call/apply invariants, in real, not so pros. Because users more likely want to catch exactly missing methods (if you don't like the word "methods", since there're no methods, there are properties, let's say -- missing properties which ended with call expression at the call-site). That's not our experience with E4X (ECMA-357), which specifies XML methods as invoke-only. They seem to be normal function-valued properties of XML.prototype, but getting one by name on an XML instance in a non-callee expression context instead tries to find an XML child element or elements of the method's name, returned as a list.

Some of this is peculiar to E4X, but the invoke-only nature of the methods, per-spec, is not. And it breaks apply and functional programming, so we extended E4X with the function:: pseudo-namespace to allow one to extract methods from XML instances.

Yes, I'm aware of it. However, you mention a similar end result (inability to extract a function with a normal (accessor) syntax), but with completely different reason. In case of EX4 you talk about the existing real methods. In case of proxies, we talk about non-existing property (which is activated with a next call expression). Stop right there -- I think you are assuming you conclusion, or favored outcome.

For proxies we want users to reify real function objects (or function proxies which are equivalent). Not reify nothing on get, making the methods invoke-only phantoms. We are disagreeing on the premise that invoke-only methods are better.

Well, if "on the premise" -- what can I say? -- then the main task is to find the solutions and fix all broken invariants.

It seems to me you favored invoke-only methods because the code to implememnt them (using noSuchMethod, an invoke trap, or even a get third argument telling context) was "simpler". Not because you wanted invoke-only methods per se. Please clarify.

Of course. I'm absolutely not against if the missed methods will be real functions. Of course it's not per se that I want phantoms. "Phantoms" are just acceptable "implementation tax" to avoid all mentioned broken invariants. The tax is -- there are no funargs/apply. The tax is less than in case of broken invariants.

I do not think invoke-only methods are a feature in any case, ignoring implementation costs of noSuchMethod vs. Proxies.

It's not versus. As I already mentioned and repeat it again -- I'd like to have noSuchMethod in addition to other methods of a proxy handler (but not instead of proxies) . It still the proxies, just have additional method which handles (I repeat again) -- the case, the situation of missing a property (this is how it's possible to justify phantoms -- the user deals not with a method, but with a fact, with the situation of missing; And to handle this situation, he has a hook; from this viewpoint, possibly the user does not need a function itself).

But of course, if it's possible to have at the same time real functions and avoid broken invariants -- it'd be the best way. If there will be broken invariants -- no one needs such implementation. If it will be needed to write a huge code just to have noSuchMethod -- no one will be glad. Of course, if this huge code of fixing broken invariants will be abstracted and encapsulated from a user, then it's OK. But it's not.

So of course, I think that additional noSuchMethod will be simpler. And simplicity/shortness of the code is one of the main features.

The difference is: in first case a user really deals with existing stuff and expect the functions to be extracted (of course in this case ECMA-357 had to do something -- provide :: -- to allow this) (No, that was our function:: extension -- E4X leaves these XML prototype methods invoke-only on XML instances.)

Oh, I see.

. In the later one, at the first place, a user wants to catch the call expression. Does the user really want only this? Or might it be "simpler" to hack with noSuchMethod, but the user would ideally want get to reify a function that could be passed around, invoked later, etc.?

I argue the latter is what users want if they think about it,

But (once again) I do not propose use just noSuchMethod. I propose it as addition. And I already repeated not once -- still the whole scheme with get+fn is still here. We may even write a best-practice note that there should be used this and that (get+fn), with describing all the cases, with providing codes for caching/invalidating cache/deleting/etc. And near, just a small note -- "or, if you don't need funargs, you may use noSuchMethod hook of a proxy" (without promoting, without even examples, without everything, just a small note, that this hook is). And the we'll see what users will choose mostly.

or get to the point of purveying their invoke-only methods to most JS hackers, who do use partial application (apply via a helper function on a funarg), and so forth. Prototype, JQuery, etc. are full of such code. It will break badly on invoke-only methods.

Yes, true. But repeat, we have to solve all broken invariants if on the premise there should no be additional noSuchMethod hook for proxy handlers.

This is something we should agree or disagree on before going further.

I see (and described before) current pros and cons of both approaches. With my approach we have first+second, with first -- only first. Of course I agree that it'd be good to have them as funargs. But if the price is -- inconvenience and broken invariants -- I choose not to use missed funargs then.

Still: try to see on it not as using a function (there is no function! it's missed), but as handling the situation (the situation of missed method). Then noSuchMethod is quite logical hook.

Plus, I'm out of time to reply to the rest of your lengthy message. More when I can,

Unfortunately, me too. I'll answer on Tom's and your second letter later (today is already night here, going to seep). I read them, glad that some broken invariants became more clear after the detailed explanation and glad to see some code for handling it. But still I need to clear some things, so we'll do later.

Dmitry.

# Dmitry A. Soshnikov (14 years ago)

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

# Tom Van Cutsem (14 years ago)

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 <dmitry.soshnikov at gmail.com>

# Dmitry A. Soshnikov (14 years ago)

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

# Dmitry A. Soshnikov (14 years ago)

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

# Mark S. Miller (14 years ago)

On Wed, Oct 20, 2010 at 7:10 AM, Dmitry A. Soshnikov < dmitry.soshnikov at 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 -- 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] 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.

Thoughts?

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

P.S.[2] In case of "no", we should back to all broken invariants and find the solutions.

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 proxyexpr), 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 forget from who. If someone knows, please post. Thanks.

# Brendan Eich (14 years ago)

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

[[HasProperty]] takes O as its receiver (this) parameter, so doesn't this break double-lifting? (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 proxyexpr), 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.

# Dmitry A. Soshnikov (14 years ago)

(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 gmail.com <mailto:dmitry.soshnikov at 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? (harmony:proxies#a_simple_membrane)

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 object.bar(); // get bar, normal own real function call object.foo(); // 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 approach.

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:

esdiscuss/2010-October/011974 (the case with an external lib for which I write a patch); esdiscuss/2010-October/011947 (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 differently;
  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, (proxy.name, proxy.name(args) or proxyexpr), 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

# Tom Van Cutsem (14 years ago)

2010/10/20 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com>

(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 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 -- 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]

[[HasProperty]] takes O as its receiver (this) parameter, so doesn't this break double-lifting? ( harmony:proxies#a_simple_membrane)

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

I think Dmitry is right: calling [[HasProperty]] on a proxy object should trigger its "has()" trap in the normal way, which should work with double lifting.

In short, double lifting is a trick where the handler object of a proxy is itself a handler. See section 4.5 of the paper about proxies that MarkM and I presented at the Dynamic Languages Symposium last week (here's a link to the submitted, non-final, version: < soft.vub.ac.be/~tvcutsem/proxies/assets/proxies.pdf>) In the paper,

we use the term "meta-level shifting" instead of "double lifting", but it's the same concept.

# Brendan Eich (14 years ago)

On Oct 25, 2010, at 12:35 PM, Tom Van Cutsem wrote:

2010/10/20 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> 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).

I think Dmitry is right: calling [[HasProperty]] on a proxy object should trigger its "has()" trap in the normal way, which should work with double lifting.

IIRC the problem is a meta level-shifter wants to implement just one trap, get in the absence of invoke. Would integration of noSuchMethod using has ("object detection") break this?

Anyway, the general issue before us is a least this choice:

  • Either we stick with the current more minimal design, which besides being smaller also serves the goal of avoiding a method_missing (honey-)trap that tends to break JS's functional-programming, methods-can-be-extracted-and-passed-around-as-funargs-for-later-apply-usage, design win.

  • Or we add noSuchMethod and satisfy the "just give me a new trap, I'll either use it well in method-only programs or make it work with has/get/etc." use-cases of Dmitry and others.

To reiterate something lest people get angry, this is not an easy trade, and I don't see anyone being rigid. It's simply a design decision we have to make one way or another.

Going into the process in TC39, as I noted previously, I did my best to argue for noSuchMethod. But having now written some proxy code, I'm much less concerned about the overhead of making a coherent proxy that has methods you can extract. That leaves me thinking the case of "just give me the method_missing trap" use-case is not worth the added weight. And Tom and Mark agree, and they are the champions of this proposal.

So can we let this stand for now and see how deployed Proxies in Firefox 4, beat on by real users who are willing to try them as spec'ed without noSuchMethod, fly with developers? That is one pretty good (albeit slow) way to get new data and insights.

Or we could keep arguing here, but I think we've gone around the same block more than once!

# Dmitry A. Soshnikov (14 years ago)

On 25.10.2010 23:35, Tom Van Cutsem wrote:

2010/10/20 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com <mailto:dmitry.soshnikov at gmail.com>>

(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 gmail.com <mailto:dmitry.soshnikov at 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)
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).

I think Dmitry is right: calling [[HasProperty]] on a proxy object should trigger its "has()" trap in the normal way, which should work with double lifting.

In short, double lifting is a trick where the handler object of a proxy is itself a handler.

Ah, I see, thanks. I realized it too while was analyzing the membrane example from Brendan's slides.

See section 4.5 of the paper about proxies that MarkM and I presented at the Dynamic Languages Symposium last week (here's a link to the submitted, non-final, version: <soft.vub.ac.be/~tvcutsem/proxies/assets/proxies.pdf, soft.vub.ac.be/~tvcutsem/proxies/assets/proxies.pdf>) In the paper, we use the term "meta-level shifting" instead of "double lifting", but it's the same concept.

Yeah, I'll take a detail look on the document. Thanks.

P.S.: btw, I also realized that I misinterpreted (possibly a hardness of translation) your conclusion that there may be no sense in noSuchMethod. I missed your words about that a proxy may have no a proxied object (i.e. a handler just returns some completely virtual calculation dealing without any object). Apologize. So, from this viewpoint of course noSuchMethod looks a bit strange.

Taking into account this fact, possibly it's good to move availability of noSuchMethod to the minor task, since the major task are mentioned (in previous letters, and in the recent one to Brendan) issues -- with if (!o.foo) o.foo = {...}, etc. Since the transparency of proxy objects is a sound feature, a user of the proxy object shouldn't know that this is a proxy. And therefore he may use it as he/she wishes. But he won't be able, since o.foo will always be a function.

Also a mentioned by Mark approach with isCall flag will also require caching/invalidating the cache, etc. And unfortunately, as also was mentioned, it's a vicious circle -- if to distinguish call/not a call (via the flag e.g.) -- we, theoretically, do not have desirable virtual functional object, i.e. o.foo -- will be (possibly) undefined anyway (in other case -- for what to distinguish call/not a call?). And o.foo() will be called as a virtual function. That again leads to only-invoke phantoms scheme.

So, it's not a simple question, and there is more sense to think about issues, rather than about presence of noSuchMethod as itself.

By the way, do you see some/any solutions for those issues (Mark, Brendan)? I don't see yet.

Dmitry.

# Tom Van Cutsem (14 years ago)

2010/10/25 Brendan Eich <brendan at mozilla.com>

On Oct 25, 2010, at 12:35 PM, Tom Van Cutsem wrote:

2010/10/20 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com>

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

I think Dmitry is right: calling [[HasProperty]] on a proxy object should trigger its "has()" trap in the normal way, which should work with double lifting.

IIRC the problem is a meta level-shifter wants to implement just one trap, get in the absence of invoke. Would integration of noSuchMethod using has ("object detection") break this?

No, this would be the case if [[HasProperty]] were called directly on the handler object. However, calling [[HasProperty]] on the proxy object will still query the handler for its "has" trap using the handler's [[Get]] internal method, i.e. it won't feature-test the handler object directly.

Anyway, the general issue before us is a least this choice:

  • Either we stick with the current more minimal design, which besides being smaller also serves the goal of avoiding a method_missing (honey-)trap that tends to break JS's functional-programming, methods-can-be-extracted-and-passed-around-as-funargs-for-later-apply-usage, design win.

  • Or we add noSuchMethod and satisfy the "just give me a new trap, I'll either use it well in method-only programs or make it work with has/get/etc." use-cases of Dmitry and others.

To reiterate something lest people get angry, this is not an easy trade, and I don't see anyone being rigid. It's simply a design decision we have to make one way or another.

Going into the process in TC39, as I noted previously, I did my best to argue for noSuchMethod. But having now written some proxy code, I'm much less concerned about the overhead of making a coherent proxy that has methods you can extract. That leaves me thinking the case of "just give me the method_missing trap" use-case is not worth the added weight. And Tom and Mark agree, and they are the champions of this proposal.

That's a good summary.

So can we let this stand for now and see how deployed Proxies in Firefox 4, beat on by real users who are willing to try them as spec'ed without noSuchMethod, fly with developers? That is one pretty good (albeit slow) way to get new data and insights.

I think this is a good choice in going forward. Since noSuchMethod can be an optional trap, it could be added later without breaking existing handlers that do not define it. The only issue we can run into is that some handler code out there already defines a noSuchMethod trap and means it to do something completely different than what a future spec. has in mind.

# Dmitry A. Soshnikov (14 years ago)

On 26.10.2010 11:41, Tom Van Cutsem wrote:

2010/10/25 Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>>

On Oct 25, 2010, at 12:35 PM, Tom Van Cutsem wrote:
2010/10/20 Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com
<mailto:dmitry.soshnikov at gmail.com>>

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


I think Dmitry is right: calling [[HasProperty]] on a proxy
object should trigger its "has()" trap in the normal way, which
should work with double lifting.
IIRC the problem is a meta level-shifter wants to implement just
one trap, get in the absence of invoke. Would integration of
noSuchMethod using has ("object detection") break this?

No, this would be the case if [[HasProperty]] were called directly on the handler object. However, calling [[HasProperty]] on the proxy object will still query the handler for its "has" trap using the handler's [[Get]] internal method, i.e. it won't feature-test the handler object directly.

Anyway, the general issue before us is a least this choice:

* Either we stick with the current more minimal design, which
besides being smaller also serves the goal of avoiding a
method_missing (honey-)trap that tends to break JS's
functional-programming,
methods-can-be-extracted-and-passed-around-as-funargs-for-later-apply-usage,
design win.

* Or we add noSuchMethod and satisfy the "just give me a new trap,
I'll either use it well in method-only programs or make it work
with has/get/etc." use-cases of Dmitry and others.

To reiterate something lest people get angry, this is not an easy
trade, and I don't see anyone being rigid. It's simply a design
decision we have to make one way or another.

Going into the process in TC39, as I noted previously, I did my
best to argue for noSuchMethod.

Damn, where have I been at that moment? ;)

But having now written some proxy code, I'm much less concerned
about the overhead of making a coherent proxy that has methods you
can extract. That leaves me thinking the case of "just give me the
method_missing trap" use-case is not worth the added weight. And
Tom and Mark agree, and they are the champions of this proposal.

That's a good summary.

Yep, the summary is good, but as was mentioned -- if to forget about noSuchMethod and to move it to the minor goal, then the major one -- is solving the issues related with current imitation of noSuchMethod (via get+fn). The most inconvenient issue, is that in this implementation always a function is returned (that breaks checks such as if (!foo.bar) foo.bar = {} -- and actually breaks principles of an abstraction -- a designer of the foo forces a user of the foo to have a very strange situation -- always a function is returned for every non-existing stuff; thus, the user, by principle of the abstraction shouldn't know that he deals with a proxied object). Unfortunately, this issue is unresolvable, since to have virtual functional objects (being able to pass them as funargs e.g.) it's required to return always a function for every non-existing property. Plus, inconvinience with caching/invalidationg the cache to keep the === invariant.

So, from this viewpoint, this additional noSuchMethod hook seems just a logical consequence of trying to avoid these issues (but not a desire to exactly have it).

So can we let this stand for now and see how deployed Proxies in
Firefox 4, beat on by real users who are willing to try them as
spec'ed without noSuchMethod, fly with developers? That is one
pretty good (albeit slow) way to get new data and insights.

I think this is a good choice in going forward. Since noSuchMethod can be an optional trap, it could be added later without breaking existing handlers that do not define it. The only issue we can run into is that some handler code out there already defines a noSuchMethod trap and means it to do something completely different than what a future spec. has in mind.

Yes, possibly it's a good choice, it really seems just a design decision ("either with, or without it"). So a real users feedback will be possibly a better reason. Let it be. The only thing I'm also worried is that it won't become too late.

P.S.: for fairness, possibly it's needed to mention on strawman proxy page, that such provided imitation of noSuchMethod has know lacks and that users should use it carefully.

Thanks, Dmitry.

# Tom Van Cutsem (14 years ago)

But having now written some proxy code, I'm much less concerned about the

overhead of making a coherent proxy that has methods you can extract. That leaves me thinking the case of "just give me the method_missing trap" use-case is not worth the added weight. And Tom and Mark agree, and they are the champions of this proposal.

That's a good summary.

Yep, the summary is good, but as was mentioned -- if to forget about noSuchMethod and to move it to the minor goal, then the major one -- is solving the issues related with current imitation of noSuchMethod (via get+fn). The most inconvenient issue, is that in this implementation always a function is returned (that breaks checks such as if (!foo.bar) foo.bar = {} -- and actually breaks principles of an abstraction -- a designer of the foo forces a user of the foo to have a very strange situation -- always a function is returned for every non-existing stuff; thus, the user, by principle of the abstraction shouldn't know that he deals with a proxied object). Unfortunately, this issue is unresolvable, since to have virtual functional objects (being able to pass them as funargs e.g.) it's required to return always a function for every non-existing property. Plus, inconvinience with caching/invalidationg the cache to keep the === invariant.

So, from this viewpoint, this additional noSuchMethod hook seems just a logical consequence of trying to avoid these issues (but not a desire to exactly have it).

With the risk of going round the block again... ;-)

I agree with your premises, namely that: a) if (!foo.bar) foo.bar = {} is a common pattern to patch up objects that don't know how to deal with bar requests b) a proxy that pretends to have all possible properties can't be patched up using this pattern

But I don't buy your conclusion, which is that a + b implies that proxies are somehow broken. If the proxy says "I know how to handle bar properties", then the client doesn't need to patch up the proxy by adding a bar property. Your patch-up pattern will work perfectly fine with proxies that don't pretend to have a foo property.

There are good reasons for why proxy writers may want to distinguish get from invoke (for example, because invoke doesn't require caching the function returned by get), but IMHO this patch-up pattern is not one of them.

So can we let this stand for now and see how deployed Proxies in Firefox 4, beat on by real users who are willing to try them as spec'ed without noSuchMethod, fly with developers? That is one pretty good (albeit slow) way to get new data and insights.

I think this is a good choice in going forward. Since noSuchMethod can be an optional trap, it could be added later without breaking existing handlers that do not define it. The only issue we can run into is that some handler code out there already defines a noSuchMethod trap and means it to do something completely different than what a future spec. has in mind.

Yes, possibly it's a good choice, it really seems just a design decision ("either with, or without it"). So a real users feedback will be possibly a better reason. Let it be. The only thing I'm also worried is that it won't become too late.

P.S.: for fairness, possibly it's needed to mention on strawman proxy page, that such provided imitation of noSuchMethod has know lacks and that users should use it carefully.

I added a link to this conversation at harmony:proxies#simulating_nosuchmethod_doesnotunderstand

# Brendan Eich (14 years ago)

On Oct 27, 2010, at 9:07 AM, Tom Van Cutsem wrote:

With the risk of going round the block again... ;-)

Last time for sure. :-|

I agree with your premises, namely that: a) if (!foo.bar) foo.bar = {} is a common pattern to patch up objects that don't know how to deal with bar requests b) a proxy that pretends to have all possible properties can't be patched up using this pattern

But I don't buy your conclusion, which is that a + b implies that proxies are somehow broken. If the proxy says "I know how to handle bar properties", then the client doesn't need to patch up the proxy by adding a bar property. Your patch-up pattern will work perfectly fine with proxies that don't pretend to have a foo property.

There are good reasons for why proxy writers may want to distinguish get from invoke (for example, because invoke doesn't require caching the function returned by get),

(that was a reason I gave back when I was arguing for an "invoke" trap, yes.)

but IMHO this patch-up pattern is not one of them.

Agreed, it is not one of them.

Dmitry, you are hypothesizing a proxy whose handler lazily creates all trapped properties with some non-falsy initial value.

But no one is dictating such a proxy handler implementation, it is broken as Tom points out but due to its own straw-design mistake (not due to anything inherent in Proxies), and in reality it's hard to write such a proxy.

In reality, programmers won't write handlers that trap any property name and return a useful (non-empty or not-constant-for-many-names) function value. My lazy method cloner, e.g., clones only methods found in (whether "own" or not) the given clone-parent object. It can't proxy a method for a property not in that object.

There is no "object detection" pattern that works for a silly proxy that returns, e.g., (function(){}), for all property has and get traps. Such a proxy is doing what you coded it to do, however broken or absurd. It is not the fault of the harmony:proxies proposal, or in any way due to the lack of noSuchMethod traps, that this could happen.

So really, this is a straw-man fallacy. We knock it down, but Proxies still stand. One can, after all, write silly or broken toString implementations in JS today, and patch them into Object.prototype even. This is a feature, not a bug.

Object detection, BTW, is not metaprogramming at all, IMHO. So I don't agree that if (!foo.bar) foo.bar = ... is a "pattern" for trapping missing properties. It is imperative code in the base-level language. You have to write it in a certain place (or places) in the control flow graph. If you want something that runs for a given foo when accessing "bar", via a proxy-like API, then we are talking about real behavioral intercession metaprogramming.

So I don't buy Dmitry's argument that noSuchMethod somehow complements or completes the existing object detection pattern, but for call expressions -- the method_missing Ruby feature. It is not complementary, or even on the same meta-level. And really, the primal issue is whether invoke-only methods are a desirable pattern.

The reason I gave up noSuchMethod was that you can't make a coherent object with extractable methods-as-funargs without the full suite in the current harmony:proxies proposal, if all you have is noSuchMethod. But given the full suite in the current proposal, if you lack noSuchMethod, you can still emulate host and native objects. There are no invoke-only methods in ECMA-262, and the ones in ECMA-357 are considered design flaws, corrected (albeit with a clunky workaround, function::) in SpiderMonkey's E4X implementation.

So my final answer remains: invoke-only methods are an anti-pattern.

Ok, still I have no doubt Dmitry will argue: a trap for invoke-only methods is desirable on some basis, by some developers. It may be among a large number of developers, but we've heard only from a few (and with too many trips around the same block).

In any event, without more going 'round the same block over and over, we'll have to see how the more minimal, current harmony:proxies design plays out with a wider audience of developers who can actually play with it in Firefox 4.

(I still don't see how invoke-only methods work with all the partial-applying/currying JS libraries out there today (Prototype, JQuery, etc.), but perhaps there is a bright line between the functional-programming style of those libraries, and the intended noSuchMethod invoke-only methods.)

# Dmitry A. Soshnikov (14 years ago)

On 27.10.2010 21:15, Brendan Eich wrote:

On Oct 27, 2010, at 9:07 AM, Tom Van Cutsem wrote:

With the risk of going round the block again... ;-) Last time for sure. :-|

Acknowledged ;|

I agree with your premises, namely that: a) if (!foo.bar) foo.bar = {} is a common pattern to patch up objects that don't know how to deal with bar requests b) a proxy that pretends to have all possible properties can't be patched up using this pattern

But I don't buy your conclusion, which is that a + b implies that proxies are somehow broken. If the proxy says "I know how to handle bar properties", then the client doesn't need to patch up the proxy by adding a bar property. Your patch-up pattern will work perfectly fine with proxies that don't pretend to have a foo property.

There are good reasons for why proxy writers may want to distinguish get from invoke (for example, because invoke doesn't require caching the function returned by get), (that was a reason I gave back when I was arguing for an "invoke" trap, yes.)

but IMHO this patch-up pattern is not one of them. Agreed, it is not one of them.

Dmitry, you are hypothesizing a proxy whose handler lazily creates all trapped properties with some non-falsy initial value.

But no one is dictating such a proxy handler implementation, it is broken as Tom points out but due to its own straw-design mistake (not due to anything inherent in Proxies), and in reality it's hard to write such a proxy.

In reality, programmers won't write handlers that trap any property name and return a useful (non-empty or not-constant-for-many-names) function value. My lazy method cloner, e.g., clones only methods found in (whether "own" or not) the given clone-parent object. It can't proxy a method for a property not in that object.

There is no "object detection" pattern that works for a silly proxy that returns, e.g., (function(){}), for all property has and get traps. Such a proxy is doing what you coded it to do, however broken or absurd. It is not the fault of the harmony:proxies proposal, or in any way due to the lack of noSuchMethod traps, that this could happen.

So really, this is a straw-man fallacy. We knock it down, but Proxies still stand. One can, after all, write silly or broken toString implementations in JS today, and patch them into Object.prototype even. This is a feature, not a bug.

Object detection, BTW, is not metaprogramming at all, IMHO. So I don't agree that if (!foo.bar) foo.bar = ... is a "pattern" for trapping missing properties. It is imperative code in the base-level language. You have to write it in a certain place (or places) in the control flow graph. If you want something that runs for a given foo when accessing "bar", via a proxy-like API, then we are talking about real behavioral intercession metaprogramming.

So I don't buy Dmitry's argument that noSuchMethod somehow complements or completes the existing object detection pattern, but for call expressions -- the method_missing Ruby feature. It is not complementary, or even on the same meta-level. And really, the primal issue is whether invoke-only methods are a desirable pattern.

The reason I gave up noSuchMethod was that you can't make a coherent object with extractable methods-as-funargs without the full suite in the current harmony:proxies proposal, if all you have is noSuchMethod. But given the full suite in the current proposal, if you lack noSuchMethod, you can still emulate host and native objects. There are no invoke-only methods in ECMA-262, and the ones in ECMA-357 are considered design flaws, corrected (albeit with a clunky workaround, function::) in SpiderMonkey's E4X implementation.

So my final answer remains: invoke-only methods are an anti-pattern.

Ok, still I have no doubt Dmitry will argue: a trap for invoke-only methods is desirable on some basis, by some developers. It may be among a large number of developers, but we've heard only from a few (and with too many trips around the same block).

In any event, without more going 'round the same block over and over, we'll have to see how the more minimal, current harmony:proxies design plays out with a wider audience of developers who can actually play with it in Firefox 4.

(I still don't see how invoke-only methods work with all the partial-applying/currying JS libraries out there today (Prototype, JQuery, etc.), but perhaps there is a bright line between the functional-programming style of those libraries, and the intended noSuchMethod invoke-only methods.)

Yes, all this has already been said before in this thread, and the reason to have "alive funargs" instead of "phantoms" is a sound reason. Again, what I wanted -- is to catch all subtle cases, to see whether the API is consistent and to find the solutions for inconsistency. Nobody wants just to argue. So, let's conclude at this step. As said before, if later this additional noSuchMethod will be strongly required by JS programmers, maybe it can be added, since it's not hard and won't break anything. Or just the isCall for get.

Anyway, Brendan and Tom, thanks for discussion and clarifications, it was useful.

Dmitry.