DEV Community

Cover image for Functional Programming in JS, part I - Composition (Currying, Lodash and Ramda)
mpodlasin
mpodlasin

Posted on

Functional Programming in JS, part I - Composition (Currying, Lodash and Ramda)

In this series of articles we will go through a soft introduction to functional programming in JavaScript.

Each article will be devoted to different aspect of functional programming. After the theoretical introduction we will see how those concepts are then put to use in actual, real world JavaScript libraries.

This mix of theory and practice will ensure that you get a deep understanding of all the concepts, while being able to use them effortlessly in practice in your day to day work.

Please be aware that this series assumes that you already have some proficiency in writing code with arrays methods such as map, filter and reduce. If they still confuse you, let me know and I will write an article explaining them in depth.

Ready? Let's get started!

Composition

If I had to name in one word what this first article will focus on, it would be composition or composability.

More specifically, I mean here the art of composing your code from small, reusable functions. Almost like composing a lego set from smaller pieces.

It turns out that properly written functional code is very composable. What does it mean? It means that it is extremely easy to take a small piece of that code and reuse it in completely different situation.

Take a look at this code, written in traditional style:

let result = [];

for (let i = 0, i < data.length, i++) {
    const num = parseInt(data[i], 10);

    if (num < 5) {
        result.push(num);
    }
}

Enter fullscreen mode Exit fullscreen mode

and now compare it to:

const stringToInt = str => parseInt(str, 10);
const lessThan = compareTo => num => num < compareTo;

const result = data
    .map(stringToInt)
    .filter(lessThan(5));
Enter fullscreen mode Exit fullscreen mode

Those two snippets do exactly the same thing. We first take the data array, which is filled with some strings. We then transform those strings into integers. And finally we store only those integers that are strictly smaller than 5 in a new array. We keep that array under result variable.

So if we got a ["1", "6", "3"] array, we would return [1, 3] as a result.

Depending on which style you are more accustomed to, you will find one of the two above snippets more readable. I believe that the second one is more readable, because - not taking into the account little helper functions that we defined - it reads almost like English:

Take data, map each stringToInt and then filter only those values that are lessThan(5).

But if you are not used to functional style however, this second snippet will seem awkward and needlessly convoluted. Are there any objective benefits of writing the code in that style?

Of course! And that benefit is exactly the composability. Note that we went out of our way to define as functions even the simplest pieces of our code. Thanks to that, we can now use those snippets in completely new situations, without ever writing the same code twice.

Of course those reusable stringToInt and lessThan functions are extremely simple, to the point where it arguably is not worth reusing them like that. But keep in mind that this example only serves as a motivation for the whole approach.

In more complex applications, those functions would be getting more and more complicated. The approach of reusing the most amount of code possible and composing new code from previously written functions will have much more apparent benefits in a bigger codebase.

Note also that apart from the simplest possible reusability - simply using stringToInt and lessThan functions in different contexts - we also see examples of using higher order array functions - map and filter. It is key to note that they possess an immense power - they allow you to use functions defined for singular values (for example strings) on whole arrays of those values (for example on arrays of strings).

This is the first moment when you can actually see the power of that approach. You wrote two functions - stringToInt and lessThan that are not supposed to be used on arrays. And yet, by wrapping them in only a few more characters - .map(stringToInt), .filter(lessThan(5)), you suddenly possess the power to use those functions on whole arrays of values.

This is exactly what we meant at the beginning. Functional approach allows you to use the same code in completely different contexts - in fact here the same code is even used on a completely different types of values! A function that was meant to work only on strings can now work on an arrays of strings! That's pretty cool.

Currying

Perhaps you have already asked yourself - "wait, what is this weird definition of lessThan about?".

If I asked you to write a lessThen function, you would probably do it like that:

const lessThan = (num, compareTo) => num < compareTo;
Enter fullscreen mode Exit fullscreen mode

And yet we did it like that:

const lessThan = compareTo => num => num < compareTo;
Enter fullscreen mode Exit fullscreen mode

Not only arguments are switched, but also the syntax of function definition is different. Is this some new, exotic addition to JavaScript standard?

In fact, no. What we simply did here, is that we wrote a function that returns an another function.

Function that we are returning is:

num => num < compareTo;
Enter fullscreen mode Exit fullscreen mode

And then we wrap it in another function, that finally provides compareTo variable for it:

compareTo => (num => num < compareTo);
Enter fullscreen mode Exit fullscreen mode

This time we wrapped the returned function in parantheses, for better readability.

