What is Functional Programming?
What is Functional Programming (FP)? I didn’t have an answer I was happy with for a long time. Over the years I discovered that it meant different things to different people, but there was a clear common thread, around which more sophisticated ideas revolved. So I asked colleagues and folks at the Recurse Center what it meant for them. I got lots of different answers. Some of which surprised me. At the time I settled on “FP is about functions, their inputs, and their outputs”. That’s it. Functions consume their inputs and produce an output. That’s all a Functional Programming Language lets you do. Specifically, there is no extraneous data. Only input and output.
The problem is it’s hard to see how a bigger, more substantial program, is coded that way. I’d liken it to saying Evolution is about random mutation. Yes, that’s part of it, but not the whole story. So much so that it misses the forest for the trees. Over long stretches of time populations have members with a mutation, some survive, some don’t and if that mutation helped members survive then it’s more likely to live on as descendants inherit that mutation. That’s a much better description of Evolution. That’s what I was looking for when it came to FP. A macroscopic definition or description that fit in with my day to day experience.
Then, back in 2020, I came across this Game of Life in JS.
If you’re like me, over the years you’ve seen a few examples like these in Go, Smalltalk (last page), and APL.
I thought the JS code was amazing!
I’d never seriously considered a set of (x,y)
points (living cells) as a representation for Life.
I had always thought of it, and coded it, as a matrix of some sort. 1
Here’s the JS code:
I got to work reproducing it in Elixir, Phoenix LiveView, and SVG. I started out from scratch, but I couldn’t get it as neat as the JS code. So, I copied it into my editor and turned it into Elixir code, construct by construct, so that it resembled the JS original syntactically as much as possible. Once I had it working I started to rework it into something that felt more like idiomatic functional code in Elixir (to be honest - that first working copy was pretty good). I’d have never thought it a worthwhile exercise to translate code, construct by construct, from one style to another, but it was. I reworked the code and a different program unravelled.
I saw that what was a nested block in JS became nested data in Elixir, nested for
loops became nested lists, and nested control structure became nested data-structure.
Variables became functions.
By the end of it I had worked the program into the data.
The difference between Structured Programming (the JS code) and Functional Programming (the Elixir code) became so clear to me.
It’s easy to see this if you compare and contrast the JS above with the Elixir below construct-by-construct.
Here’s the final code in Elixir:
The imperative I like to use now is:
Manipulate program data; Don’t manipulate program text. 2
The essence of FP isn’t just about functions, inputs, and outputs. FP is about representation; Specifically, one representation after the other. The succession of data; Not a succession of instructions. A succession of collections; Not a succession of statements. It’s your application snap-shot by snap-shot; Not step by step. Now with this idea in mind, it’s much easier to see how a bigger program is coded in a functional style, how it’s pieced together from plain old functions, and just their inputs, and outputs.
So that was the story of how I found my answer. In conclusion I want to highlight three learnings:
-
Representation, as we know, can make a big difference.3 So goes the adage about data structures being more important than functions, procedures, etc.
-
Even after years of looking at a problem; The elegance of the representation above eluded me. We can miss a great solution over and over again. Perhaps it’s worth entertaining the seemingly strangest formulations using all the tools at our disposal when we start out.
-
Translation can be a valuable way to learn. I hadn’t thought so because it would detract from getting into the right mindset. I was wrong.
Some other reading you might enjoy:
-
↩
That is, an array of arrays, or a list of lists for example. What we want, in fact, is a sparse array (as my dad pointed out).
-
↩
Aditya Athalye kindly pointed out that in some languages, like Lisp, data can be code and code can be data and manipulated as such. That’s not what I mean here.
-
↩
I haven’t compared this code to any of my historical code in Elixir that uses a list of lists, or similar representations, but it did make a difference to how much fun this was!