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