IRC channel logs
2023-06-05.log
back to list of logs
<Zarutian_iPad>ACTION is idly thinking about how to do ocapn over https POSTs for doing E() at val.town <dthompson>hey all, spritely has contributed 2 entries to the lisp game jam which just ended! <Zarutian_iPad>(direct translations as I can manage of the names into Icelandic if ya curious) <dthompson>I know many programming languages, but only one natural one. :) <Zarutian_iPad>I know two pretty well, one or two so and so, and quite the beginner in one natural and one constructed <humanetech><humanetech> "Ian Clarke mentioned that that..." <- I found that Ian caused a nasty split with the original freenet project and removed Spritely-related GH comment. <fr33domlover>Goblins: If an error occurs while running an actor behavior handler, then *all* direct synchronous calls ($) done in the handler are reverted and not applied? <juliana[m]>if any error occurs during a turn, none of the effects of that turn are applied. So, yes and then some, potentially <dthompson>fr33domlover: yup. synchronous calls are part of the same transaction. <fr33domlover>But if before the error, some async (<-) calls succeed, those aren't reverted, am I right? <Zarutian_iPad>nope, those invocations are discarded. That is they are not released at then end of a turn <fr33domlover>Also, is there any info about how you implemented the transactional heaps? I'm implementing actors in Haskell, intuitively I'd use the built-in STM library, but I'm curious to learn how Goblins is doing it :P <fr33domlover>Zarutian_iPad what if a $ invocation depends on a value coming from a <- invocation? <Zarutian_iPad>basically during an transaction eventual sends are accumulated and then added to revelant vats’ event queues iirc <Zarutian_iPad>to get a value from a $ invocation you need to (on … ) the promise returned by that $ invocation <dthompson>I double checked the goblins turn implementation and yeah, upon error any queued asyncs would be thrown out <juliana[m]>in the guile-goblins repo there is a directory called "pre-goblins" which is more-or-less the steps Christine followed porting Goblins from Racket to Guile. that's probably the closest thing currently available to a guide on implementing Goblins-esque OCaps. I think (trans)actormaps are shown in there <Zarutian_iPad>but if you are just passing the promise made by an eventual send (<-) to an immediate call ($) for code there to do further eventual sends targeting that promise then those eventual sends just accumulate in the transaction <dthompson>actors are stored in actormaps, which map actor references to their current behavior. <dthompson>at the base of this system is the whactormap, or "weak hash actormap". it's basically just a mutable hash table. <dthompson>when processing a message in goblins, we create a transactormap, a transactional actormap, that accumulates changes to actor behavior. if the turn is successful, those changes are committed to the whactormap. <dthompson>for messages to send outside of the vat, yes, but I wasn't going to cover that here. <dthompson>I could get into more detail about actormaps, but the main point is that we have a buffer where we accumulate changes during a turn. <dthompson>if the turn is successful, those changes are applied to the vat's "official" actormap. <dthompson>if an error occurs, the transaction is thrown out and nothing happens. <fr33domlover>dthompson: So if e.g. I press a key, and that leads to some actor's method being called, the entire thing resulting from that call - including async (<-) calls to other, even remote actors - is considered a single transaction? That either globally succeeds or it entirely discarded? Alternatively: What makes a turn start? <dthompson>not quite, things can partially succeed. transactionality is on the "turn" level. <dthompson>for example, if pressing a key notified two GUI widgets, let's say, and one of them had trouble with the input and threw an error, the other one would still update. <Zarutian_iPad>idea, for ocapn and such machinery: allow it to know a successful turn has finished? To better pack such eventual send invocations into say DTN bundle application data units. <dthompson>the point of the transaction is that we can guarantee consistency for units of code that run sequentially. <dthompson>yes, that is the context in which code can run sequentially. <Zarutian_iPad>fr33domlover: a turn starts when a vat takes one event of its event queue and proccesses it <dthompson>the remove-entity procedure updates 3 cells, which are actors. <Zarutian_iPad>such an event can be an eventual send that was added due to either internal or external event <dthompson>if the code successfully updated 2 of the cells but failed to update the third, the system would be left in an inconsistent state. <dthompson>but thanks to transactions, if there were an error here all of the state changes would be rolled back. <fr33domlover>So these transactions are a bit like SQL DB transactions, I suppose <dthompson>that code needs to be run sequentially to ensure consistent app state <dthompson>really trying to stay out of the weeds of the implementation details <Zarutian_iPad>the main chalange and trick is to write apps that relie as little as possible on having large state consistant. <fr33domlover>I have more clarity now but I still need to wrap my head around how it works with promise pipelining :P <dthompson>I guess my brain completely separates these things. <fr33domlover>If the listener passed to `on` does some $-calls, that's a different transaction that will happen when the promise is resolved? <Zarutian_iPad>and beware the state assumptions you made might have been broken in the intrum <fr33domlover>Zarutian_iPad, dthompson: But `on` doesn't yet send the message, right? It waits for the current turn to succeed and only then queues it? <dthompson>'on' results in a listen request for the promise. <Zarutian_iPad>it just, eh, awaits an promise resolution just like .then() in js land does <fr33domlover>Hmm then I mean does <- wait for the end of the turn to send the message? <dthompson>when the message is dispatched is an implementation detail <dthompson>haven't thought about this too deeply but I don't think pipelining relies on this <dthompson>it's more of an implementation strategy to do as much local work as possible and then dispatch what needs to happen somewhere else <dthompson>conceptually I still think of those messages as having happened at that point. in fact our debugger thinks so, too. ;) <Zarutian_iPad>the promise pipelining is basically that you can eventual send onto promises made by other eventual sends and do not need them to resolve first <Zarutian_iPad>that these eventual sends havent yet been sent is akin to writing a letter and not have posted it yet <dthompson>just had this though: promise pipelining is the tail call of ocaps <dthompson>tail calls are the dark souls of programming <Zarutian_iPad>why? tail calls just reuse the topmost callstack frame therefore enabling certain recursive functions to take same runtime space as loops <fr33domlover>Thank you Zarutian_iPad and dthompson for answering my many questions ^_^ <dthompson>I was just thinking about how tail calls are both an optimization and important to understand as a user of the language <dthompson>promise pipelining is in a similar space, at least in my mind palace. <Zarutian_iPad>whilist promise pipelineing makes it possible to fob off the work onto another vat (the same future on is diffrent from the current lazy one) <dthompson>like they are both optimizations that have consequences for the language in which you express your intentions. <Zarutian_iPad>btw having support to invoke ?standard? methods on base datums like numbers, lists, strings, etc. can enable fun ways to get another distant vat to do most of the work that is depending on data in that distant vat. <dthompson>fr33domlover: yw for the help. hope it was actually helpful to you! :) <humanetech><Zarutian_iPad> "and? why ya bringing this up?" <- I mentioned Spritely to them, and freenet here. It was just a FYI that I deleted reference to spritely. <fr33domlover>Does $ send the message and then block until the message is handled and a result is sent back? <juliana[m]>that's an interesting way to word it... $ is just a wrapper around a normal, synchronous function call <juliana[m]>conceptually in the actor model i suppose that's sending a message and receiving a result, but the classic actor model doesn't have synchronous invocation at all <juliana[m]>to build on the previous answer in the hopes of avoiding confusion ;) <fr33domlover>juliana[m]: But how does a regular function call work, considering the actor might also be receiving messages? <fr33domlover>I mean, it's possible, but it means concurrent write attempts to the actor's state <dthompson>fr33domlover: $ only works on "near" objects, which means objects in the same vat. <dthompson>a vat processes a queue of messages, one at a time. <fr33domlover>dthompson: So $ inserts the message, waits for the vat to process it, and then returns the result? (a.k.a blocking) <dthompson>there's no queuing at all. it send the arguments to the actor immediately and returns the result. just like a regular function call. <fr33domlover>I'm confused :p why is there no queuing? How does $ send the arguments to the actor? Via the vat event handler queue? Or does it somehow bypass it? <dthompson>($ foo 1 2 3) is essentially a hash table lookup to find the behavior (a procedure) for 'foo' and then apply the arguments '1 2 3' to it. <dthompson>a message is never queued for later processing. the object's behavior is invoked directly and immediately. <fr33domlover>dthompson: What if the `foo` actor is busy executing a transaction, responding to a message it has received? Does $ still somehow run in parallel? Or does it wait for `foo` to finish the turn? <fr33domlover>dthompson: Why not? What if I passed a reference to `foo` to some remote actor earlier, and now this actor is sending messages to `foo` AKA calling its methods? <dthompson>how familiar are you with event loops, in general? <dthompson>this page of our docs explains the vat model <dthompson>basically: an object in a vat will *never* execute actor behavior in parallel. there are no multi-threading issues to worry about there. a vat is a single-threaded event loop. <dthompson>this property is why synchronous invocation of near actors via $ can happen in the first place. <fr33domlover>Hmm so $ ignores the queue, and calls the behavior function instantly? <dthompson>yes. all $ calls happen as part of the same "turn" <dthompson>a "turn" is the processing of a message in the vat's queue