Back to blog

Functional Programming with Ramda

Blog
• Immutable.
• Stateless.
• Declarative.
• Pure.
• Compositions.
• Functions.

These are just a few things that come to one’s mind when thinking about functional programming.

In this post, I’m going to delve a bit deeper into the functional world and specifically, explore the awesomeness of currying and function compositions.

Currying

Currying is a way to partially apply arguments to a function.

```const add = (a, b) => a + b;

Here, our `add` function accepts two arguments and for it to actually do something, it needs both arguments to be supplied at the same time. It is possible, however, to apply just one argument and pass another later on in the application by returning a closure function like so:

```const add = a => b => a + b;

and later on…

`addTwo(3); // => 5`

Though we have now lost the original functionality to pass both arguments at the same time, we can continue to add more arguments like so:

`const add = a => b => c => d => a + b + c + d;`

As it currently stands, it is impossible to pass just one function on a first call, and then the rest (3 in this case) together, afterwards.

This is where `currying` comes in. In theory:

• we want to create a function that accepts a function (our `add` in this case)
• then remembers the number of arguments it expects and returns a function that when called with `n`number arguments, will check if the number of arguments is `>=` the arguments that we’re expecting
• and if `true`, will then execute that original function with these arguments
• If not true, it will return another function that will accept some other arguments and when called, will `concat` the top arguments with these `other` arguments and loop back to this whole functionality again

A bit confusing? Let’s look at some code:

```const curry = fn => {
// here we save the number of arguments we need
const totalArgsRequired = fn.length;

// then we return our partial function that accepts n arguments
return function basePartial (...args) {
// and when called, it checks if our
// original function is satisfied
if (args.length >= totalArgsRequired) {
// if so, then we call it and pass these args
return fn.apply(null, args);
}
// otherwise, we return a function, that
// accepts some otherArguments
return function partial (...otherArgs) {
// and when called, we loop back to the same thing,
// passing all the arguments that we have so far
return basePartial.apply(null, args.concat(otherArgs));
};
};
};```

For a better understanding, I have named the inner functions. Let’s break this down by the following example:

```const add = curry((a, b, c) => a + b + c); // => here we get a "basePartial" function
const firstLoop = add(2); // => here, firstLoop is a "partial" function
const secondLoop = firstLoop(3); // => "partial" again
const thirdLoop = secondLoop(4); // => the "if" statement is fulfilled and we get the result - 9.```

Why is this useful? Well, we’ll get there, but let’s first look at some partial function applications we can start implementing straight away, like these two generic functions here:

```const prop = curry((property, obj) => obj[property]);
const filter = curry((predicate, arr) => arr.filter(predicate));```

Here are a few use cases:

```const users = [
{ id: 1, name: 'Ed', online: true },
{ id: 2, name: 'Edd', online: true },
{ id: 3, name: 'Eddy', online: false },
];

const getName = prop('name');
users.map(getName); // => ['Ed', 'Edd', 'Eddy']

const getOnlineUsers = filter(prop('online'));
getOnlineUsers(users); // => [ { id: 1, name: 'Ed', online: true }, { id: 2, name: 'Edd', online: true } ]```

Point-free

This seamlessly leads us to another concept that comes with currying: point-free programming.

Notice how our `getName` or `getOnlineUsers` functions don’t really say anything about the `data` they need? They just explain what they’re doing in a very descriptive, generic way. Thanks to currying we can completely get rid of the `data` part. But to achieve this, we strategically placed `obj` and `arr` as the last arguments accepted by our generic functions above.

If we had done this the other way around, we would just create some very unnecessary and useless wrapper functions, as you can see here:

```const prop = curry((obj, property) => obj[property]);
const filter = curry((arr, predicate) => arr.filter(predicate));

const getName = user => prop(user, 'name');
const getOnlineUsers = users => filter(users, user => prop(user, 'online'));```

The concern here, is that if you don’t specify the data in the arguments list, you might not understand what a function expects. This is why most functional programming languages have robust type systems.

But let’s not get off track.

Now that we’ve seen currying in action, let’s continue with the idea of point-free programming and start with implementing a `map`.

```const map = curry((fn, data) => data.map(fn));
const prop = curry((property, obj) => obj[property]);
const filter = curry((predicate, arr) => arr.filter(predicate));

const getNames = map(prop('name'));
const getOnlineUsers = filter(prop('online'));```

And to get the names of just online users:

```const getJustOnlineUsersNames = users =>
getNames(getOnlineUsers(users));

getJustOnlineUsersNames(users); // => ['Ed', 'Edd']```

By explicitly specifying what `data` our function expects, we’re no longer abiding by the rules of point-free programming.

This does however bring us to the next topic, function compositions.

Function compositions

There is a way to create these generic, descriptive functions, and to keep the `data` out of the way.

• First we create a function that will accept a `n` number of functions and return a new function, that will wait for the data
• Once called with data (`n` number of arguments), it will reduce on functions supplied, passing the result of one function, as an argument, to another, starting off with that initial data

This is what we’re trying to achieve:

`compose(getNames, getOnlineUsers);`

We can see here that `getOnlineUsers` function is placed as a second argument, rather than run as a first step in our composition. This is because we’re following the same pattern as our daily coding – executing functions from right-to-left:

`getNames(getOnlineUsers(users)); // => the rightmost function is run first, going to the left direction`

Let’s implement our compose function here:

```const compose = (fn, ...fns) => (...args) =>
fns.reduce((result, x) => x(result), fn(...args));```

We’re not done yet, however, as our reduce method will start with the very first function and do left-to-right computation, as it is now.

But before we fix that and make it right-to-left, let’s look at the reason why we’re breaking down our first batch of arguments into `fn` and `fns`. As we know, `reduce` method accepts only one argument as an initial data. But we need to be able to pass multiple arguments to our composed function, so `fn` will produce the initial data for our reducer here.