Note that we used here the fact that in an arrow function we can provide returned value direcly, instead of function body. If we really wanted to write the body, we might rewrite above example like so:

compareTo => {
    return num => num < compareTo;
};
Enter fullscreen mode Exit fullscreen mode

In fact, this pattern doesn't really rely on ES6 arrow function syntax. Me might have as well written it in old school function syntax:

function(compareTo) {
    return function(num) {
        return num < compareTo;
    };
}
Enter fullscreen mode Exit fullscreen mode

What ES6 arrow syntax does however is that it makes that monstrous code look much nicer:

compareTo => num => num < compareTo;
Enter fullscreen mode Exit fullscreen mode

That pattern is called currying.

If you take a function taking some number of parameters:


const someFunction = (a, b, c) => {
    // some code here
};
Enter fullscreen mode Exit fullscreen mode

you can "curry" it (or produce its "curried" version), which looks like that:

const someFunction = a => b => c => {
    // some code here
};
Enter fullscreen mode Exit fullscreen mode

In this case, there original function accepts three parameters.

After currying it, we get a function that accepts one parameter a, returns a function that accepts one parameter b, then returns a function that accepts one parameter c and finally executes the body of the original function.

Ok, we explained how that mechanism works, but we didn't explain why did we even decide to write our functions like that.

Frankly, the answer is extremely simple. The only reason is so that we could later use lessThan function like so:

.filter(lessThan(5))
Enter fullscreen mode Exit fullscreen mode

Note that if we used our first definition of that function:

const lessThan = (num, compareTo) => num < compareTo;
Enter fullscreen mode Exit fullscreen mode

then applying it in filter method wouldn't be nearly as nice. We would have to write that code like so:

.filter(num => lessThan(num, 5))
Enter fullscreen mode Exit fullscreen mode

So again, you see that we wrote our function in a way that makes it compose nicely with methods such as filter.

In fact, it also composes nicely with map. Writing code like this:

numbers.map(lessThan(5))
Enter fullscreen mode Exit fullscreen mode

would return an array of booleans saying if the number on a given place in the array is smaller than 5. For example running that code on an array [5, 1, 4], would return an array [false, true, true].

So you can see that lessThen function composes now much nicer with other, higher-order functions.

On top of that, assume we noticed that we use lessThen very often with a number 5 specifically. Maybe that's a very important number, let's say a number of the servers that we have in the company.

This number now appears in several places in our code. But having it hard-coded like that is a very bad practice. What if that number changes at some point, for example to a 6? We would have to search for all those appearances of 5 and change them to 6 manually. This would be both extremely cumbersome and error prone.

The first solution that comes to mind is to store that number in a variable, a constant with some semantic name that describes what this number really means:

const NUMBER_OF_SERVERS = 5;
Enter fullscreen mode Exit fullscreen mode

Now we can use the constant, instead of the number:

.filter(lessThan(NUMBER_OF_SERVERS))
Enter fullscreen mode Exit fullscreen mode

Iff that number changes (for example our company buys more servers), we can simply update it in one place, where that constant is defined.

This is certainly nicer and very readable, but it's still a tiny bit cumbersome to import two separate values (lessThan and NUMBER_OF_SERVERS) even though we always want to use them together.

However, the way we defined lessThan function allows us to fix that. We can simply store the returned function in an another variable!

const lessThanNumberOfServers = lessThan(NUMBER_OF_SERVERS);
Enter fullscreen mode Exit fullscreen mode

Now whenever we want to use that function with that specific value, we can simply import it once and use it directly:

.filter(lessThanNumberOfServers)
Enter fullscreen mode Exit fullscreen mode

So not only our function is more composable with other functions, but it also allows us to define new functions in a very easy manner.

Very often certain values in our functions are only some kind of configuration. Those values do not change very often. In fact, you will often find yourself hard-coding those values inside your functions:

const someFunction = (...someArguments) => {
   const SOME_VALUE_THAT_WILL_PROBABLY_NOT_CHANGE = 5;

   // some code here
};
Enter fullscreen mode Exit fullscreen mode

It's sometimes a good idea to put such value as an argument of a curried function and simply create a new function, with this value already set to a value we expect to be the most common:

const someBiggerFunction = (someValueThatWillProbablyNotChange) => (...someArguments) => {
    // some code here
}

const someFunction = someBiggerFunction(5);
Enter fullscreen mode Exit fullscreen mode

