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?) :

-------- Original message -----

Hi,

I continue experiments with proxies (the first one was delegation-based mixins). Currently (only for academic curiosity) I've implemented objects with "magic hooks" which are defined "directly" on the objects. Actually, it's just a "proxied proxies", and there is no much sense in it (except maybe that such objects syntactically are shorter than real proxies), but repeat, the reason is -- only an academic interest of how do Proxy objects work. The script is here: http://gist.github.com/617338

But this letter is not about the script (the script is just a reason), but about Proxy objects themselves. I'd like to clarify some features.

First, unfortunately, because of stratification of /Proxy.create(handler, proto)/ and /Proxy.createFunction(handler, callTrap, constructTrap)/ I can't manage in my "sugar" /typeof/ and /[[Class]]/ of a proxy object (depending on presence of /__call__/ or /__construct__/ -- in one case /typeof/ should result "function", in other case, if I later remove both /__call__/ and /__construct__/, it'd nice to have "object"). Yes, it's quit clear that it's related with /Function.prototype/ which is (should be?) set as a prototype of the function proxy. This question is not so essential, because I understand that the proxy strawman's idea assumes that a user will work with proxies, but not with directly defined (not stratified from an object) meta-methods, so I just mention this fact (that it turned out inconvenient for my script) :) Although, possibly someone will want to use this Python's "fancy" /__name__/s "sugar".

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

Of course, /get/ method of the handler may return (possibly creating every time new) a function and then we have /__noSuchMethod__/implementation. That's OK (having this approach we have "funargs + call/apply" scheme). But unfortunately, we can't distinguish what contains a call-site:

Is it a property reading:

foo.bar

or possible invocation:

foo.bar()

How should /get/ trap of the proxy be implemented to support both: /__noSuchProperty__/ and /__noSuchMethod__/ ? This is the main question. Seems, there is no obvious way.

On the other hand, regating "funargs + call/apply" scheme: if the /get/ trap returns each time a new function, then === invariant is broken. That means it should return some predefined function from available for handling function-table (i.e. if name is "foo" -- return table.foo, etc). It of course can dynamically define the function and save it in the table. And actually to keep === invariant, it should do so.

Dmitry.

-------- End of original message -----


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 -- http://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"
    --https://mail.mozilla.org/pipermail/es-discuss/2010-October/thread.html --
    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.



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

Returning a function approach has advantages:

1. returned method can be extracted as a functional object (and e.g. passed as a funarg); 2. we have working call/apply invariant, i.e. foo.bar.apply(...) is foo.bar(...)

But what if the user wants to trap get for non-existing /properties/ and non-existing /functions/? Here e.g. /get/ function from my implementation:

/**
* generic [[Get]]
*/
handler.get = function (r, name) {

  // __get__ hook
  if ("__get__" in object) {
    object.__get__(name);
  }

  // if a property is not found
  if ("__noSuchProperty__" in object && !(name in object)) {
    return object.__noSuchProperty__(name);
  }

  // here I don't know how to handle a method call-site

  return object[name];
};

foo.bar;  // __get__ bar
foo.nonExisting;  // __get__ nonExisting, __noSuchProperty__ nonExisting
foo.baz(); // __get__ baz
foo.nonExisting(); // *HOW CAN I MANAGE IT?*

I don't know what a call-site contains -- a function invocation or a property reading, so I can't in one get return a function or a property.

There can be /invoke/ trap which traps /all/ invocations:

foo.bar(); // __invoke__ bar
foo.nonExisting(); // __invoke__ bar

But then it will be hard to distinguish it from /get/ which returns a function.

On the other hand, there can be not /invoke/, but /noSuchMethod/ (/methodMissing/, /doesNotUnderstan/ -- call it as you wish) -- /exactly method/, i.e. a /TypeError "not a function"/. Then we can combine both: get+fn and noSuchMethod:

get: function (r, name) {
  if (name == "bar") {
    return function () {};
  }
  return object[name];
}

then we have:

proxy.bar; // function
proxy.baz; // object.baz
proxy.bar(); // OK -- get + fn -> call
proxy.nonExisting() // fail, get+undefined -> call: TypeError "not a function"

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.


Dmitry.


    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: <https://mail.mozilla.org/pipermail/es-discuss/2010-May/011062.html>

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 <http://obj.name> and obj.name <http://obj.name>() would both return 'a'. This could lead to a lot of confusion, since it's hard to quantify what "obj.name <http://obj.name>" denotes. If it's supposed to be a method, then obj.name <http://obj.name> should return a function. If it's supposed to be a getter, then obj.name <http://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 <[email protected] <mailto:[email protected]>>

    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.


    _______________________________________________
    es-discuss mailing list
    [email protected] <mailto:[email protected]>
    https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to