This blog post is part 2 of a multi-part series. In part 1, we introduced the functional 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/.
Our task from part 1, for the purpose of illustration, was to grab the first initial of a user in a string format such as “Doc Emmett Brown”, simply creating the string “E’ in this case.
We were left with the following code. (To make it more accessible, I’ve rewritten it to keep only const and the arrow function from ES2015):
Composition is our bread and butter. We want to mix and match simple functions to make complex programs… not so complex for us to scale and modify!
In this post, we will discover the joys of curry which will allow us to create more intuitive function compositions. We will also dig deeper into Ramda as a tool to fit our eager functional mindset.
Let’s Get On With It
You might have been somewhat saddened when we changed our building blocks to handle maybes. I wouldn’t blame you. Wasn’t it better, clearer, originally? Take a look:
It sure makes me a little teary-eyed. In fact, we’re repeating the same pattern in both cases: taking a maybe “thing” and “fmapping” over it (the goal being to keep our null check in one single place). Wouldn’t it be grand if we could factor out this functor logic?
Perhaps we’d have something like this (incomplete) code:
We’ve created a new map function that takes a function and a functor, and applies that function to the wrapped value inside the functor via fmap. Our building blocks are kept clear and to the point, and we are starting to feel better about life.
But darn, how do we pipe this together now? Have we actually created a quagmire for ourselves? Let’s take a step back and see how we might do this in standalone JS:
OK, so we use our shiny new map to get a (maybe) first name, and then we map that result to get the (maybe) first letter that we are after.
The root problem here is that we can’t use our beloved R.pipe() to compose functions that take more than one parameter (and our map function takes two). That’s because the output of one function is the input of the next one in the composition. So, how could we return two separate values from a function? We can’t!
(And we won’t bother trying the ugliness of returning an object with multiple keys; or even worse, an array to represent two values).
Curry Me Some New Functions
We need to prep our map function so it takes a single parameter at the time of composition. We want that single parameter to be the maybe, as that is the only thing that is unknown before we run the composition.
This is exactly what currying will help us do! Ta-da: here we are introduced to a functional programming concept: Curry.
After you “curry” a function, you can then “partially apply” it. That means you simply pass it fewer parameters than it expects, and it returns a new function that expects the rest of the parameters, until all parameters are met, and only then will you get the output result.
Yes, you might have guessed it: Ramda provides such a curry function. Let’s look at the quintessential example, making an add10 function based on an original add function:
That’s all there is to it. We declare add to take two parameters. We curry it with Ramda. We partially apply the curried version with one parameter to create a new function add10. Now, add10 takes one param and returns a result. Done.
Let’s actually dive in a little deeper and write our own little implementation of curry. Homemade curry! (Gee, I had promised myself I wouldn’t make any food puns).
Ramda helps a lot because our version only handles two parameters, while Ramda (or lodash-fp)’s curry will handle any number of parameters for us. And, not to mention, it’s a lot cleaner: just R.curry() any function your heart desires.
So, What About Our Code?
So now we have the solution in our mind, let’s return to the problem at hand! Check it out:
It’s worth noting that I was a bit sneaky about the order of the parameters when I first created the map function. For the currying and composition to work, map has to take the function we want to run first, and the data structure second (the maybe “thing”). It makes sense, but you have to be mindful of it.
This parameter ordering is often the case in functional programming: the data comes last, which also allows for the pointfree style that we saw before. Interestingly, underscore does it backwards, so as popular as it is, it doesn’t lend itself to functional programming!
Because we’ve kept our function “pure” (more about that in a later post), we can rearrange the composition and avoid “opening the container twice” (using mapCurried twice, which is not only an eye sore but also inefficient).
So let’s rewrite this once more. And while we’re at it, let’s actually make map curried by default! And hell, we’ll also break down getFirstName into two parts (adding a getWords method) to truly keep all our responsibility separate, take a look:
Hey now, that looks pretty good. We simply use composition (pipe) inside our curried map function! If we have a Maybe(null) as input, the whole thing is circumvented right off the bat, and we return Maybe(null) directly. If the Maybe has data inside it, we run though each function. We are becoming more versatile with function composition.
Now, we know about the power of currying, which is handy in its own right (even outside of pure functional programming), but it also helps us keep our building blocks neat and to the point, that is, they are only concerned with their core functionality and not with functor logic.
We’ll see this is quite powerful in the next post because we can mix and match different functors in the compose chain.
Curry By Default
Now is a good time to mention that Ramda has heaps of functions available for us to use (similar to lodash or underscore)… and they are all curried by default (as are all functions in Haskell)!
Let’s see an example for good measure, using indexOf:
With Ramda’s curried version of indexOf we can easily build a new function that always looks for the second index of an array. Sounds pretty good, especially if you do the same lookup multiple times.
What does that mean for us? We can easily rewrite our building blocks to be pointfree: without any mention of our particular data: no “string”, “name”, or “user”. (I’ve actually inadvertently swapped these names throughout this series; naming things is tricky). So let’s use Ramda for our building blocks:
Voila. For instance, Ramda’s split actually expects two parameters (the separator then the array). We only feed it the first one, so we have a new function who’s sole purpose is to separate words, and is waiting for data work on. Perfect for our composition!
Now, all our building blocks are native Ramda functions. So, why even wrap them in our own functions. Seems overkill, let’s clean up even more variable names (getting rid of more stateful things):
And that’s it for part 2: curry and more Ramda in our arsenal.
Please, feel free to check out or fork this code on CodePen.
In part 3, we will refactor the Maybe factory to be more memory efficient. We will then introduce new functors. “Maybe” isn’t enough for us serious programmers! We want to be able to handle proper validation beyond wrapping a null in our composition. We want to extract useful messages if something goes awry. Stay tuned!
Update: Part 3 is now available.
Any feedback or criticism is most appreciated.
You can also follow me on twitter.
Thanks for reading!