Playing With Fire

Exploring the web one Elixir at a time

Functional Programming with JS

This is an introduction to Functional Programming (FP) for the unaware and discusses a number of concepts that JavaScript (or in deed any) programmers that come from OOP-Land might probably not be overly familiar with.

Given the drive to FP and the benefits that it can bring over imperative coding styles, now is a great time to get up to speed.

 

FP for the layman

First off, for people that are new to this sort of programming I need to cover off some a few things.

Firstly, Functional Programming (FP) as a concept has been around for longer than OOP, but in particular what is meant in general by FP is the following:

  • It is declarative
  • Direct state mutation is avoided
  • Immutable data
  • Functional purity
  • Function composition

Some languages, especially those that come from the Haskell family combine these traits with Lazy evaluation, but JavaScript is not capable of this so it will not be discussed here. The closest we can get to this is to apply the concept of currying (discussed below).

The idea behind this post is to enable you to write your code in a “point-free” and composable style.

 

Declarative

Most programming languages, at least most mainstream that you may come across in a traditional programming environment are classed as imperative. Languages like Java, C#, PHP, Ruby, C/C++ fall into this category.

This means that you build up a list of instructions that detail exactly how to want to manipulate the state of your program, usually though state changes or mutation. These programs work kind of like a recipe. What you are building really is a series of steps.

In short, imperative programming details how something is done.

Declarative is the other end of the spectrum - it gives an instruction saying what you want done and leaves the details put to the computer on how it is to be completed.

Short example:

// Task: increment the numbers in a list

// Imperative - in a loop take each member of the list and increment by 1

const list = [1,2,3,4,5];

for (let i = 0; i < list.length; i++) {
    list[i] = ++list[i];
}

// Declarative - heres the list, just add 1

// Using Arrays prototype map method

const list = [1,2,3,4,5].map( item => item + 1 );

// Using a lib (ramda.js)

const R = require('ramda');
const add = item => item + 1;

const list = R.map( add, [1,2,3,4,5] );

Even in these short examples you can see the difference between the two.

 

Avoid state mutation (immutability)

Functional programs avoid state mutation. That doesn’t mean that state isn’t changed, it’s just that it is only transformed by means of a function.

To support this, many functional languages enforce this by ensuring that variables are immutable. In JavaScript this enforcement needs to be by developer rigour as data structures and variables in JavaScript can be directly updated.

// Directly mutating state

const obj = {a: 1, b: 2};
obj.a = 2;

Here you are accessing the data directly, changing the object state;

// Treating state as immutable - using a lens to abstract access to the given 
// property

const obj = {a: 1, b: 2};
const aLens = R.lensProp('a');

const res = R.set(aLens, 2, obj);

This leads on to the concept of purity…

 

Functional purity

Functional purity concerns itself with ensuring that the function does not perform any side-effects. In particular this means that the function will not affect the state of anything it is not directly concerned with. I.e. if you provide a function with the same arguments/state you receive the same output/state back out again without affecting any other part of your system. This is called Idempotency.

Code that mutates state using side effects is difficult to reason about, and therefore difficult to debug - can you be absolutely sure that the values you’re looking at are not going to be changed by some other operation in the code base?

Additionally code that is pure is easier to compose.

So why does this matter?

The answer to that is - it depends. In many FP languages, you have no choice but create pure functions - Haskell for example. If you need an effect, say string printing to the console, or reading a file, you need to be explicit about it and you need to use specific types that isolate the rest of your code from the effect.

For JS it’s more of a matter of rigour. Creating functions that don’t directly mutate state, preferring to clone instead.

The idea is that the function takes a state (its arguments) and returns the result of its operation without changing the arguments that it’s received, instead returning a new state that can be used in the next operation. If you’re using a value type that gets passed by reference (i.e. an array or object), then your function should clone that state and return it plus any relevant transformations. Never change the initial state.

 

Function composition

Function composition is a way by which functions can be bundled together - this has several benefits: it removes glue names; it minimises the amount of explicit looping; minimises side effects; removes hidden inputs and provides a way of separating application composition (the application) from the data (state) that it needs to operate over.

// Glue names - variable names that are used to "glue" code together

const on_error =  error => console.log(error.message);  // 'error' name is used 3 times...

// Simple composable log function

const log = (x) => { console.log(x); return x; }

At the simplest level compose() is the application of one function over another:

// Simple compose function

const compose = (g, f) => {
    return (x) => {
        return g(f(x));
    }
}

const nextBirthday = compose( add(1), getLastBirthday )( new Date() );

The result of compose is a new function that bundles together the functionality of the constituent parts to create “machines” that form the application. All they are waiting for is the initial state to kick things off.

 

Some other things to discuss

 

Iteration

Not exactly related to whether or not something is a functional programming language, however it is important to understand that most looping in an application can be categories in the following manner:

  • Map => apply a function over a value container
  • Reduce => apply a function with an accumulator over a value container to return a scalar value
  • Filter => apply a function to remove unwanted information

JS also has access to a forEach (Each) function. forEach() should be avoided as its semantics are inherently mutating and it doesn’t not (natively at least) return a value container back out after running its function over it.

 

Currying

This means partial application of inputs to any particular function as information becomes available - this is not the natural way that JS behaves. JS functions will execute on invocation whether or not all the arguments have been provided.

The best way to achieve this effect in JS is to use a specialist functional programming library like Ramda.js. That’s not to say that you can’t do it in vanilla JS, but you will need to create functions in a particular way that might not aid readability in some cases:

// Create a function that is capable of being curried
const myCurriedFunction = x => {
    return y => {
        return x + y;
    }
};

const f1 = myCurriedFunction(1);
const ans = myCurriedFunction(2);  // => 3

Using a lib like Ramda.js means that you can do something like this instead:

const func = R.curry( (x, y) => { return x + y; } );

const f1 = func(1);
const ans = f1(2);  // => 3

 

Ramda.js and Lodash/Underscore.js

Those eagle-eyed readers amongst you might be wondering why I keep using Ramda.js in these examples.

This is intentional.

Yes, I understand that Lodash.js and Underscore.js are more prevalent in the ecosystem, and no I don’t have anything to gain by using Ramda.js in my examples - there is no agenda here. I use Lodash pretty much every day in my day job. It’s an excellent library for getting stuff done.

However, Underscore (and by extension Lodash), even though it states that it’s a functional programming library for JS, simply isn’t. There, I’ve said it.

Underscore is not set up for currying and therefore cannot be composed, hence it breaks a simple tenant of FP - composability. Simple as.

In order to use Lodash in a composable way you need to use the _.flip() function which the starts to interfere with readability:

// Lodash Map

const container = _.map(obj, func)  // Can't be curried as the function is the last argument in the list.

// Composble Lodash Map

const map = _.curry( _.flip( _.map ) );

const container = map(func)(obj);

You need to do a lot of work upfront to make the library behave correctly. Ramda.js on the otherhand is set up with currying in mind from the get go.

Until next time.

 

Additional reading

If you got this far and enjoyed this post, the good folks at TopTal have the following interesting article which might also entertain you Introduction to Functional Programming: JavaScript Paradigms