The “Great FP Terminology Barrier”
Hello, Rod Serling of The Twilight Zone here. Al will be back shortly, but for now, let me take you to another place and time … an alternate universe …
In this alternate universe you are born a few years earlier, and one day you find yourself writing some code. One week, you create a List
class, and then a few days after that you find yourself writing the same for
loops over and over again to iterate over list elements. Recognizing a pattern and also wanting to be DRY (“Don’t Repeat Yourself”), you create a cool new method on the List
class to replace those repetitive for
loops:
val xs = List(1, 2, 3).applyAFunctionToEveryElement(_ * 2)
You originally named this method, “apply a function to every element and return a value for each element,” but after deciding that was way too long for a function name, you shortened it to applyAFunctionToEveryElement
.
But the problem with this shorter name is that it’s not technically accurate. Because you are applying a function to each element and then returning the corresponding result for each element, you need a better name. But what name is accurate — and concise?
Pulling out your handy thesaurus, you come up with possible method names like these:
apply
convert
evolve
transform
transmute
metamorphose
As you try to settle on which of these names is best, your mathematics buddy peers over your shoulder and asks, “What are you doing?” After you explain what you’re working on, he says, “Oh, cool. In mathematics we call that sort of thing ‘map.’” Then he pats you on the back, wishes you luck, and goes back to doing whatever it is that mathematicians do.
While some of the names you’ve come up with are good, this brief talk with your friend makes you think that it might be good to be consistent with mathematics. After all, you want mathematicians and scientists to use your programming language, so you decide to name your new method map
:
val xs = List(1, 2, 3).map(_ * 2)
“Whoa,” you think to yourself, “that looks cool. I’ll bet there are zillions of functions that people can pass into map
to achieve all kinds of cool things. And then I can use phrases like ‘map over a list.’” Things are taking shape.
As you think about your invention, it occurs to you that there are at least a few different data types in the world that can be mapped over … not just lists, but hashmaps, too. Shoot, you can even think of a String
as a Seq[Char]
, and then even that can be mapped over. In time you realize that any collection whose elements can be iterated over can implement your new map
function.
As this thought hits you, you realize that a logical thing to do is to create a trait that declares a map
method. Then all of these other collections can extend that trait and implement their own map
methods. With this thought, you begin sketching a new trait:
trait ThingsThatCanBeMappedOver {
// extending classes need to implement this
def map[A, B](f: A => B): TODO[B]
}
You realize that the map
function signature isn’t quite right — you’re going to have to invent some other things to make this work — but never mind those details for now, you’re on a roll.
With that trait, you can now implement your List
class like this:
class List extends ThingsThatCanBeMappedOver {
...
}
As you write that first line of code you realize that the trait name ThingsThatCanBeMappedOver
isn’t quite right. It’s accurate, but a little long and perhaps unprofessional. You start to pull out your thesaurus again, but that act makes you think of your math buddy; what would he call this trait?
It occurs to you that he would be comfortable writing code like this:
class List extends Map {
...
}
and as a result, you decide to call your new trait Map
:
trait Map {
// extending classes need to implement this
def map[A, B](f: A => B): TODO[B]
}
There, that looks professional, and math-y like, too. Now you just have to figure out the correct function signature, and possibly implement a default method body.
Sadly, just at that moment, Rod Serling returns you to this version of planet Earth …
In this version of Earth’s history, someone beat you to the invention of “things that can be mapped over,” and for some reason — possibly because they had a mathematics background — they made this declaration:
“Things that can be mapped over shall be called … Functor.”
Huh?
History did not record whether the Ballmer Peak, caffeine, or other chemicals were involved in that decision.
In this book, when I use the phrase, “Functional Programming Terminology Barrier,” this is the sort of thing I’m referring to. If a normal human being had discovered this technique, they might have come up with a name like ThingsThatCanBeMappedOver
, but a mathematician discovered it and came up with the name, “Functor.”
Moral: A lot of FP terminology comes from mathematics. Don’t let it get you down.
As a few more examples of the terminology barrier I’m referring to, here are some other terms you’ll run into as you try to learn functional programming:
Term |
Definition |
---|---|
combinator |
Per the Haskell wiki, this has two meanings, but the common meaning is, “a style of organizing libraries centered around the idea of combining things.” This refers to being able to combine functions together like a Unix command pipeline, i.e., |
higher-order function |
A function that takes other functions as parameters, or whose result is a function. (docs.scala-lang.org) |
lambda |
Another word for “anonymous function.” |
As these examples show, when you get into FP you’ll start seeing new terminology, and oftentimes they aren’t terms that you need to know for other forms of programming. For instance, I taught Java and OOP classes for five years, and I didn’t know these words at that time. (As a reminder, my background is in aerospace engineering, not computer science.)
A common theme is that these terms generally come from mathematics fields like category theory. Personally, I like math, so this is good for me. When someone uses a term like “Combinatory Logic,” I think, “Oh, cool, what’s that? Is it something that can make me a better programmer?”
However, a bad thing about it is that it’s easy to get lost in the terminology. If you’ve ever been lost in a forest, the feeling is just like that.
As I write later in this book, I personally wasted a lot of time wondering, “What is currying? Why does everyone write about it so much?” That was a real waste of time.
I’ll say this more than once in this book: the best thing you can do to learn FP is to write code using only pure functions and immutable values, and see where that leads you. I believe that if you place those restrictions on yourself, you’d eventually come up with the same inventions that mathematicians have come up with — and you might have simpler names for all of the terms.
“Mathematicians have big, scary words like ‘identity’ and ‘associativity’ and ‘commutativity’ to talk about this stuff — it’s their shorthand.”
~ From the book, Coders at Work
The key point of this lesson is that there’s generally no need to worry about a lot of mathematical and FP jargon, especially when you’re first getting started. As I found out through my own experience, all this terminology does is create a learning barrier.
That being said, one good thing about terminology is that it lets us know that we’re all talking about the same thing. Therefore, I will introduce new terms as they naturally come up in the learning process. Which leads me to …
In the next lesson I’ll formally define the term, “Pure Function.” In this particular case — because I use the term so often throughout the remainder of the book, and it’s a foundation of functional programming — it will help your learning process if I take a few moments to clearly define that term now.
If for some reason you want to see a lot of FP terms at this point, Cake Solutions has a nice Dictionary of functional programming
Combinator on the Haskell Wiki
Combinatory logic on the Haskell Wiki
Combinator pattern on the Haskell Wiki
Higher-order functions on scala-lang.org
The Ballmer Peak
The book, Coders at Work