Raw capture â raw/what-color-is-your-function.md
url: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ date_fetched: 2026-08-08
I donât know about you, but nothing gets me going in the morning quite like a good old fashioned programming language rant. It stirs the blood to see someone skewer one of those âblubâ languages the plebians use, muddling through their day with it between furtive visits to StackOverflow.
(Meanwhile, you and I, only use the most enlightened of languages. Chisel-sharp tools designed for the manicured hands of expert craftspersons such as ourselves.)
Of course, as the author of said screed, I run a risk. The language I mock could be one you like! Without realizing it, I could have let the rabble into my blog, pitchforks and torches at the ready, and my fool-hardy pamphlet could draw their ire!
To protect myself from the heat of those flames, and to avoid offending your possibly delicate sensibilities, instead, Iâll rant about a language I just made up. A strawman whose sole purpose is to be set aflame.
I know, this seems pointless right? Trust me, by the end, weâll see whose face (or faces!) have been painted on his straw noggin.
Learning an entire new (crappy) language just for a blog post is a tall order,
so letâs say itâs mostly similar to one you and I already know. Weâll say it has
syntax sorta like JS. Curly braces and semicolons. if, while, etc. The
lingua franca of the programming grotto.
Iâm picking JS not because thatâs what this post is about. Itâs just that itâs the language you, statistical representation of the average reader, are most likely to be able grok. VoilĂ :
function thisIsAFunction() { return "It's awesome"; }
Because our strawman is a modern (shitty) language, we also have first-class functions. So you can make something like this:
// Return a list containing all of the elements in collection // that match predicate. function filter(collection, predicate) { var result = []; for (var i = 0; i < collection.length; i++) { if (predicate(collection[i])) result.push(collection[i]); } return result; }
This is one of those higher-order functions, and, like the name implies, they are classy as all get out and super useful. Youâre probably used to them for mucking around with collections, but once you internalize the concept, you start using them damn near everywhere.
Maybe in your testing framework:
describe("An apple", function() { it("ain't no orange", function() { expect("Apple").not.toBe("Orange"); }); });
Or when you need to parse some data:
tokens.match(Token.LEFT_BRACKET, function(token) { // Parse a list literal... tokens.consume(Token.RIGHT_BRACKET); });
So you go to town and write all sorts of awesome reusable libraries and applications passing around functions, calling functions, returning functions. Functapalooza.
Except wait. Hereâs where our language gets screwy. It has this one peculiar feature:
1. Every function has a color.
Each functionâanonymous callback or regular named oneâis either red or
blue. Instead of a single function keyword, there are two:
blue_function doSomethingAzure() { // This is a blue function... } red_function doSomethingCarnelian() { // This is a red function... }
There are no colorless functions in the language. Want to make a function? Gotta pick a color. Themâs the rules. And, actually, there are a couple more rules you have to follow too:
2. The way you call a function depends on its color.
Imagine a âblue callâ syntax and a âred callâ syntax. Something like:
doSomethingAzure()blue; doSomethingCarnelian()red;
When calling a function, you need to use the call that corresponds to its color.
If you get it wrongâcall a red function with blue after the parentheses
or vice versaâit does something bad. Dredge up some long-forgotten
nightmare from your childhood like a clown with snakes for arms hiding under
your bed. That jumps out of your monitor and sucks out your vitreous humour.
Annoying rule, right? Oh, and one more:
3. You can only call a red function from within another red function.
You can call a blue function from within a red one. This is kosher:
red_function doSomethingCarnelian() { doSomethingAzure()blue; }
But you canât go the other way. If you try to do this:
blue_function doSomethingAzure() { doSomethingCarnelian()red; }
Well, youâre gonna get a visit from old Spidermouth the Night Clown.
This makes writing higher-order functions like our filter() example trickier.
We have to pick a color for it and that affects the colors of the functions
weâre allowed to pass to it. The obvious solution is to make filter() red.
That way, it can take either red or blue functions and call them. But then we
run into the next itchy spot in the hairshirt that is this language:
4. Red functions are more painful to call.
For now, I wonât precisely define âpainfulâ, but just imagine that the programmer has to jump through some kind of annoying hoops every time they call a red function. Maybe itâs really verbose, or maybe you canât do it inside certain kinds of statements. Maybe you can only call them on line numbers that are prime.
What matters is that if you decide to make a function red, everyone using your API will want to spit in your coffee and/or deposit some even less savory fluids in it.
The obvious solution then is to never use red functions. Just make everything blue and youâre back to the sane world where all functions have the same color, which is equivalent to them all having no color, which is equivalent to our language not being entirely stupid.
Alas, the sadistic language designersâand we all know all programming language designers are sadists, donât we?âjabbed one final thorn in our side:
5. Some core library functions are red.
There are some functions built in to the platform, functions that we need to use, that we are unable to write ourselves, that only come in red. At this point, a reasonable person might think the language hates us.
You might be thinking that the problem here is weâre trying to use higher-order functions. If we just stop flouncing around in all of that functional frippery and write normal blue collar first-order functions like God intended, weâd spare ourselves all the heartache.
If we only call blue functions, make our function blue. Otherwise, make it red. As long as we never make functions that accept functions, we donât have to worry about trying to be âpolymorphic over function colorâ (âpolychromaticâ?) or any nonsense like that.
But, alas, higher order functions are just one example. This problem is pervasive any time we want to break our program down into separate functions that get reused.
For example, letâs say we have a nice little blob of code that, I donât know, implements Dijkstraâs algorithm over a graph representing how much your social network are crushing on each other. (I spent way too long trying to decide what such a result would even represent. Transitive undesirability?)
Later, you end up needing to use this same blob of code somewhere else. You do the natural thing and hoist it out into a separate function. You call it from the old place and your new code that uses it. But what color should it be? Obviously, youâll make it blue if you can, but what if it uses one of those nasty red-only core library functions?
What if the new place you want to call it is blue? Youâll have to turn it red. Then youâll have to turn the function that calls it red. Ugh. No matter what, youâll have to think about color constantly. It will be the sand in your swimsuit on the beach vacation of development.
Of course, Iâm not really talking about color here, am I? Itâs an allegory, a literary trick. The Sneetches isnât about stars on bellies, itâs about race. By now, you may have an inkling of what color actually represents. If not, hereâs the big reveal:
Red functions are asynchronous ones.
If youâre programming in JavaScript on Node.js, everytime you define a function that âreturnsâ a value by invoking a callback, you just made a red function. Look back at that list of rules and see how my metaphor stacks up:
try/catchor inside a lot of other control flow statements.___Sync()versions of a lot of things.)When people talk about âcallback hellâ theyâre talking about how annoying it is to have red functions in their language. When they create 4,089 libraries for doing asynchronous programming, theyâre trying to cope at the library level with a problem that the language foisted onto them.
Update 2021/12/03: 15,118 async libraries as of today.
People in the Node community have realized that callbacks are a pain for a long time, and have looked around for solutions. One technique that gets a bunch of people excited is promises, which you may also know by their rapper name âfuturesâ.
These are sort of a jacked up wrapper around a callback and an error handler. If you think of passing a callback and errorback to a function as a concept, a promise is basically a reification of that idea. Itâs a first-class object that represents an asynchronous operation.
I just jammed a bunch of fancy PL language in that paragraph so it probably sounds like a sweet deal, but itâs basically snake oil. Promises do make async code a little easier to write. They compose a bit better, so rule #4 isnât quite so onerous.
But, honestly, itâs like the difference between being punched in the gut versus being punched in the privates. Technically less painful, yes, but I donât think anyone should really get thrilled about the value proposition.
You still canât use them with exception handling or other control flow statements. You still canât call a function that returns a future from synchronous code. (Well, you can, but if you do, the person who later maintains your code will invent a time machine, travel back in time to the moment that you did this and stab you in the face with a #2 pencil.)
Youâve still divided your entire world into asynchronous and synchronous halves and all of the misery that entails. So, even if your language features promises or futures, its face looks an awful lot like the one on my strawman.
(Yes, that means even Dart, the language I work on. Thatâs why Iâm so excited some of the team are experimenting with other concurrency models.)
C# programmers are probably feeling pretty smug right now (a condition theyâve
increasingly fallen prey to as Hejlsberg and company have piled sweet feature
after sweet feature into the language). In C#, you can use the await
keyword to invoke an
asynchronous function.
This lets you make asynchronous calls just as easily as you can synchronous
ones, with the tiny addition of a cute little keyword. You can nest await
calls in expressions, use them in exception handling code, stuff them inside
control flow. Go nuts. Make it rain await calls like a theyâre dollars in the
advance you got for your new rap album.
Async-await is nice, which is why weâre adding it to Dart. It makes it a lot easier to write asynchronous code. You know a âbutâ is coming. It is. But⌠you still have divided the world in two. Those async functions are easier to write, but theyâre still async functions.
Youâve still got two colors. Async-await solves annoying rule #4: they make red functions not much worse to call than blue ones. But all of the other rules are still there:
Task<T>(orFuture<T>in Dart) wrappers around the value.await.T. You canât unwrap it unless you makeyourfunction async and await it. (But see below.)await, we did at least fix this.It is better. I will take async-await over bare callbacks or futures any day of the week. But weâre lying to ourselves if we think all of our troubles are gone. As soon as you start trying to write higher-order functions, or reuse code, youâre right back to realizing color is still there, bleeding all over your codebase.
So JS, Dart, C#, and Python have this problem. CoffeeScript and most other languages that compile to JS do too (which is why Dart inherited it). I think even ClojureScript has this issue even though theyâve tried really hard to push against it with their core.async stuff.
Wanna know one that doesnât? Java. I know right? How often do you get to say, âYeah, Java is the one that really does this right.â? But there you go. In their defense, they are actively trying to correct this oversight by moving to futures and async IO. Itâs like a race to the bottom.
C# also actually can avoid this problem too. They opted in to having color.
Before they added async-await and all of the Task<T> stuff, you just used
regular sync API calls. Three more languages that donât have this problem: Go,
Lua, and Ruby.
Any guess what they have in common?
Threads. Or, more precisely: multiple independent callstacks that can be switched between. It isnât strictly necessary for them to be operating system threads. Goroutines in Go, coroutines in Lua, and fibers in Ruby are perfectly adequate.
(Thatâs why C# has that little caveat. You can avoid the pain of async in C# by using threads.)
The fundamental problem is âHow do you pick up where you left off when an operation completesâ? Youâve built up some big callstack and then you call some IO operation. For performance, that operation uses the operating systemâs underlying asynchronous API. You cannot wait for it to complete because it wonât. You have to return all the way back to your languageâs event loop and give the OS some time to spin before it will be done.
Once operation completes, you need to resume what you were doing. The usual way a language âremembers where it isâ is the callstack. That tracks all of the functions that are currently being invoked and where the instruction pointer is in each one.
But to do async IO, you have to unwind and discard the entire C callstack. Kind of a Catch-22. You can do super fast IO, you just canât do anything with the result! Every language that has async IO in its coreâor in the case of JS, the browserâs event loopâcopes with this in some way.
Node with its ever-marching-to-the-right callbacks stuffs all of those callframes in closures. When you do:
function makeSundae(callback) { scoopIceCream(function (iceCream) { warmUpCaramel(function (caramel) { callback(pourOnIceCream(iceCream, caramel)); }); }); }
Each of those function expressions closes over all of its surrounding
context. That moves parameters like iceCream and caramel off the callstack
and onto the heap. When the outer function returns and the callstack is
trashed, itâs cool. That data is still floating around the heap.
The problem is you have to manually reify every damn one of these steps. Thereâs actually a name for this transformation: continuation-passing style. It was invented by language hackers in the 70s as an intermediate representation to use in the internals of their compilers. Itâs a really bizarro way to represent code that happens to make some compiler optimizations easier to do.
No one ever for a second thought that a programmer would write actual code like that. And then Node came along and all of the sudden here we are pretending to be compiler backends. Where did we go wrong?
Note that promises and futures donât actually buy you anything, either. If
youâve used them, you know youâre still hand-creating giant piles of function
literals. Youâre just passing them to .then() instead of to the asynchronous
function itself.
Async-await does help. If you peel back your compilerâs skull and see what
itâs doing when it hits an await call youâd see it actually doing the
CPS-transform. Thatâs why you need to use await in C#: itâs a clue to the
compiler to say, âbreak the function in half hereâ. Everything after the await
gets hoisted into a new function that the compiler synthesizes on your behalf.
This is why async-await didnât need any runtime support in the .NET framework. The compiler compiles it away to a series of chained closures that it can already handle. (Interestingly, closures themselves also donât need runtime support. They get compiled to anonymous classes. In C#, closures really are a poor manâs objects.)
You might be wondering when Iâm going to bring up generators. Does your
language have a yield keyword? Then it can do something very similar.
(In fact, I believe generators and async-await are isomorphic. Iâve got a bit of code floating around in some dark corner of my hard disc that implements a generator-style game loop using only async-await.)
Where was I? Oh, right. So with callbacks, promises, async-await, and generators, you ultimately end up taking your asynchronous function and smearing it out into a bunch of closures that live over in the heap.
Your function passes the outermost one into the runtime. When the event loop or IO operation is done, it invokes that function and you pick up where you left off. But that means everything above you also has to return. You still have to unwind the whole stack.
This is where the âred functions can only be called by red functionsâ rule
comes from. You have to closurify the entire callstack all the way back to
main() or the event handler.
But if you have threads (green- or OS-level), you donât need to do that. You can just suspend the entire thread and hop straight back to the OS or event loop without having to return from all of those functions.
Go is the language that does this most beautifully in my opinion. As soon as you do any IO operation, it just parks that goroutine and resumes any other ones that arenât blocked on IO.
If you look at the IO operations in the standard library, they seem synchronous. In other words, they just do work and then return a result when they are done. But itâs not that theyâre synchronous in the sense that it would mean in JavaScript. Other Go code can run while one of these operations is pending. Itâs that Go has eliminated the distinction between synchronous and asynchronous code.
Concurrency in Go is a facet of how you choose to model your program, and not a color seared into each function in the standard library. This means all of the pain of the five rules I mentioned above is completely and totally eliminated.
So, the next time you start telling me about some new hot language and how awesome its concurrency story is because it has asynchronous APIs, now youâll know why I start grinding my teeth. Because it means youâre right back to red functions and blue ones.