IRC channel logs
2023-06-19.log
back to list of logs
<KrisKowal[m]>I’d be much obliged if someone could point me at an example of a Goblins actor implementation. I’m looking for concrete examples of Actors that can be used like a lambda (nil method name), like an object (symbolically named method), or like a JavaScript object (stringly named method) cc Jessica <KrisKowal[m]>Would also be obliged to see how these get applied or invoked remotely. <KrisKowal[m]>In JavaScript, object.method() is a stringly-named method. It’s equivalent to object'method' <KrisKowal[m]>Whereas objectSymbol.for('deposit-gift') is a symbolically named method invocation. <KrisKowal[m]> * Whereas object[Symbol.for('deposit-gift')]() is a symbolically named method invocation. <KrisKowal[m]> * In JavaScript, object.method() is a stringly-named method. It’s equivalent to object['method'](). <KrisKowal[m]>Whereas object[Symbo.asyncIterator]() is a well-named-symbolically named method invocation, and the well-known-symbols have a disjoint namespace from other interned symbols. This is the basis of JavaScript’s iterator protocol among other things. <KrisKowal[m]>That is to say, object[Symbol.for('asyncIterator')]() is not equivalent to object[Symbol.asyncIterator](). <juliana[m]>Hopefully they help! And if not Spritely folks may be able to when they're free <KrisKowal[m]>Interesting. I could use a reference to the implementation of the methods macro. <KrisKowal[m]>Specifically, I’m hoping to answer the question, “Is it possible for a Goblins actor to provide all of the behaviors of a function, an object with symbol methods, and an object with string-name methods (since this is currently necessary to emulate a JavaScript object) <KrisKowal[m]>This presumably boils down to something like a switch/case on the method name, and the method name can be any value? <KrisKowal[m]>Or will O’Cap’n only dispatch in the two calling forms: (f nil ...args) or (f symbol ...args)? <KrisKowal[m]>That is to say, could an actor handle (actor "toString")? <KrisKowal[m]> * Or will O’Cap’n only dispatch in the two calling forms: (actor nil ...args) or (actor symbol ...args)? <juliana[m]>The current Guile implementation does use let limiting method identifiers to symbols, but there's no hard-and-fast reason I'm aware of this has to be the case. The macro could be implemented differently to get support for strings. There are questions of performance and code readibility at play and I don't know all the details well enough. But at the present moment, Guile Goblins cannot handle strings as method names <KrisKowal[m]>(Also, no urgency. Very happy with an answer in 24 hrs!) <KrisKowal[m]>Can a single Guile actor provide both (actor nil) and (actor 'symbol) behaviors? <KrisKowal[m]> * (I assume nil would be `undefined` in JavaScript, `None` in Python, `nil` in Go, &c) <KrisKowal[m]>I gather that it’s not likely to be expressed and might be expressible. <KrisKowal[m]>That might be close enough to what I’m hoping, but for Hyrum’s Law, my hope is the answer is no. <KrisKowal[m]>My impression from speaking with dthompson earlier was that Goblins actors were much more free-form lambdas. <KrisKowal[m]>I’m thinking about mutual expressivity, that no behavior hosted by JavaScript cannot be expressed by Goblins and vice versa. <isd>Sounds like the methods macro can't, but you could in principle just write the switch yourself if you needed to do something else. <KrisKowal[m]>I would not pester, but I’ve been asked to descend from the æther and provide concrete examples! <isd>...apologies if the parens are off; don't have brace matching in my matrix client... <KrisKowal[m]>Thanks. I think that the low-level is the level I’m going for and that’s got all the atoms I wasn’t sure about. cond for and equal? should get me all the way, knowing 'symbol and "string". Oh, I need to know how to spell nil. That’s false? <KrisKowal[m]> * Thanks. I think that the low-level is the level I’m going for and that’s got all the atoms I wasn’t sure about. cond and equal? should get me all the way, knowing 'symbol and "string". Oh, I need to know how to spell nil. That’s false? <isd>...which is semantically false as well. <isd>That's a good question; it will map to boolean false at the wire, so I don't know what you'd map null and undefined to <isd>scheme doesn't have a direct equivalent afaik <isd>Right, but that's also the empty list, which need to also be different. <KrisKowal[m]>Yeah, I assume [] will arrive as '() and come back as [] <isd>Probably something is going to have to map in some hacky way if we're going to have two bottoms. <KrisKowal[m]>As I assume undefined will arrive as undefined and come back as undefined. <KrisKowal[m]>I assume null will arrive as indigestable tagged data and come back as null. <isd>Yeah, we might need to do that for both null and undefined. <isd>juliana: do you just mean the symbol `'undefined`, or is there some scheme construct I'm unaware of? <KrisKowal[m]>This suggests that JavaScript E(f)() needs to arrive as a #f invocation. <KrisKowal[m]> * This suggests that JavaScript E(f)() needs to arrive as a(n?) #f invocation. <KrisKowal[m]> * This suggests that JavaScript E(f)() needs to arrive as a[n] #f invocation. <KrisKowal[m]> * This suggests that JavaScript E(f)() needs to arrive as a/an #f invocation. <isd>Another piece of the puzzle to think about: how are non-call pipeline operators supposed to work? E.g. in capnp you can do alice.foo().bar.baz.quux() in one round trip, but some of those are field accesses rather than calls <KrisKowal[m]>In JavaScript, E.get(remote).baz is an async field access and our hope is to eventually have remote~.baz for that. <isd>What does that look like at the protocol level? <KrisKowal[m]>Which is to say E.get(remote).baz evaluates to a promise. <KrisKowal[m]>I can’t speak for Agoric’s implementation, but my implementation of Q-Connection is based on the same prior art. The promise “handler” is obliged to implement “send(method, ...rest)” where “method” is “then”, “get”, “apply”, or “invoke” <isd>Hm, so how do you distinguish .bar.baz from .bar().baz? <isd>I guess the first would be .get('baz') and the second would be .apply().get('baz')? <KrisKowal[m]>Precisely, E.get(E(far).bar()).baz produces an eventual reference for the baz property of the record eventually returned by the bar method of the far object. <KrisKowal[m]>And that would be far~().baz if we got tilde operators into the language. <KrisKowal[m]> * And that would be far~.bar()~.baz if we got tilde operators into the language. <KrisKowal[m]>And E.get(E(far)()).baz produces an eventual reference for the baz property of eventually applying the far function. <KrisKowal[m]>And that would be far~()~.baz if we got tilde operators. <KrisKowal[m]> * And E.get(E(far)()).baz produces an eventual reference for the baz property of the record returned by eventually applying the far function. <KrisKowal[m]><isd> "I guess the first would be .get(..." <- And this is a coherent statement about the handler protocol, if you assume every handler function returns a handler, as would be well and proper in Haskell. <KrisKowal[m]>And would be true of JavaScript except we allow for auto-wrapping and auto-unwrapping of promises and eventual references. <KrisKowal[m]>Not sure how to express that each method might have a different signature and handle variadic other arguments differently. <Zarutian_iPad>isd: js`E.get(foo).baz results in an equiv of op:get message on the wire that is of the form `<6'op:get< 11'desc:answer3"foo>3"baz52+>` syrup wise or something similiar <Zarutian_iPad>but the point is there is an op:get that takes an target, an selector, and what the answer_pos should be where the eventual thing will be and is pipeline invokable <Zarutian_iPad>btw this is pretty much same ?niche? of problem that GraphQL occupies <Zarutian_iPad>as I understand it Agoric is using E.get() to enable promise pipelinable access to minimize the need of .then()/E.when() for only getting a spefic suppart of an array or struct <Zarutian_iPad>mainly because there are quite a few structs used in Agoric code to bundle together various associated things. <KrisKowal[m]>There are indeed many record-style-objects with bits and pieces you might ignore, but the whole record is going to get transferred because it’s pass-style is “copy”, regardless of how you pick it apart. <KrisKowal[m]>E.get(record).field is much more useful because you can pass the resulting reference somewhere else immediately. <Zarutian_iPad>so, protocol wise then why have op:deliver messages with a verb selector all? <KrisKowal[m]>Suppose something where you’re passing the part to another party. <Zarutian_iPad>it isnt the picking apart that is the ?proof-of-value? it is the lack of awaiting, no? <KrisKowal[m]>Lacking await is not interesting unless you don’t have to await the result to get something interesting. <KrisKowal[m]>Like, if you had a remote method that created a controller and controllee pair, then you can E(alice).send(E.get(record).controller) and E(bob).send(E.get(record).controllee) without an await. <Zarutian_iPad>but back to the wire protocol question. Given an op:get then do we really need the verb part of op:deliver? <KrisKowal[m]>I would expect op:deliver to accept a verb of "get", not an op:get. But I have not read the work in detail.