The Poetry of Function Naming

October 18, 2010

For nearly a quarter of a century, one of the responsibilities that I’ve taken most seriously is the shepherding of the design of Mathematica. Partly that has involved establishing foundational principles, and maintaining unity and consistency across the system. But at some point all the capabilities of Mathematica must get expressed in the individual built-in functions—like Table or NestList—that ultimately make up the system.

Each one of those functions encapsulates some piece of repeated computational work—often implemented by some deep tower of algorithms. And each one of those now 3000 or so functions requires a name.

We’re currently in the closing weeks of a (spectacular!) new version of Mathematica, and I spent part of last week doing final design reviews for some fascinating new areas of the system. And as part of those design reviews, we were confirming and tweaking some of the names we’re going to use for new functions.

The naming of functions is a strange and difficult art—a bit like an ultimately abstracted form of poetry. The goal is to take the concept and functionality of a function, and capture the essence of it in one, or two, or perhaps three, words (like Riffle, or DeleteCases, or FixedPointList)—chosen so that when someone sees those words, they immediately get the right idea about the function. In even the most succinct forms of ordinary poetry, you get at least a handful of words to communicate with. In function names, you typically get at most perhaps three.

With enough experience, it can sometimes be pretty easy to come up with that little gem of a name for a function. Sometimes it can even seem quite obvious as soon as one thinks about the function. But sometimes it can take immense amounts of time—wrestling with what can seem like an insoluble problem of packing everything one needs to say about a function into that one little name.

It’s an unforgiving and humbling activity. And the issue is almost always the same. The reason you can’t find a good name is because you don’t really understand with complete and ultimate clarity what the function does.

And sometimes that’s because the function really isn’t designed quite right. There’s something muddled about it, that has to be unmuddled before you’ll ever be able to find a good name.

It’s very satisfying, though, when you finally crack it. These days I’m usually working on design reviews with teams of people. And when we finally get the right name, everyone on the call (yes, it’s essentially always a phone call) immediately says “Oh yes, that’s it”. And we all feel a little stupid that we just spent an hour, or however long, just coming up with one or two words.

In ordinary human languages, new words typically develop by some form of natural selection. Usually a word will be introduced—perhaps at first as a phrase—by one person. And then it spreads, sometimes changing a bit, and either becomes popular enough to be widely understood and useful for general communication, or disappears.

But for a computer language the pattern is necessarily different. For once a function name—that corresponds to a “word” in the language—has been introduced, it must immediately be a full, permanent, element of the language. For programs will be written that contain that name, and they would all have to be found and updated if that name was changed. And indeed, in Mathematica, I am proud to say that in nearly a quarter of a century, very very few names have ever had to be changed—so that a program written for Mathematica 1.0 in 1988 can still be understood and executed today by the very latest version of Mathematica.

There is also another difference between words in human languages and function names in a computer language. In a human language, there is no ultimate, absolute, meaning defined for most words. Instead, the best we can do is—like in a dictionary—to define words by relating them to other words.

But in a computer language, each function name ultimately refers to a particular piece of functionality that is defined in an absolute way, and can be implemented by a specific precise program.

This doesn’t usually make it any easier to come up with function names, though. It just means that there’s a clearer notion of the “right name”: the name where a human has the best chance of correctly figuring out from it what the function does.

Function names are in a sense ultimate points of human-machine communication. They’re the places where all that internal computational activity has to be connected with something that humans can understand. When the functionality is simple there are pictorial and other alternatives. But when the functionality is diverse or sophisticated we don’t know any possibility other than to use language—and the linguistic construct of names for things.

The function names in Mathematica are ultimately based on English, and for the most part, they consist of ordinary English words. In ordinary natural human languages, it is possible to introduce a completely new word, and have it gradually gain popularity and understanding. But in the dynamics of computer languages—with their necessarily sudden introduction of new names—one has no choice but to leverage on people’s existing understanding of a human language, like English.

Still, when we come up with function names in Mathematica today, they are in a sense not based just on “raw English”. They also rely on the web of meaning that has developed through the several thousand other functions that already exist in Mathematica.

There are definite conventions about what particular kinds of names mean. (Functions that end in List generate lists; functions that begin with Image operate on images; functions that begin with Find involve some kind of searching; and so on.) There are ways that names tend to appear together in typical usage of the language. And there are definite conceptual frameworks—and metaphors—that have developed in the language and the system. (Nest refers to repeated function application; Flat refers to flattening of nested structures; Dynamic refers to dynamic interactivity; and so on.)

In ordinary human language, natural selection no doubt often favors words that follow certain patterns. Sometimes for example consistency may make words easier to remember; sometimes inconsistency makes them stand out more, and thereby easier to remember. But there is no grand plan to organize the words in a particular way: say to avoid having obscure meaning “take up” short words, or to make words easier to organize in a particular way.

But when we introduce function names in Mathematica we have both the ability—and, I think, the responsibility—to design everything. Of course, the development of Mathematica is incremental, and at any given time we can only foresee a certain amount of what will follow. But still, I take great pains to name every new function in the best possible way.

What are some of the criteria?

First, one must leverage on peoples’ existing knowledge and understanding. If there is a familiar name that’s already widely used, then if at all possible one must use it.

