Functional Programming with JS
Posted on August 25, 2017 by Clive in JavaScript, Functional Programming
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.
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: 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. 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: Even in these short examples you can see the difference between the two. 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. Here you are accessing the data directly, changing the object state; This leads on to the concept of 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 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. At the simplest level compose() is the application of one function over another: 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. 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: 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. 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: Using a lib like Ramda.js means that you can do something like this instead: 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: 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. 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 ParadigmsFP for the layman
Declarative
// 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] );
Avoid state mutation (immutability)
// Directly mutating state
const obj = {a: 1, b: 2};
obj.a = 2;
// 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);
Functional purity
Function composition
// 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; }
// Simple compose function
const compose = (g, f) => {
return (x) => {
return g(f(x));
}
}
const nextBirthday = compose( add(1), getLastBirthday )( new Date() );
Some other things to discuss
Iteration
Currying
// 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
const func = R.curry( (x, y) => { return x + y; } );
const f1 = func(1);
const ans = f1(2); // => 3
Ramda.js and Lodash/Underscore.js
// 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);
Additional reading