Functional Programming
with JavaScript
- Greg Douglas @GWShark0

Why Functional Programming?
A monad is just a monoid in the category of endofunctors.
What's the problem?
Functor
Category
Catamorphism
Zygohistomorphic Prepromorphism
Future
Closure
Setoid
Option
Foldable
Lambda
Why JavaScript?!
Map
const numbers = [1, 2, 3];
const result = numbers.map(x => x + 1);
//=> [ 2, 3, 4 ]
const numbers = [1, 2, 3];
const result = [];
for (let i = 0; i < numbers.length; i++) {
result.push(numbers[i] + 1);
}
//=> [ 2, 3, 4 ]
For Loop
Map
const numbers = [1, 2, 3];
const result = numbers.map(x => x + 1);
//=> [ 2, 3, 4 ]
const numbers = [1, 2, 3];
const add = (a, b) => a + b;
numbers.map(n => add(n, 2));
//=> [ 3, 4, 5 ]
numbers.map(add(2)); // ??
Partial Application
// ES5
function add(a) {
return function (b) {
return a + b;
}
}
// ES6
const add = (a) => (b) => a + b;
const addOne = add(1);
//=> function (b) { return 1 + b; }
const addTwo = add(2);
const addMilllion = add(1000000);
Partial Application
const add = (a) => (b) => a + b;
const addTwo = add(2);
const numbers = [1, 2, 3];
numbers.map(addTwo);
//=> [ 3, 4, 5 ]
numbers.map(add(1000000));
//=> [ 1000001, 1000002, 1000003 ]
function add (a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}
add(1)(2)(3);
// 6
Currying

