@Richard Feldman
I'm not sure if you're familiar with Temporal, but they're sort of an existence proof that the "capture and replay side-effects" technique can work, even outside of functional languages. IIUC, when you write your workflow logic, in whatever language you want, each time you want to call another Temporal workflow/task, and each time you want, say, a random number or current time, you go through the Temporal SDK API. Then, if your workflow sleeps for 3 months, or needs to be retried, the calls to the SDK API instantly return the cached results and "skip ahead" to the next part of the workflow. Of course, you have to promise not to change your code enough to cause a different path to be taken (and Temporal will error out if SDK API calls happen in an unexpected sequence).
I also think that for the specific use-case of Roc shell/repl, it would be an interesting exercise to write a platform that offered transactions across I/O (where possible, of course): An example shape could be one that let you issue sqlite-like "savepoint"/"release", and did all I/O to an in-memory (or cached-on-disk) overlay that wouldn't get applied to the real files or real output, etc. until you commit. "savepoint"/"release" could use stacked overlays that merge down upon "release".
I hadn't heard of this, thanks for sharing it!
Just ran across restate.dev. They appear to be doing the same thing as Temporal, but perhaps a bit more rigorously, and more explicitly as captured continuations. The definitely capture previous callback/continuation results and replay them to skip forward through reinstantiated/unpaused tasks.
Last updated: Jul 06 2025 at 12:14 UTC