Fabrizio Fortunato

Demystifying functional programming with Ramda

February 15, 2017

A very opinionated introduction to functional programming in Javascript using Ramda. The benefits of using functional programming in Javascript and a first look at functions composition.

I also need to say that i’m definitely not an expert on the field but i’m really starting to appreciate the subject and i’m realising all the benefits that it brings to the table.

Speaking about Javascript at work, yes i work in Ryanair as Lead Frontend Developer, we already banned long time ago the use of for and while loops ( where performance it’s not an issue ), using only map/filter/reduce over the other ones.

We can say that we are on our journey on functional programming.

First i want to start by answering the easy questions so:

  • What is a functional programming language?
  • Why Javascript is a functional programming language?

functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. —Wikipedia.

So what we can get from Wikipedia definition is that is paradigm, like object oriented or imperative programming. The basic computation are functions which lead us to the following question, why Javascript is a functional programming language.

In javascript functions are first class functions, in fact we can assign a function to variable, as an array element.

const foo = () => 'bar';

const list = [foo];

The other point revolves around functions of course, in javascript you can have a function as a result of another function or you can pass function as a parameter of another function, this is called higher order functions.

const higher = f => f('bar');

const another = () => {
  return () => 'bar';
};

Why

Having introduced a little bit what is functional programming and why javascript is a functional programming language the following question is why we should worry about learning and write in this new paradigm?

Attention the following statements are very subjective and personal.

First it comes down to readability, when doing code reviews or peer reviews i can see what is going on inside a pull request without poking the developer asking what is going on inside.

In Ryanair we used different frameworks for different projects, we started out with Angular 1 and now we are moving also to Angular 2, while those two frameworks are very different the paradigm that we are following is the same.

Frameworks will come and go, yesterday was Backbone, today it’s React tomorrow who knows.

Learning a new paradigm and particularly functional programming, enables to solve problems in a completely different way, which in my opinion is more elegant and more robust.

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Why not object oriented

We followed an object oriented approach at the start of the new web site and we probably stumble into all the problems that comes with it:

Inheritance

One of the pillar of Object oriented paradigm for code reuse. When using inheritance you are creating a taxonomy, a hierarchy for the concepts that you have to shape. Forcing you to inherit from a single ancestor, in a large project pretty soon you will have the famous gorilla/banana problem

The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.

Joe Armstrong

Interfaces can save you here instead of directly relying on inheritance.

Encapsulation

Encapsulation is one of the other pillars of object oriented programming, variables are protected from access because they are encapsulated inside the object. In javascript this is true about primitive types which they are passed by value. But objects are passed always through reference which means that the passed object is not safe at all.

Immutability will rescue here and the use of pure functions, but we will explore them later on.

Ok stop chatting and now let’s dive into it. We choose Ramda as a helper library to start this journey into functional programming.

Composition

One of the most fundamental concepts of functional programming. Composition let us break down our logic, our operations into smaller functions which later on we can just compose them together.

If we look at Ramda it exposes different functions for doing this, we start with compose

const compose = (f, g) => x => f(g(x));

R.compose in Ramda take n functions and as the function name tell compose them together. One thing to note about compose is that we define the functions to execute from right to left.

We define them right to left because we are following the mathematical composition which applies right to left as well, this way our compose will follow the same laws that the mathematical one has: associativity

It doesn’t matter how you group compose, the result will be equivalent.

Given this data structure for a flight and three functions a b c:

const flight = {
  options: {
    1: {
      globalCode: 'foo'
    }
  }
};

const a = data => data.options[1].globalCode;

const b = data => data.toUpperCase();

const c = data => data + '-BAR';

And we want to compose them together

R.compose(
  R.compose(c, b),
  a
)(flight) //=> 'FOO-BAR'

it’s equal to

R.compose(
  c,
  R.compose(b, a)
)(flight) //=> 'FOO-BAR'

So how can we leverage composition in javascript and what it enables?

Let’s see a small example, we want to filter all the flights that have a discount, our flight structure is really nested so we have to walk through it and not all the flights will have this nested attribute so we want to be safe at the same time that we are not causing any errors.

const getDiscountFlights = flights => {
  return flights.filter(flight => {
    if (flight && flight.options && flight.options[1] &&
        flight.options[1].globalCode) {
      return flight.options[1].globalCode.indexOf('ESDSC') !== -1;
    }
    return false;
  })
};

Now let’s try to break down our functions into smaller ones and composed them together:

const hasDiscountCode = R.contains('ESDSC');
const getGlobalCode = R.pathOr(
  '',
  ['options', '1', 'globalCode']
);
const getDiscountFlights = R.filter(
  R.compose(
    hasDiscountCode,
    getGlobalCode
  )
);

This code is more or less equivalent, which one do you think is more readable and maintainable?

We have introduced some other Ramda functions in here:

  • R.contains will check if our globalCode contains the code ESDSC and you can use it on arrays or objects as well.
  • R.pathOr returns the value at that path for the given object otherwise returns the provided default value.

In the end we are composing the two functions together to check if the flight has a discount or not.

Breaking down the functionality into smaller functions allow also to reuse them in different part of the application, avoiding code duplication and also it enables very quick refactoring of the functionality. For example what we could do is extract the hasDiscount function into a generic file

//util.js
export const hasDiscount = R.compose(
  R.contains('ESDSC'),
  R.pathOr(
    '',
    ['options', '1', 'globalCode']
  )
);

//main.js
import { hasDiscount } from './utils';

const getDiscountFlights = flights => {
  return R.filter(
    hasDiscount
  )(flights);
};

Reverse compose

If instead of applying functions right to left you prefer to apply the functions left to right, Ramda has already a function for it which is called pipe.

It’s just a different flavour of composition and you can choose which one you are more comfortable with it.

On the next post i will talk about pure functions.


Head of Frontend at RyanairLabs @izifortune
Fabrizio Fortunato © 2021, Built with Gatsby