I share the author's enthusiasm for coroutines. They're nice abstractions for all sorts of state-machine-like code and for concurrency (without parallelism).
> You could allocate a piece of memory for a coroutine stack; let the coroutines on it push and pop stack frames like ordinary function calls; and have a special ‘yield’ function that swaps out the stack pointer and switches over to executing on another stack. In fact, that’s not a bad way to add coroutines to a language that doesn’t already have them, because it doesn’t need the compiler to have any special knowledge of what’s going on. You could add coroutines to C in this way if you wanted to, and the approach would have several advantages over my preprocessor system.
In C minicoro is a nice library that provides just that: https://github.com/edubart/minicoro
In Zig there's zigcoro: https://github.com/rsepassi/zigcoro
Another source I found enlightening on coroutines is "Coroutines in Lua": https://www.lua.org/doc/jucs04.pdf
Lua's stackful coroutines are awesome! The Lua C API even allows to pass a continuation function when calling a Lua function from C, so you can yield across the C-call boundary (e.g. a Lua function, calling a C function, calling a Lua function that yields). See also https://www.lua.org/manual/5.2/manual.html#4.7
I'd add that while I was already familiar with coroutines from a few different contexts and languages, I found this particular framing of them -- especially seeing them contrasted side by side with a state-machine -- enlightening and novel.
We implemented a low-power wireless sensing device on a microcontroller using asynchronous coroutines in C to replace eight state machines. It was a dream. Every operation process was clearly "linear" in readability while overlapping radio frequency hopping, sensor power-up/down sequencing, sense state, transmission, firmware update, etc. All while going into low-power mode until the next event to process.
> and for concurrency (without parallelism)
They are pretty good as a task abstraction with parallelism as well.
I’ve been fascinated by coroutine code for ages – this was a great read.
For one of the best work projects I’ve done, I used Redux-Saga. It was so elegant for a heavily live-updating ui app. Using coroutines for making API requests, watching sockets, sending and receiving updates, and reacting to data conflicts felt so natural and all just came together so beautifully.
The term he uses for “turning functions inside out” is spot on for how they feel. In particular, the way a good coroutine only focuses on the pure state, while being independent of side effects and even time itself really expanded my view of code. It made testing and reasoning about logic so much easier too. It’s a model I really wish was more widespread in languages and the industry.
Something which bothers me about coroutines is the pure boilerplate -- I don't even mean the ridiculous verbose coroutine definitions in C++20 but just the spread of "await" and "async" keywords all over the code, for instance in Python and Rust. I understand that these keywords are the compromise to introduce the feature into the language without paying for it when not using it, but I wonder if there is not a more concise way. Something not mentioned at all in the particle is the "what color is your function" problem (https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...) where I wonder whether there is a good solution.
Those aren't actual symmetrical coroutines though. Try the raw greenlet python module. A coroutine can yield to an arbitrary coroutine and not just to the event loop.
Recently updated: [Simon Tatham, initial version 2023-09-01, last updated 2025-03-25]
> For example, here’s a tiny Python generator function that I put in a lot of my programs, because I often find I want to use it, and despite being so simple, it’s not in the standard library:
itertools.pairwise[0] comes pretty close.
[0] https://docs.python.org/3/library/itertools.html#itertools.p...
I really don't like writing state machines by hand... So...
I was writing a web-based text adventure game, using async / await on the server...
I was writing a SDML text adventure game, with some graphics, using async / await on the control side...
Then I finally realized that since all of my commands are like Console WriteLine and Console ReadLine... that I could just do all of my logic in a thread of its own. And it stores UI state in a object... and uses two AutoResetEvents to keep track of which thread is in control.
I guess those are coroutines. One for logic and one for UI.
Makes my logic code as simple as could be, with no async / await or any other garbage. (I handle a couple top-level exceptions for closing connections or closing the app.)
And it's also trivial to test my code on an actual Console, before worrying about setting up my web server or my GUI environment, which has a bit of latency to it.
If you write a text adventure, use Inform6 if you like OOP progamming or Inform7 if you want... something else, declaratively. Both can compile to a Z8 game which can run everywhere, from 16 bit computers and up.
I'm one of those idiots who would rather write an engine than a game.
I'm cribbing from LambdaMOO and Inform 6.
As for opening the ZMachine games (V3/5/8), you know, from Zork and friends... there's Winfrotz on Windows, Lectrote on Android/OSX whatever, and Frotz for Linux/BSD diehard fans. Oh, and Frotz for Amiga, Atari, DOS, old Macs...)
That's why I suggested you Inform6. Even with Inform7, a Z8 machine game (look up Anchorhead anchor.z8 and Inside Woman woman.z8 under a search engine) they will look and play well on really, really old computers. Download and open them in order to see what the ZMachine and Inform are able to do with very little constraints.
The 1st game it's a Lovecraftian tale set in the 90's (hello Stephen King), the second one a futuristic/half-cyberpunk/dystopian one.
And is not the best one I've seen. They are jewels like Spider and Web (tangle.z5), a techno-triller which is a bit like Memento/Bourne and similar movies.
Again, these are playable from a current powerhouse computer to a DOS one from 1993. And they are far better than the guess-the-word games from the 80's, the community improved the Infocom's ZMachine to really easy levels.
In order to start with Inform6, I suggest you to learn with "Inform's Begginers' Guide', a free to download book. For advanced stuff, there's the Inform Designed manual, but that's for really low level stuff. Such as declaring really fancy grammars or even a new one for languages like Italian to create parsers for that language, for instance (as we already have a library for Spanish games). But it's good to have (dm4.pdf) in order to debug issues.
Have fun.