Of course, sometimes that name may only be familiar in some particular area. And it may be very short—perhaps a single letter—and incomprehensible without further context. And in that case, what we typically do in Mathematica is to burn into the name some kind of stylized context. (So, for example, the Fresnel integral S(x) has the name FresnelS in Mathematica.)

In building Mathematica, we’ve had the longstanding principle of always trying to make every function as general as possible—so that it is applicable to as wide a range of situations as possible. Sometimes, though, a function will have one particular, familiar, use. But if the name of the function reflects only that use, one is shortchanging the function. For without a more general name, people will never think to apply it in other cases. (So, for example, it’s List, not “vector”, and it’s Outer, not “outer product”.)

And indeed, one of the responsibilities of function naming is that it is the names of functions that to a large extent directly determine how people will think about a function. If they are led in a particular direction by the name, that will be the direction in which they will go in using the function.

And even the very “texture” of the name is important in getting people to think correctly about functions. A sophisticated function should have a sophisticated name (like DynamicModule or EventHandler). A straightforward, common, function should have a simple name (like Length or Total). A function that does a clear but unusual thing should have an unexpected name (like Thread or Through).

By now in Mathematica there are a great many precedents for how functions should be named. And we always try to follow these precedents whenever possible. First, because they often represent good solutions to the naming problems we’re now trying to solve. And second, because by following them one is maintaining a certain consistency that makes it easier for the system to grow, and for people to learn the system—and to guess about functionality they do not already know.

When one finds a good name for a function, one of the things that happens is that when people hear the name, they can successfully “unpack” it into a one-sentence description of what the function must do—often in effect just by using the name of the function as the main part of a sentence. And indeed, when we’re stuck in trying to find a good name for a function, I’ll often suggest that we try to write a sentence that describes what the function does—that we can perhaps use in the Documentation Center for the function, but then condense down into the nugget we need for the name itself.

One of the painful aspects of function naming is that however clever you are about it, it can never be perfect. I often claim that the only language that is perfectly consistent is the one that does nothing. As soon as there is actual functionality to represent, there are inevitably awkward cases and corners. For example, one wants to maintain consistent simplicity in naming in each area of the system. But then at the overlaps between these areas there are inconsistencies.

And sometimes one runs into limitations of English: there just isn’t any familiar word or phrase for a concept, perhaps because that concept is somehow new to our experience. And in such cases what one typically has to do—just like in natural language—is to come up with an analogy.

Some of the analogies and metaphors we consider start quite wild and outlandish. But eventually they become tamer—like Sow and Reap or Throw and Catch—and an important way to extend the linguistic base for names in Mathematica.

It might be nice if English—like Mathematica—had the feature that a particular word meant only a particular thing, or at least a class of things. But that is not how it works. A single word can act as different parts of speech, and can have wildly different meanings. Usually in actual English usage, one can disambiguate by context.

But in the tiny length of a single function name, one does not have that option. And quite often that means one has to reject some wonderful word just in order to avoid a possible misunderstanding from a different way it can be used in English. (So, for example, “Live” or “Active” can’t be candidates for Dynamic—they’re just too easy to misunderstand.)

If one is lucky, a thesaurus (these days in Wolfram|Alpha) will give one a word that captures the same concept but avoids the potential misunderstanding. But sometimes one has to rearrange the whole structure of the name to avoid the possibility of misunderstanding.

And yet, after all those judgment calls, after all that drilling to clarify precisely what a function does, one has to come to a conclusion: one has to settle on a definite name. That will represent the function—and all the work done to implement it—well. And that will serve as a permanent handle by which people can access some piece of functionality in Mathematica.

I have no idea now how much time I have spent over the past quarter century coming up with names in Mathematica. Each one encapsulates some idea, some creative concept—frozen in a tiny clump of words. Like little poems. Thousands of them.

15 comments. Show all »

I remember I was very surprised by introduction of Sow and Reap. But they are great namings I think, though I amuse myself by wondering whether they would be born somewhere not that agricultural as the state of Illinois.

Indeed, at Wolfram Research people spend hours and hours and hours over the proper choice of symbol names, and also the arguments they take, and how to handle special cases in the inputs and outputs, and what would be the most consistent when seen in comparison to the other symbol names. And I think (usually) a very good choice was made. If one compares the symbol names in M with the names used in classes (and their members) in the traditional languages (Java, C#, C++, Fortran, etc.) one can only discover that the names in M are still the most systematic and consistent. Some names and naming conventions in these traditional languages cause feelings in me that range from mild chuckles to outright hilarity. Some bad choices were made in M as well, but they are honest about it. All in all, one has to use a natural language for function/symbol names, and therein lies the problem. If you want to use exactly the right name for what it does, you are still limited to what the English language provides you.

Interesting that you compare naming with poetry. It immediately brought to mind the Paul Dirac anecdote:

Oppenheimer was working at Göttingen and the great mathematical physicist, Dirac, came to him one day and said: “Oppenheimer, they tell me you are writing poetry. I do not see how a man can work on the frontiers of physics and write a poetry at the same time. They are in opposition. In science you want to say something that nobody knew before, in words which everyone can understand. In poetry you are bound to say…something that everybody knows already in words that nobody can understand.