Three definitions of Expressivity

Thanks to Richard Feldman for this idea.

Programmers use the term expressivity a lot. Unfortunately, there's not one right definition of the term. Instead, it's a loose term that is used in many different ways. Let's go over three ways I know that programmers use expressivity.

The first definition is kind of the baseline: it's Turing Completeness. It means you can compute anything that can be computed. That's not very interesting. Any general purpose language can do that. It doesn't really help. Anyone who makes an appeal to language equivalence based on Turing Completeness is either unaware of what it's like to program in very different languages (say, C and Prolog) or is being disingenuous.

Okay, let's talk about a second definition. We could say expressivity is what a programming language lets you do. Does it let you write to that file? Does it let you get the job done? Does it let you write GOTO? Does it let you manage your own memory? Does it let you take the reins and write unsafe code? Just look at the broad spectrum of what a language lets you do. That's its expressivity. It's what the language lets you express. This definition is one used in the world of type systems and static analysis (among other places).

People will say that a type system limits the expressivity to that which is type safe. For example, in an untyped language, you can write 1 + "hello". What it does is up to the language. But the untyped language lets you write it. Most type systems would not allow that kind of expression. They're limiting the expressivity. The trick is to limit expressivity in useful ways, like programs that would result in errors, and not to limit too much of what people want to do. Otherwise, the language stops being useful.

Or look at a linter. The linter is a static analysis tool that limits the expressivity of your language. For example, a linter may identify unused imports and warn you about them.

People will also say type systems increase the expressivity in that they give you the ability to express type constraints, which is not possible in untyped languages.

In general, I think this is a very practical and objective definition.

Alright, now for a third definition, which will be more subjective. We could say that expressivity is the dual of readability (perhaps synonymous with writeability). How easy is it to program in? For example, if I write two programs that parse JSON, one in Haskell and another in C++, I will probably find that the C++ is kind of a mess, while the Haskell one is nice and clean. In C++ I have to deal with so many extraneous details like deconstruction and pointer management.

However, notice that it depends on the problem. If the problem was one of low-level memory management and pointer chasing, like writing a garbage collector, then C++ might be exactly what I need. I'm not sure I would even know how to manage pointers in Haskell.

Either way, this definition of expressivity has a lot to do with how easy a problem is to solve within the semantics the language gives you. Notice that this definition is relative to the context of a particular problem and is quite subjective. It's a feeling. It's hard to find an objective measure of how hard a program is to write. We can only find proxies, like the count of the lines of code, the time it takes, or the number of bugs that are later found.

However, this idea of expressivity is also very important. People often direct their entire careers based on the feeling of expressivity of a certain language. David Hannemeir Hansen talks about elegance when explaining why he chose Ruby. And Gene Kim explains that Clojure gave him the joy and confidence to call himself a developer.

Technical terms with multiple definitions bother me. I wish I could reform the world to agree on a definition. But I don't have that power. It turns out many words we use every day have multiple definitions. Just look in the dictionary! Multiple definitions are a reality of life.

I've learned to face reality by learning all of the definitions I can find for a term, then trying to identify the meaning someone intends while they're talking. In general, I find it doesn't hurt discussion or reasoning if you consistently stick to one definition.

But as soon as someone switches, that's when there's a problem. And often a disagreement is merely people using different definitions for the same term. They're talking about different things entirely!

So that's why I take the time to look up these definitions and document them, especially for terms that come up frequently and occur in heated discussions. And if I can't get agreement on a definition, at least I can equip myself and others with a map of the landscape.