For example, if we do this (left-to-right computation):

```const doMaths = compose(Math.max, Math.round);
doMaths(1, 2, 0.3, 3.4);```

Our reduce method would be confused, taking `1, 2, 0.3, 3.4` as initial arguments. We therefore let `Math.max` produce that initial data first.

Dilemma

Do we keep the left-to-right computation or switch to right-to-left instead?

Solution

Really, it comes down to whatever you find more readable, so why not have both versions?

```const pipe = (fn, ...fns) => (...args) =>
fns.reduce((result, x) => x(result), fn(...args)); // => left-to-right computation

const compose = (...fns) =>
pipe.apply(null, fns.reverse()); // => right-to-lefty```

And now going back to our initial issue, here is the full version:

```const curry = fn => {
const totalArgsRequired = fn.length;

return function basePartial (...args) {
if (args.length >= totalArgsRequired) {
return fn.apply(null, args);
}
return function partial (...otherArgs) {
return basePartial.apply(null, args.concat(otherArgs));
};
};
};

const map = curry((fn, data) => data.map(fn));
const prop = curry((property, obj) => obj[property]);
const filter = curry((predicate, arr) => arr.filter(predicate));

const pipe = (fn, ...fns) => (...args) =>
fns.reduce((result, x) => x(result), fn(...args));

const compose = (...fns) =>
pipe.apply(null, fns.reverse());

const getNames = map(prop('name'));
const getOnlineUsers = filter(prop('online'));

const getOnlineUsersNames = compose(getNames, getOnlineUsers);

const users = [
{ id: 1, name: 'Ed', online: true },
{ id: 2, name: 'Edd', online: true },
{ id: 3, name: 'Eddy', online: false },
];

getOnlineUsersNames(users); // => ['Ed', 'Edd']```

But now, 90% of our code is just boilerplate. And this is where ramda comes in. In Ramda, every single function is automatically curried. So, if we were to re-implement this using Ramda, we would simply do:

```import { pipe, prop, map, filter } from 'ramda';

const getOnlineUsersNames = pipe(
filter(prop('online')),
map(prop('name')),
);

const users = [
{ id: 1, name: 'Ed', online: true },
{ id: 2, name: 'Edd', online: true },
{ id: 3, name: 'Eddy', online: false },
];

getOnlineUsersNames(users); // => ['Ed', 'Edd']```

Ramda vs. …?

Another `lodash` library, one might say. But no, Ramda specifically targets pure and functional programming concepts, meaning that not one of the functions here could have a side effect (like `Math.random` or `debounce`). There is however `lodash-fp`, which is similar to Ramda at first, but when you dig deeper, Ramda not only comes with loads of tiny utility functions, but also plays nicely with `Monadic` interfaces in `JS`, that will be covered in the next chapter.

Suppose we have an object and we need to take one of its properties and spread it at a current level:

```const state = {
page: {
url: '/home',
assets: [],
},
fetchUsers: () => {},
};```

desired result:

```{
url: '/home',
assets: [],
fetchUsers: () => {},
}```

We can create a generic `spread` function like so:

```import { converge, merge, dissoc, prop } from 'ramda';

const spread = converge(merge, [dissoc, prop]);

// => { title: 'Home page', url: '/home', assets: [], links: [], fetchUsers: () => {} }```

Example 2: reject and transform

Or perhaps we’re receiving an array of page objects from the server that look like this:

```{
id: 1,
title: 'home',
url: '/home',
resources: [],
active: null,
archived: true,
meta: {
views: 33,
},
}```

We want to reject the ones that are `archived`, and transform the rest with the following logic:

1. Make title uppercase
2. prepend //test.amido.com to the `url`
3. default `active` property to false if empty,
4. increment `meta -> views`
5. default `meta -> comments` to 0 if empty
```import { evolve, toUpper, inc, defaultTo, concat, map, reject, compose } from 'ramda';

const transformer = evolve({
title: toUpper,
url: concat('//test.amido.com'),
active: defaultTo(false),
meta: {
views: inc,
},
});

const transformAndReject = compose(map(transformer), reject(prop('archived')));

api.get('/pages').then(transformAndReject);```

episode 3: format user

We’ve just received a `user` object from one endpoint:

```const user = {
profile: {
firstName: 'One',
lastName: 'Two',
},
skills: ['js', 'elm', 'clojure'],
social: {
},
};```

some posts array from another:

```const posts = [
{ id: 1, body: 'hello world', created_at: 138201389 },
{ id: 2, body: 'functional world', created_at: 123928838 },
];```

And we decided that we want to format the user in a very specific way – we want to merge `firstName `and `lastName` in a single `fullName` property and then add posts array to the user object under `posts` key.

```import { pipe, props, join, objOf, over, lensProp, useWith, merge } from 'ramda';

const createFullName = pipe(
props(['firstName', 'lastName']),
join(' '),
objOf('fullName'),
);

const formatUser = useWith(merge, [
over(lensProp('profile'), createFullName),
objOf('posts'),
]);

formatUser(user, posts);```

Conclusion

Since an application, in functional programming, is just one big function consisting of tiny building block functions, in an ideal world, we could keep creating these function compositions and end up with just one big composition in the end.

However, there are some `undefined` and `null` monsters lurking in the shadows, that want to completely ruin our code execution and throw errors in the way. There are also side effects that need to be dealt with in a functional way. We can overcome these obstacles with the help of a few, beautiful monadic interfaces and keep composing the life. I will cover this in the next chapter.

P.S. There is a nice collection of recipes for Ramda to get you started: Ramda cookbook

The browser you're using is out of date. Please update for better security, speed and experience on this site.