Much of functional programming is about composing functions together to handle our data in predictable ways. Simple functions neatly tied together create increasingly complex, yet dependable functions to build upon.
This blog post is part 1 of a multi-part series. In part 1, we will introduce the library Ramda and the concepts of composition, pointfree style, and functors through the simplest of examples.
Disclaimer: Please note that I am not an authority on functional programming. I intend this series solely as “introduction to concepts”. It remains a work-in-progress, and I encourage constructive criticism and discussion. This guide is quite unconventional in the order in which the content is provided. I choose to focus on practical examples, leaving essential theory such as typeclasses and function purity until late in the game. If you thirst for more technical resources, I happily point you to http://github.com/MostlyAdequate/mostly-adequate-guide, or a free ebook on Haskell: http://learnyouahaskell.com/.
Getting Our Feet Wet
Let’s say we have a person in a string format such as: “Doc Emmett Brown”. Our task is to obtain the person’s first initial, simply creating the string “E” in this case.
If you are reading this, you might already be keen to program with small functions that have single responsibilities. Let’s build on top of this idea from scratch with this starter ES6 code:
getFirstInitial is the main function. In it actually lies the essential aspect of functional programming (we return a function) and composition (the function is nested). We build on top of the first two simple functions (that are trivial to test and debug) to create a slightly more complex function that we can rest assure is fundamentally sound! Easy does it.
Yet, you might agree that our nested function here doesn’t look all that friendly (although ES6 makes it quite tolerable). It’s a bit of an eye-full and not too appealing to work with, given how simple the logic actually is:
It’s not incomprehensible by any means, but complexity is our enemy so let’s nip it in the bud.
In comes Ramda
We can do better by importing the Ramda library for our functional approach. Let’s change our main function getFirstInitial, and we’ll rename it to a noun: firstInitial. (“Functioneers” like to think of data in terms of nouns, not verbs).
The code is easier to grok and to maintain this way. We’re easily nesting or composing functions together. In fact, there is an equivalent function in Ramda called compose and it does the exact same thing as pipe, but the arguments go in the reverse direction, like so:
I prefer pipe and reading the code like normal English (left to right). Ramda does a lot more than composition (we’ll see more specific use cases in part 2).
Something else interesting is happening here with pipe and compose. In our original getFirstInitial function, we had a reference to person in there (in fact we even had two), take a look:
Now the function definition is free of that pesky data reference. Isn’t it nicer on the eyes (and on the brain)? That’s called Pointfree style.
You don’t have to tie your function to a “person” input variable (or rename that variable over time). It’s more encompassing this way: we could as well pass in a fictional character that fits the format of the string but isn’t necessarily a person (“Smurf Papa Smurf”).
More Syntax Goodness
Note how you could drop another function in the middle of this composition easily, let’s say a log function (if we had implemented one) to make sure we get the first name appropriately.
pipe(getFirstName, log, getFirstLetter)
Think about doing that with the original nested function. Three times as long to edit? Here it is:
It’s rather ugly. Did I get the parentheses just right?
The Real World
Let’s say our original input string could very well be null. No data. What happens when we call firstInitial(null)? It breaks! It begins as instructed with getFirstName, goes in to try to perform String.split on what turns out to be null, and it errors out.
Alright, no biggie, we could pepper our app with null checks in our functions to avoid this unfortunate turn of events.
Now, when we try to get the firstInitial of a null, we get null back, which is fairly reasonable. More importantly, the app didn’t blow up on us.
But let’s say a new requirement later follows that dictates we also check for undefined as well as null! We have to go back and change all the instances of these null checks in the nitty-gritty of our building blocks. It would work, but it’s not really ideal. We don’t wanna mess with our building blocks every five minutes.
Do it, Maybe
How can we lift our invalid checks outside of each individual function, so we can deal with them in one place? The pattern is always the same as we see in the duplication of the code above: if the value is null, don’t do anything and just return null.
if(name===null) return null;
The key abstraction here lies in wrapping our data in a “container” that can perform its own check. Then, we pass around this container instead of passing around the raw data.
It’s a bit of leap, but bear with me. Let’s make such a container.
We’ll call this container method fmap. It’s reminiscent of Array.map in that it looks inside the data structure (the array or the container) and returns a new structure based on the original element(s).
We’ll create a container factory that produces these container objects. We’ll call our factory Maybe, because the wrapped value may or may not exist (may or may not be null).
Let’s see this container concept in code:
We’re using ES6 goodies to create our Maybe factory. It takes in a value and returns a fresh new object with that value as val as well as a fmap method. fmap itself accepts a function f as a parameter, checks this.val and calculates a new value to wrap and return!
If the ES6/2015 is unfamiliar, here is an ES5 implementation of Maybe:
Interesting, isn’t it? Maybe actually returns a functor, which is (loosely) described as any data structure that can be mapped over. And “mapped over” simply means you are creating (or projecting) a new value based on the original one (via a function).
Fixing the building blocks
Now, we need to change getFirstName and getFirstLetter to handle our Maybe containers instead of plain strings (which in fact could be null!) Let’s do some fixing:
We’ve now delegated the null check to the Maybe wrapper! The great thing is that Maybe is nice enough to provide its own interface (as we built it ourselves!) so it can act based on its own internals. It’s introspective. We just call its own fmap method passing in a function that we want to invoke on the internal value… as long as it‘s not gonna crash the app!
Let us recap our code, including a check for undefined.
Feel free to fork and edit this code at: http://codepen.io/collardeau/pen/JYpLEY
Now we can compose with more ease and assurance thanks to our new functor Maybe.
Stay tuned for part 2, where we shall learn about currying. Fun stuff ahead.
Update: Part 2 is now available.
Any feedback or criticism is appreciated. You can also follow me on twitter.
Thanks for reading!