Structured concurrency and delimited continuations


Traditional stackful symmetric coroutines can be used to implement call/cc, by starting a new coroutine with a given function and yielding a closure to it which returns to the current coroutine & call point. Similarly, you can implement coroutines in terms of call/cc.

So the two concepts are equally powerful, though in practice implementations may have different performance characteristics and implementing callcc with coroutines might leak resources in some languages, and common callcc use can be harder to track than common coroutine uses for compilation.

Structured concurrency-coroutines like in Trio & Kotlin are more limited since they work within a specific scope. You can’t do manupulations that pull in the entire call stack. This reminds me a lot of delimited continuations.

Is it possible to implement structured concurrency with delimited continuations, and/or delimited continuations with structured coroutines?


I feel like the key idea in structured concurrency isn’t really about computational primitives, and is more about how you organize your computation to make it understandable by humans. In terms of pure computational power, you can actually use structured concurrency to implement unstructured concurrency. If your primitive is a Trio-style nursery, then create a nursery at the top of your program and stash it in a global variable so any code in the program can spawn tasks into it at any time. If your primitive is CSP-style “parallel composition”, then it’s also doable, just more awkward.

And I’m not as familiar with the exact details, but I’m pretty sure delimited continuations are at least as powerful as call/cc, and in practice more powerful, right? You can definitely use delimited continuations to implement both structured and unstructured concurrency.

So I get the intuition that there’s some similarity between why delimited continuations are preferred over call/cc, and why structured concurrency is preferred over unstructured concurrency. But I don’t think it’s going to be as simple as saying that structured concurrency = delimited continuations. It has to be a bit more subtle than that.


I wonder if this is still true in languages which enforce scoping in the type system rather than dynamically, like Rust’s scoped threads. (I mean, I suspect I’m the bigger Rust expert here so I should be the one to know, but I don’t…)

Probably. 'static is a valid lifetime, and leaking resources isn’t unsafe.