On May 4, 2009, at 10:46 PM, Allen Wirfs-Brock wrote:

-----Original Message-----
From: Brendan Eich [mailto:[email protected]]

On May 4, 2009, at 6:20 PM, Allen Wirfs-Brock wrote:

In the function defined for the example's invoke item the first
argument to apply probably should be obj rather than peer.

Possibly, but in the example peer[id] may be a function that insists
on |this| being bound to peer, not obj. If the function is pure, the
arguments flow in and a result comes back, and the catchall delegates
without mutating peer or obj. If the function mutates |this|, then the
effects can be recovered by calling other methods, or by property
access, which again is delegated without a wrapper.

You're right it is probably situational. As I look at the example again, I'm uncertain about your intent for the invoke and construct properties of the catch all descriptor. My original assumption was that they would be invoked if obj (the object itself, not one of its properties) was invoked or new'ed. However, in that case there wouldn't be a property id to pass as an argument to the handler function, yet the function signatures for invoke and construct in the example have an id argument.

Yes, they are for "invoking a method" and "constructing via new applied to a method" respectively. From

http://wiki.ecmascript.org/doku.php?id=discussion:catchalls

"One user expectation, fed and watered by E4X, is that methods are not properties, or to avoid this JS oxymoron, that methods are not in the default property namespace:

js> x = <room><width>12</width><length>16</length></room>
<room>
  <width>12</width>
  <length>16</length>
</room>
js> x.length
16
js> x.length()
1
Another point in favor of a call * catchall is efficiency:

var o = {
  peer: ...,
  get *(id) {
    return function () { return this.peer[id](this.peer, arguments) }
  }
}
creates a closure per get, whether or not the get result is called. Whereas

var o = {
  peer: ...,
  call *(id, args) { return this.peer[id].apply(this.peer, args) }
}
creates a generic call forwarder."

It isn't just E4X. People want to make objects with arbitrary methods callable on a given object, e.g. when bridging to CORBA or Java or whatever. There may be static types in the peer space, but still you don't want to mandate a custom JS object mapping exactly the M methods in type T via hardcoded proxying. You don't want to reify the methods as function objects stored in obj, even. You want to direct the call through to the peer function.

This is why invoke has an ident leading parameter in http://wiki.ecmascript.org/doku.php?id=proposals:catchalls .

Invoking a callable object is best done through a separate protocol, not catchalls but something like the ES4 intrinsic::invoke (contrast to meta::invoke the catchall hook for invoking a property, originally not expressed by that qualified name but rather via dedicated 'call *(id, args) {...}' syntax in the property initialiser production under the object initialiser syntax).


The other possibility, at least for invoke, is that the handler is invoked whenever an attempt is made to call the value of an non- existent property of obj.

The general intention for catchalls is to call the action hook only when the property is missing. The odd case is set, vs. add in the strawman (or first-set vs. every-set in the prose).

There's a less common case of wanting every-get too, to filter results of and/or control access to existing properties.


In other words invoke and get are mutually exclusive. I'm guessing that wasn't you intent as it really doesn't match the ES meta-model.

No, you've got it -- get and invoke are exclusive precisely to avoid cons'ing up methods -- function-valued properties -- just to dispatch calls.


However, I think I was momentarily thinking that way when I made the observation about passing obj rather than peer. That raises for me some usability concerns. I'm well aware that a "method" is called by first getting the property value and then invoking the function value returned from the get but I still made the mental slip of thinking that invoke in the catch all descriptor was about calling a method of obj rather than calling obj itself. I expect that the typical non-expert JavaSript programmer will be even more prone to making that mistake.

This is the non-mistake droid you are looking for, and what I was trying to sketch. I'll clarify the strawman page. Thanks for bringing this to light.


I would be inclined to specify an additional argument (probably the
first) for each handler function that would be passed the "this"
value. Closure capture like is done in the example works but my
intuition is that there will be situations where it would be handy
to use the same catch-all descriptor for several objects.

Agreed, but why not use |this| instead of an extra (leading) parameter?


Good point, it depend upon whether the model is that the entire descriptor is associated with obj as its catch all handler or whether the individual handler functions are separately associated with obj. If the entire catch all descriptor object is associated with obj then I would expect that when one of its methods is invoked that the this object would be the descriptor object rather than obj. On the other hand, if the descriptor object is just a way to aggregate a bunch of optional arguments to defineCatchAll (similar to the defineProperty pattern) then it is reasonable to assume that the individual handler functions are invoked as if they were methods of obj and thus the this value would be obj.

That's the idea -- like defineProperty, the object initialiser syntax is a convenient approximation of named parameter passing. No this- binding implied (as indeed none is implied in other object initialisers).

/be

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

Reply via email to