This pattern is handy, because it ultimately gives you the same result - a function with a value hard-coded inside. But at the same time you get a much bigger flexibility. When it turns out it is actually necessary to set that variable to some other value, you can do it easily, without any refactoring, simply by running someBiggerFunction with another argument.

So, as we have seen, using curried versions of functions, gives us bigger composability, allowing for both easier use of those functions in other compositions, as well as composing brand new functions with ease.

Lodash and Ramda

I hope that it is clear by now that in order to use this aspect of functional programming, you don't need any external libraries.

Everything you need is already baked into the JavaScript itself (most notably an arrow function syntax).

If however you decide to write your code in that style, perhaps it is not a bad idea to use one of popular functional programming utility libraries.

After all, one of the benefits or writing composable code was supposed to be reusability. This means it would be kind of pointless to write from scratch a code that was already written and carefully tested by someone else.

Also, as we have seen, writing JavaScript in functional style promotes making your functions as general as possible. So, again, it would be dumb to write a completely new function to solve a particular problem, if you can simply compose that function from a two or three already existing functions.

So let's take a look at Lodash and Ramda and see what do they have to offer for programmers coding in functional style.

It's important to mention that in the case of Lodash we will be talking particuraliry about lodash/fp package, which is a version of the library more geared for functional programming.

On the other hand, Ramda supports functional style out of the box.

Curried APIs

We have spent so much time describing currying, because it really is a powerful tool in programming with functions. So powerful, that it was built-in both into Ramda and Lodash libraries.

Take a look at Ramdas splitWhen function, which allows you to split an array, using a function that, by returning true for a chosen parameter, will decide where the split will happen.

For example given an array of numbers, we might want to split it at the first occurrence of number 5. So we first construct a function that detects the number 5, given an arbitrary element from the array.

Sounds complicated? It's not:

x => x === 5
Enter fullscreen mode Exit fullscreen mode

Now we can use that function in Ramdas splitWhen function. When we run this code:

import { splitWhen } from 'ramda';

splitWhen(x => x === 5, [1, 2, 5, 6]);
Enter fullscreen mode Exit fullscreen mode

the result will be an array consisting of two arrays:

[[1, 2], [5, 6]]
Enter fullscreen mode Exit fullscreen mode

So we see that the original array was split at 5, as we wanted.

Note that we executed splitWhen function in a traditional manner, passing to it two arguments and getting some result.

But it turns out that functions from Ramda can also behave like curried functions. This means that we can create a new function, like so:

const splitAtFive = splitWhen(x => x === 5);
Enter fullscreen mode Exit fullscreen mode

Note that this time we did not pass both arguments to splitWhen at once. We created a new function which waits for an array to be provided. Running splitAtFive([1, 2, 5, 6]) will return exactly the same result as before: [[1, 2], [5, 6]].

So we see that Ramda supports currying out of the box! That's really great for people who love to code in functional style.

And while we are at it, we can mention that Ramda has an equals method, that is basically a wrapper for an === operator.

This might seem pointless (after all equals(2, 3) is a bit less readable than 2 === 3) but because all Ramda functions support currying, and equals is no exception, we can refactor our splitAtFive function like so:

const splitAtFive = splitWhen(equals(5));
Enter fullscreen mode Exit fullscreen mode

This reads basically like English! That's the beauty of functional programming.

That last example works, because splitWhen can accept only a one argument function. equals requires two arguments, but thanks to currying, we can provide one argument earlier, while the second will be provided by the splitWhen itself.

This is exactly the same trick as our previously created lessThan function.

Curry your own functions

We mentioned that it is incredibly easy to write curried functions in modern JavaScript with the use of arrow syntax. For example we could implement equals utility function as so:

const equals = a => b => a === b;
Enter fullscreen mode Exit fullscreen mode

But this approach has a certain drawback. If you defined a function as curried, now you can only use it in it's curried form. Meaning, writing equals(5, 4) will not work now.

That's because even though you passed two arguments to it, our equals function only expects one. Second argument gets ignored and the function returns another function, to which just now we could apply the second argument.

So in the end we would have to use this function by writing equals(5)(4), which maybe is not tragic, but looks a bit awkward.

Luckily both Ramda and Lodash provide us with a handy curry helper function, which can be used to produce functions that work both in curried and uncurried forms.

So, using Ramda library, we could define our equals function like so:

import { curry } from 'ramda';

const equals = curry((a, b) => a === b);
Enter fullscreen mode Exit fullscreen mode

And now we can use this function in traditional way, by calling equals(5, 4), but we can also utilize its curried form by - for example - passing only one argument to it in the filter method:

