What Color is Your Function? – journal.stuffwithstuff.com
Pangram verdict · v3.3
We believe that this document is fully human-written
AI likelihood · overall
HumanArticle text · 1,666 words · 5 segments analyzed
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. A new language#a-new-language 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. What color is your function?#what-color-is-your-function 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.
It’s functional programming’s fault!#its-functional-programmings-fault 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. A colorful allegory#a-colorful-allegory 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:
Synchronous functions return values, async ones do not and instead invoke callbacks.
Synchronous functions give their result as a return value, async functions give it by invoking a callback you pass to it.
You can’t call an async function from a synchronous one because you won’t be able to determine the result until the async one completes later.
Async functions don’t compose in expressions because of the callbacks, have different error-handling, and can’t be used with try/catch or inside a lot of other control flow statements.
Node’s whole shtick is that the core libs are all asynchronous. (Though they did dial that back and start adding ___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. I promise the future is better#i-promise-the-future-is-better 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.