Following some in-person chats on a number of concepts of functional programming, my team pushed me to try to share and present some of these to a wider audience. Admittedly, finding online resources on FP that are both palatable and reasonably sized is not always easy. This article was written in December 2015 and was my best attempt—in my own perspective and with my own analogies—to talk about what lies beyond the obscure term monad by starting with functors.
I was asked why one should prefer
Here’s an analogy: why do we, as a talking species, use different layers of vocabulary? Why do we have abstract terms in our language, as opposed to only concrete terms drawn from tangible things from our physical world? I mean, the Romans did fine without abstract language, right? Or were they limited by Latin? Wasn’t the incredible boom of philosophical thought during the Enlightenment facilitated by the abstraction powers of the German language?
“You might think, well that’s just common sense. But last I checked, computers don’t have common sense. Indeed, they must have a formal way to automate these kind of code optimizations. Maths has a way of formalizing the intuitive, which is helpful amidst the rigid terrain of computer logic.”
— Mostly Adequate Guide to Functional Programming on Wadler’s free theorems
I recently had a conversation at work on possible coding advice, that quickly diverged into a matter of what I feel is code that speaks for itself vs. code that is ridden with comments because it can’t live on its own.
I tried to illustrate my point by defending some practices on clarity, which really are just a repackaging of common ideas from the world of “functional” programming — adopting recognizable patterns via higher-level functions, aiming for declarative code, centered around data transformations, etc. — in contrast with what I’d consider traditional imperative code — longer methods built around statements, more (and explicit) branching, mutable data and variable reassignments, etc.
My colleague then rightfully remarked that, while
filter et al. can be used to make things clearer, they can be abused, and chaining a bunch of these can lead to monstrous spaghetti just as badly as more traditionally imperative code. Similarly, list comprehensions can be nice and concise, but don’t really scale. That got us to try to generalize, which lead to my commenting:
I agree that “functional” code, in itself, isn’t immune to spaghettitis. I think the key here is really to keep functions small and as strongly specified as possible, so that 1) one can easily inspect them and understand what piece they represent in the larger chain of data transformations, and 2) anyone can think of them as black boxes (by trusting their specification; not specific to functional, but usually easier, especially with referential transparency) as they are called within a chain of manipulations elsewhere in the code.
In a way, maybe the root of all evil is just about anything we can call a “long anonymous chain of things”. So a long sequence of function compositions in one statement is bad, the same way a long sequence of rules within a list comprehension is bad, the same way a long sequence of statements within a procedure is bad. I added the term “anonymous” in there because I think the naming plays an essential role in the way we can break things down and grasp them. That is why my initial functional example [see below] defined
isPopularseparately, and it’s why a procedure might be defined in “paragraphs”, where each revolves around a variable or a helper.
Functional programming combines the flexibility and power of abstract mathematics with the intuitive clarity of abstract mathematics.