.filter(equals(5))
Enter fullscreen mode Exit fullscreen mode

This versatility is built-in in many functional programming languages. With curry helper function we can easily achieve the same effect in JavaScript.

Functional wrappers for JS methods

The last thing that I would like to mention in relation to Ramda and Lodash libraries are wrappers for native JavaScript functions and methods.

We have already seen that things that are already available and easy in the language (like equality checks) have their corresponding wrappers (equals function), in order to make functional programming with them easier.

The same thing applies to other methods. For example popular array methods map filter and reduce all have their corresponding functions in Ramda and Lodash.

Why would that be useful?

As we mentioned again and again, the whole point of functional programming is easy composability. Creating a function that has a new behavior should be realy easy and preferably would be a composition of other functions.

Let's take our stringToInt function and say that now we want to create a version of that function that works on arrays of strings. The obvious solutions is a code like this:

const stringsToInts = strings => strings.map(stringToInt);
Enter fullscreen mode Exit fullscreen mode

This is not the worst, but is there a way to write that even cleaner?

First thing that we have to notice is that map method accepts two arguments and not one, as it might seem at the beginning. It accepts first parameter - an array of strings - in a method syntax, before the dot, and second parameter - a function - inside regular function brackets:

firstArgument.map(secondArgument);
Enter fullscreen mode Exit fullscreen mode

This object oriented syntax makes things a bit more confusing. Let's imagine that map is a regular function, not a method. Then we would rewrite our code like so:

const stringsToInts = strings => map(strings, stringToInt);
Enter fullscreen mode Exit fullscreen mode

But wait. Now we can notice something. Could we maybe use curried version of map to write that code? Before we try that, let's reverse in what order strings and stringToInt arguments are accepted:

const stringsToInts = strings => map(stringToInt, strings);
Enter fullscreen mode Exit fullscreen mode

We have a function that accepts an array and returns an array. But that's exactly what curried version of map would do! Let's see:

const stringsToInts = map(stringToInt);
Enter fullscreen mode Exit fullscreen mode

Whoa, whoa! What exactly happened here? Let's go through that example again, step by step.

map is a function that accepts two parameters, an array and a function, and returns a new array. If map was curried, we could provide it only one parameter - the function.

What would we get as a result? Well, curried function returns another function, that waits for the second argument. In this case a second argument is an array, because we passed only the function so far.

So as a result we get... a function that accepts an array and returns an array (after applying stringToInt function to each parameter of course).

But that's exactly what we wanted!

Indeed, those two functions:

const stringsToInts = strings => strings.map(stringToInt);

const stringsToInts = map(stringToInt);
Enter fullscreen mode Exit fullscreen mode

behave in exactly the same way! After running them on ["1", "2", "3"] we get [1, 2, 3].

Again, which code looks cleaner to you depends entirely on your past experiences, but you cannot argue that using curried version of map at the very least gives you more flexibility in how you write your code.

Note that we had to make three changes to map: we had to make it a function (instead of method), we had to reverse the order of arguments and we had to make the function curried.

That's exactly how Ramdas and Lodash array methods differ from their native implementations.

You can use those (and much more) wrapper functions when writing functional code with native JavaScript implementations seems awkward and convoluted.

Conclusion

The theme of this article was composability. I attempted to show you how you can make your codebase more composable, by utilizing functional programming patterns, most notably by currying your functions.

I then presented how some functional programming utility libraries like Ramda and lodash make it a bit easier to write code of that style in JavaScript.

I would highly encourage you to write some code fully in functional style. I wouldn't do that for production applications, because I believe that the most readable JavaScript is a mix between functional and object oriented approaches, but it is still a great exercise for you to familiarize yourself deeply with concepts described in that article.

The practice is key here. If you do that, soon even the most confusing looking functional code will actually seem simpler and nicer to you than its traditional alternative.

If you enjoyed this article, considered following me on Twitter, where I am regularly posting articles on JavaScript programming.

Thanks for reading!

(Cover Photo by La-Rel Easter on Unsplash)

Top comments (2)

Collapse
 
demystifyingjavascript profile image
demystifying-javascript

Checkout this new VS Code Extension: marketplace.visualstudio.com/items...

This extensions will allow you to have Ramda Js REPL/Playground & Documentation right in your VS Code Editor.

Follow for more on my Youtube Channel: youtube.com/channel/UCVhh3gpOXCb-r...

Collapse
 
abelmark profile image
Mark Abel

Great article! I love functional programming.