Async/await, and coloring schemes in general

This is from a while back, but now I have a blog to put it on! I found these essays on async/await to be particularly helpful and relevant and understandable to me personally. They’re all internally linked, too — so, I’ll post them in the order that I read them (chronologically backwards), but you might alternatively read them in the order they were posted (bottom to top).

The concept of “function coloring” applies essentially everywhere we have a type system. So: functions in the Coroutines TS can be sync-colored or async-colored, but in C++ at large they can already be, for example, int-returning-colored or void-returning-colored. They can already be noexcept-colored or noexcept(false)-colored. (This causes no end of difficulty when trying to use std::vector, the one template in the language that cares whether its type parameter is noexcept-colored or not. Also, notice that the current efforts to introduce a lightweight error handling mechanism into C++, whether it’s the chaos of <filesystem> or Boost.Outcome or D0709, effectively introduce a third color in this particular dimension.) Compile-time-ness (constexpr) is a color.

Allocator-awareness might be considered a color. Heck, constness might reasonably be considered a color!

// grr, function colors are so annoying! Wish we didn't have them!
V& at(const K& key) { return some_complicated_expression; }
const V& at(const K& key) const { return const_cast<Self&>(*this).at(key); }

And in many places we lack a coloring scheme but really wish we had one. Memory-allocation comes to mind; so does locking. C++ is so abysmally desperate for a “thread-safety-colored” coloring scheme that we actually took and reused the “const-colored” scheme as our “thread-safety-colored” scheme, because it was just too painful to keep programming threads without some kind of coloring scheme!

I caricature Nystrom’s thesis as, “Function colors are bad because they make you think about colors all the time.” I caricature Benfield’s thesis as, “Yes, but they allow you to expose asynchrony to the programmer, so they are sometimes a good tradeoff, and you don’t need to use them if you don’t like them.” I caricature Smith’s thesis as, “No! They are the primary way in which we signal that an operation is blocking; they are an absolute good; you should not even attempt to avoid them!”

That is, one more place C++ lacks a coloring scheme is along the dimension of “blocking.” How many times have you called a function (let’s say localtime) thinking that it was going to be quick, but in fact observed that it was taking a global mutex lock — or even going all the way to disk sometimes (let’s say, to see whether your system timezones have been updated in the last few milliseconds)? Wouldn’t it be cool if C++ had a way to mark each function call as either blocking-colored or nonblocking-colored?

We can live in this world! All we need to do is enforce the rule that nonblocking-colored functions are not allowed to call blocking-colored functions. My interpretation of Smith’s thesis is that the async keyword gives us (almost?) exactly this coloring scheme.

Coloring schemes don’t stack

There is a problem with coloring schemes, though: it feels like they don’t compose very well. You can’t stack too many color-related syntaxes on top of one another before you’re writing things like

async std::optional<int> bar() throw;

int foo() noexcept {
    auto result = try (co_await bar()).value();
    return result;

(And thank you for asking, but it does not help if you allow me to write ?*(... bar()) instead, or whatever crazy punctuation your proposal co-opts.)

Prior work on ad-hoc coloring schemes

For an attempt to generalize the const (and volatile) “coloring scheme” to cover other dimensions, such as thread-safety and taint analysis, see the CQUAL project.

Completely off-topic

This blog post by Ted Unangst was an interesting read on “things you expect to block not blocking, and vice versa.”

Above I used the phrase “Yes, but…” (not to be confused with “Yes, and…”), which is also the title of a French movie about a variety of psychotherapy called “brief therapy.” Wikipedia’s article on that quotes a proponent as saying,

It’s easier to cure a phobia in ten minutes than in five years.

I’m not saying the blog posts linked above cured me — the Coroutines TS syntax is still ugly as sin — but I do like the idea of coloring my functions as “blocking” or “non-blocking.” At least in Python. At least in theory.

Posted 2018-03-16