On the proliferation of try (and, soon, await)

Just wanted to post some new insight from a project I'm working on. For various reasons, in this project, most of the code I have needs to be executed step-wise, in tiny increments, but with no parallelism. I actually have an execution object on which a mutating step() method is called repeatedly. You could easily imagine doing this with multiple execution objects in round-robin fashion to get a kind of concurrency-without-parallelism. It's classic inversion of control.

After trying some alternatives, I implemented it in continuation-passing style with a trampoline, which ends up introducing a lot of complexity and, of course, the classic pyramid-of-doom.

I would actually love to try using async/await for this project, but IIUC the parts are not yet available to use async without creating parallelism (I would love to be wrong about that, and if I am, I very much hope someone will correct me!)

In any case, it seems like almost a perfect application for async/await, and it would undeniably be an improvement over CPS. That said, because of the tiny increments I mentioned above, the code I'm trying to clean up would surely be littered with unhelpful awaits. For the purposes of this project, the fact that a function is going to be executed step-wise is something that needs to be said at most once, and the precise step divisions don't much matter.

Because I'm in CPS-land I've been thinking a lot about functional programming idioms and how monads with “do” notation would support the same kind of code transformation as I'd get with async/await, but with only one marking on the whole function where the transformation is applied, rather than a marking at each possible suspension point… which is what this thread is about.

fin.

4 Likes