function add(a, b, c) {
return a + b + c;
}
add(1) //=> NaN
add(1, 2) //=> NaN
add(1, 2, 3) //=> 6
const curriedAdd = curry(add);
curriedAdd(1) //=> Function
curriedAdd(1)(2) //=> Function
curriedAdd(1)(2)(3) //=> 6
const curry = (f, arr = []) => {
return (...args) => {
return (
(a) => {
return (a.length === f.length)
? f(...a)
: curry(f, a);
}
)([...arr, ...args])
}
}
const identity = (x) => x;
const curriedIdentity = curry(identity);
// => Function
const curry = (f, arr = []) => {
return (...args) => { //=> [ 1 ]
return (
(a) => { //=> [ 1 ]
return (a.length === f.length) //=> true
? f(...a)
: curry(f, a);
}
)([...arr, ...args]) //=> [ 1 ]
}
}
const identity = (x) => x;
const curriedIdentity = curry(identity);
// => Function
curriedIdentity(1);
// => 1
const curry = (f, arr = []) => {
return (...args) => {
return (
(a) => {
return (a.length === f.length)
? f(...a)
: curry(f, a);
}
)([...arr, ...args])
}
}
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
// => Function
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
// => Function
curriedAdd(1);
// => Function
const curry = (f, arr = []) => {
return (...args) => { //=> [ 1 ]
return (
(a) => { //=> [ 1 ]
return (a.length === f.length) //=> false
? f(...a)
: curry(f, a);
}
)([...arr, ...args]) //=> [ 1 ]
}
}
const curry = (f, arr = []) => {
return (...args) => { //=> [ 2 ]
return (
(a) => { //=> [ 1, 2 ]
return (a.length === f.length) //=> false
? f(...a)
: curry(f, a);
}
)([...arr, ...args]) //=> [ 1, 2 ]
}
}
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
// => Function
curriedAdd(1)(2);
// => Function
const curry = (f, arr = []) => {
return (...args) => { //=> [ 3 ]
return (
(a) => { //=> [ 1, 2, 3 ]
return (a.length === f.length) //=> true
? f(...a)
: curry(f, a);
}
)([...arr, ...args]) //=> [ 1, 2, 3 ]
}
}
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
// => Function
curriedAdd(1)(2)(3);
// => 6
lodash/fp
lodash with its methods wrapped to produce immutable auto-curried iteratee-first data-last methods
function get(property, object) {
return object[property];
}
const people = [ { name: 'Alice' }, { name: 'Bob' } ];
function getPersonName(person) {
return get('name', person);
}
const names = people.map(getPersonName);
//=> ['Alice', 'Bob']
import curry from 'lodash/fp/curry';
get = curry(get);
const names = people.map(get('name'));
//=> ['Alice', 'Bob']
import split from 'lodash/split';
const words = (str) => split(' ', str);
words('The quick brown fox');
//=> [ 'The', 'quick', 'brown', 'fox' ]
import split from 'lodash/fp/split';
const words = split(' ');
words('The quick brown fox');
//=> [ 'The', 'quick', 'brown', 'fox' ]
Normal
Auto-curried
import { get, map } from 'lodash';
const names = map(people, (person) => get(person, 'name'));
//=> ['Alice', 'Bob']
Normal
Data-last
const people = [ { name: 'Alice' }, { name: 'Bob' } ];
import { get, map } from 'lodash/fp';
const names = map(get('name'))(people);
//=> ['Alice', 'Bob']
import { get, map } from 'lodash/fp';
const getNames = map(get('name'));
const names = getNames(people);
//=> ['Alice', 'Bob']
Point-Free
foo(function(v) {
return bar(v);
});
foo(function(v) {
return bar(v);
});
foo(bar);
function isOdd(n) {
return n % 2 == 1;
}
function isEven(n) {
return !isOdd(n);
}
isEven(4); // true
function negate(fn) {
return function negated(...args) {
return !fn(...args);
};
}
function isOdd(n) {
return n % 2 == 1;
}
const isEven = negate(isOdd);
isEven(4); // true
Composition
function compose(f, g) {
return function(x) {
return f(g(x));
}
}
compose(reverse, capitalize)('hello');
// => 'olleH'
'olleH' <= 'Hello' <= 'hello'
compose(capitalize, reverse)('hello');
// => 'Olleh'
'Olleh' <= 'olleh' <= 'hello'
function compose(f, g) {
return function(x) {
return f(g(x));
}
}
pipe(capitalize, reverse)('hello');
// => 'olleH'
'hello' => 'Hello' => 'olleH'
function pipe(f, g) {
return function(x) {
return g(f(x));
}
}
Example
const scientists = [
{
firstName: 'Stephen',
lastName: 'Hawking',
focus: 'Physics',
},
{
firstName: 'Charles',
lastName: 'Darwin',
focus: 'Biology',
},
{
firstName: 'Marie',
lastName: 'Curie',
focus: 'Physics',
},
];
const formatName = (p) => `${p.firstName} ${p.lastName}`;
import { filter, sortBy, map } from 'lodash';
const filtered = filter(scientists, { focus: 'Physics' });
const sorted = sortBy(filtered, 'firstName');
map(sorted, formatName);
// => [ 'Marie Curie', 'Stephen Hawking' ]
import { map, sortBy, filter } from 'lodash';
map(
sortBy(
filter(scientists, { focus: 'Physics' }),
'firstName'
),
formatName
);
// => [ 'Marie Curie', 'Stephen Hawking' ]
import { chain } from 'lodash';
chain(scientists)
.filter({ focus: 'Physics' })
.sortBy('firstName')
.map(formatName)
.value();
// => [ 'Marie Curie', 'Stephen Hawking' ]
import { compose, map, sortBy, filter } from 'lodash/fp';
compose(
map(formatName),
sortBy('firstName'),
filter({ focus: 'Physics' }),
)(scientists);
// => [ 'Marie Curie', 'Stephen Hawking' ]
import { pipe, filter, sortBy, map } from 'lodash/fp';
pipe(
filter({ focus: 'Physics' }),
sortBy('firstName'),
map(formatName),
)(scientists);
// => [ 'Marie Curie', 'Stephen Hawking' ]
Questions
-
Brian Lonsdorf (@drboolean)
-
Kyle Simpson (@getify)
- Hardcore Functional Programming in JavaScript
https://frontendmasters.com/courses/functional-javascript/
- Mostly Adequate Guide to Functional Programming
https://drboolean.gitbooks.io/mostly-adequate-guide/
- Functional-Light JavaScript
https://github.com/getify/Functional-Light-JS
https://frontendmasters.com/courses/functional-javascript-v2/
Pure Functions
A function can be considered pure if it:
-
Evaluates to the same result given the same arguments
-
Does not cause observable side effects
f(x) = x^2
f(x)=x2
function square(x) {
return Math.pow(x, 2);
}
square(1);
// => 1
square(2);
// => 4
square(-3);
// => 9
let b = 0;
function foo(a) {
b++;
return a + b;
}
foo(0);
//=> 1
foo(0);
//=> 2
foo(0);
//=> 3
Side Effects
- I/O
- Network Requests
- Mutation of global state
- DOM Manipulation
- Timestamps
- Math.random()
Advantages
- Easier to reason about
- Portable
- Easier to test
- Memoizable
- Parallelizable
Functional Programming in
By unicode
Functional Programming in
- 165