New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Object method identification: convention or requirement? #54
Comments
It might be nice if property access and function invocation were orthogonal, though I know this isn't how js works wrt. |
Goblins doesn't prescribe a perspective on how methods work, or even if they are used. It's not an exception to the rule that actors are just lambdas and have no methods.
I'm not sure what you mean, could you elaborate? |
Early in the pre-pre-standardization state of discussing OCapN, I had a talk with @erights about this. Since Goblins takes a "lambda, the ultimate" view, and Agoric's JS decends from the E view that "method-objects are the ultimate", obviously there's a mismatch. But what we had discussed as a solution, per my memory, is that since Agoric doesn't really use symbols anyway, this can be detected as an indicator where if the first argument is a symbol, that's conventionally the same as calling a method. So |
I don't think that works, because both strings and symbols can be used as JS property keys (e.g., |
Re: keeping property access orthogonal, my thinking was that it might be nice if whatever js maps to the operations regarding
There's also a separate problem with this, which is that functions can themselves have properties:
|
I’m weighing in on Team Method Invocation Normalization is a Requirement. An idiomatic Guile Goblin Object is very similar to an Endo JavaScript Far Object. These are approximately equivalent. (define actor1
(methods
(('method1 arg1 arg2) "method1 called")
(('method2 arg1 arg2) "method2 called")))
(actor1 'method1 arg1 arg2)
(actor2 'method2 arg1 arg2) const actor1 = Far('Actor', {
method1: (arg1, arg2) => "method1 called",
method2: (arg1, arg2) => "method2 called",
});
E(actor1).method1(arg1, arg2);
E(actor2).method2(arg1, arg2); However, if we do not normalize invocation in O’Cap’n, such that the wire representation of a method name is the same regardless of peer languages, the idioms for calling across languages will be different, as well as the idioms for defining actors. (actor1 "method1" arg1 arg2)
(actor1 "method2" arg1 arg2) E(actor1)[Symbol.for('method1')](arg1, arg2)
E(actor2)[Symbol.for('method2')](arg1, arg2) To emulate a JavaScript actor in Scheme, you’ll need to drop below the (lambda (method arg1 arg2)
(cond method
((equal? method "method1") "called method1")
((equal? method "method2") "called method2"))) And, likewise, to emulate a Scheme actor in JavaScript: const actor1 = Far('Actor', {
[Symbol.for('method1'): (arg1, arg2) => "method1 called",
[Symbol.for('method2'): (arg1, arg2) => "method2 called",
}); The following JavaScript and Scheme actors are approximately equivalent bare functions and respective calling conventions: (define actor3 (method arg1 arg2)
(if (equal? method #f) "called as a function"))
(actor3 #f arg1 arg2) const actor3 = Far('Actor', (arg1, arg2) => "called as a function");
E(actor3)(arg1, arg2); However, the following Scheme actor is not expressible in JavaScript because it implements a heterogenous mix of function and object behaviors, including JavaScript and Scheme idioms for method names. (define actor4 (method arg1 arg2)
(cond method
((equal? method #f) "called-as-function")
((equal? method 'symbol) "called-with-symbol")
((equal? method "string") "called-with-string")))
(actor4 #f)
(actor4 'symbol)
(actor4 "string")
The following JavaScript actor cannot be called from Scheme because it implements both a well-known-symbol and a registered-symbol. These are disjoin namespaces. Scheme could presumably call this JavaScript method using a tagged type. I do not know how that would look, but it would not be pretty. const actor5 = Far('Actor', {
[Symbol.asyncIterator): (arg1, arg2) => "asyncIterator well-known symbol called",
[Symbol.for('asyncIterator'): (arg1, arg2) => "asyncIterator registered symbol called",
}); These complications come to bear especially when O’Cap’n begins defining protocols like the bootstrap object for three-party-handoff, which effectively requires every language to speak to every other language using the Scheme actor idiom. E(bootstrap)[Symbol.for('deposit-gift')](gift) (bootstrap 'deposit-gift gift) I would like to arrive at an agreement that:
I wish to then convince you:
I move:
I do not think Agoric needs well-known-symbols or registered symbols on the wire. We can make do well with just one bank of method names and enjoy the reduction in complexity. |
I think I agree with these.
I'm not sure this is true, but I'm coming around to the idea that they may be more trouble than they're worth.
This matches the capnp convention. I'm not convinced we need to enforce dromedaryCase here, and it is probably more trouble than it's worth, but we should follow this convention for any standard interfaces we define.
...these are not sticking to my brain; can you elaborate on the design you're proposing? I've been musing as to whether it wouldn't be better to just have method calls be the only thing supported, and let implementations wanting bare functions use something like python's |
Originally posted by @gibson042 in #42 (comment)
Agoric arguably promotes this convention into a requirement in the sense that every inbound delivery message is interpreted as a [method, arguments] array in which the method is either
undefined
(a special case corresponding with direct function invocation) or is coerced to a JavaScript property key (i.e., either preserved as a symbol or coerced to a string) and interpreted as identifying a method of the target object, and e.g. the coercion of any pass-by-copy record or other complex data (if successful) would destructively conflate it with the string "[object Object]".The text was updated successfully, but these errors were encountered: