Functional Programming Examples
Douglas Crockford The Good Parts of JavaScript and the Web
All functions below are taken directly from Crockford's Frontend Masters course
// An identity function that takes an argument and returns that argument
function identity(x) {
return x;
}
// Binary functions for adding, subtracting, and multiplying
function add(first, second) {
return first + second;
}
function sub(first, second) {
return first - second;
}
function mul(first, second) {
return first * second;
}
// A function that takes an argument and returns a function that returns that argument
function identifyf(x) {
return function() {
return x;
}
}
var three = identifyf(3);
three() // 3
// A function that adds from two invocations
function add(first) {
return function (second) {
return first + second;
};
}
add(3)(4) // 7
// A function that takes a binary function and makes it callable with two invocations.
function liftf(binary) {
return function (first) {
return function (second) {
return binary(first, second);
};
};
}
var addf = liftf(add);
addf(3)(4) // 7
liftf(mul)(5)(6) // 30
// A function that takes a binary function and an argument, and returns a function that can take a second
// argument
function curry(binary, first) {
return liftf(binary)(first);
}
// A function that increments a value by one, three different ways
var inc = addf(1);
var inc = liftf(add)(1);
var inc = curry(add, 1);
// A function that takes a binary function and returns a unary function that passes its arugment
// to the binary function twice
function twice(binary) {
return function(a) {
return binary(a, a);
};
}
var doubl = twice(add);
doubl(11) // 22
var square = twice(mul);
square(11) // 121
// A function that reverses the arguments of a binary function
function reverse(binary) {
return function (first, second) {
return binary(second, first);
};
}
var bus = reverse(sub);
bus(3, 2) // -1
// A function that takes two unary function and returns a unary function that calls them both.
function composeu(f, g) {
return function(a) {
// note left to right
return g(f(a));
};
}
composeu(doubl, square)(5) // 100
// A function that take two binary functions and return a function that calls them both;
function composeb(f, g) {
return function(a, b, c) {
return g(f(a, b), c);
};
}
composeb(add, mul)(2, 3, 7) // 35
// A function that allows a binary function to be called a limited number of times.
function limit(binary, count) {
return function (a, b) {
if (count >= 1) {
count -= 1;
return binary(a,b);
}
return undefined;
};
}
var add_ltd = limit(add, 1);
add_ltd(3, 4) // 7
add_ltd(3, 5) // undefined
// A function that produces a generator that will produce a series of values.
function from(start) {
return function () {
var next = start;
start += 1;
return next;
};
}
var index = from(0);
index() // 0
index() // 1
index() // 2
//A function that takes a generator and an end value and returns a generator that will produce
// numbers up to that limit.
function to(gen, end) {
return function () {
var value = gen();
if (value < end) {
return value;
}
return undefined;
};
}
var index = to(from(1), 3);
index() // 1
index() // 2
index() // undefined
// A function that produces a generator that will produce values in a range.
function fromTo(start, end) {
return to(
from(start),
end
);
}
var index = fromTo(0, 3);
index() // 0
index() // 1
index() // 2
index() // undefined;
// A function that takes an array and a generator and returns a generator that will produce elements from
// the array
function element(array, gen) {
return function () {
var index = gen();
if (index !== undefined) {
return array[index];
}
};
}
var ele = element(['a', 'b', 'c', 'd'], fromTo(1, 3));
ele() // 'b'
ele() // 'c'
ele() // undefined
// Modifies the element function so that the generator argument is optional. If no generator, each of the
// elements of the array will be produced.
function element(array, gen) {
if (gen === undefined) {
gen = fromTo(
0,
array.length
);
}
return function () {
var index = gen();
if (index !== undefined) {
return array[index];
}
};
}
var ele = element(['a', 'b', 'c', 'd']);
ele() // 'a'
ele() // 'b'
ele() // 'c'
ele() // 'd'
ele() // undefined
// A collect function that takes a generator and an array and produces a function that will
// collect the results in the array.
function collect(gen, array) {
return function () {
var value = gen();
if (value !== undefined) {
array.push(value);
}
return value;
};
}
var array = [];
var col = collect(fromTO(0, 2), array);
col() // 0
col() // 1
col() // undefined
array // [0, 1]
// A filter function that takes a generator and a predicate and produces a generator that produces
// only the values approved by the predicate.
function filter(gen, predicate) {
return function recur() {
var value = gen();
if (value === undefined || predicate(value)) {
return value;
}
return recur();
};
}
var fil = filter(fromTo(0, 5), (val) => val % 3 === 0);
fil() // 0
fil() // 3
fil() // undefined
// A function that takes two (or more) generators and produces a generator that combines the two sequences.
function concat(...gens) {
var next = element(gens);
var gen = next();
return function recur() {
var value = gen();
if (value === undefined) {
gen = next();
if (gen !== undefined) {
return recur();
}
}
return value;
};
}
var con = concat(fromTo(0, 3), fromTo(0, 2));
con() // 0
con() // 1
con() // 2
con() // 0
con() // 1
con() // undefined
// A function that takes a generator and calls it until it returns undefined
function repeat(gen) {
if (gen() !== undefined) {
return repeat(gen);
}
}
var array = [];
repeat(collect(fromTo(0, 4), array));
console.log(array); // 0, 1, 2, 3
// A map function that takes an array and a unary function, and returns an array containing
// the result of passing each element to the unary function.
function map(array, unary) {
var ele = element(array);
var result = [];
repeat(collect(function () {
var value = ele();
if (value !== undefined) {
return unary(value);
}
}, result));
return result;
}
map([2, 1, 0], inc) // [3, 2, 1]
// A reduce function that takes an array and a binary function, and returns a single value.
function reduce(array, binary) {
var ele = element(array)
var result;
repeat(function () {
var value = ele();
if (value !== undefined) {
result = result === undefined ? value : binary(result, value);
}
return value;
});
return result;
}
reduce([], add) // undefined
reduce([2], add) // 2
reduce([2,1,0], add) // 3
// A function that makes a function that generates unique symbols.
function gensymf(prefix) {
var number = 0;
return function() {
number += 1;
return prefix + number;
};
}
var geng = gensymf("G");
var genH = gensymf("H");
geng() // "G1"
genh() // "H1"
geng() // "G2"
genh() // "H2"
// A function that takes a unary function and a seed and returns a gensymf
function gensymff(unary, seed) {
return function (prefix) {
var number = seed;
return function () {
number = unary(number);
return prefix + number;
};
};
}
var gensymf = gensymff(inc, 0)
var geng = gensymf("G");
geng() // "G1"
geng() // "G2"
// A function that returns a generator that will return the next fibonacci number.
function fibonaccif(a, b) {
return function () {
var next = a;
a = b;
b += next;
return next;
};
}
var fib = fibonaccif(0, 1);
fib() // 0
fib() // 1
fib() // 1
fib() // 2
fib() // 3
fib() // 5
// A counter function that returns an object containing two functions that implement and up/down
// counter, hiding the counter.
function counter(value) {
return {
up: function () {
value += 1;
return value;
},
down: function () {
value -= 1;
return value;
}
};
}
var object = counter(10)
var up = object.up
var down = object.down
up() // 11
down() // 10
down() // 9
// A function that takes a binary function, and returns an object containing an invoke function that
// can invoke the binary function, and a revoke function that disables the invoke function.
function revocable(binary) {
return {
invoke: function (first, second) {
if (binary !== undefined) {
return binary(
first,
second
);
}
},
revoke: function() {
binary = undefined;
}
};
}
var rev = revocable(add)
var add_rev = rev.invoke;
add_rev(3, 4); // 7
rev.revoke();
add_rev(5, 7); //undefined
// A function that takes a value and an optional source string and returns them in
// an object
function m(value, source) {
return {
value: value,
source: (typeof source === 'string') ? source : Sting(value)
};
}
JSON.stringify(m(1)) // {"value": 1, "source": "1"}
JSON.stringify(m(Math.PI, "pi")) // {"value": 3.14159..., "source": "pi"}
// A function that takes two m objects and returns an m object
function addm(a, b) {
return m(
a.value + b.value,
"(" + a.source + "+" + b.source + ")"
);
}
JSON.stringify(addm(m(3), m(4))) // {"value": 7, "source": "(3+4)"}
JSON.stringify(addm(m(1), m(Math.PI, "pi"))) // {"value": 4.14159..., "source": "(1+pi)"}
// A function that takes a binary function and a string and returns a function that acts on m objects.
function liftm(binary, op) {
return function(a, b) {
return m(
binary(a.value, b.value),
"(" + a.source + op + b.source + ")"
);
};
}
var addm = liftm(add, "+");
JSON.stringify(addm(m(3), m(4))) // {"value": 7, "source": "(3+4)"}
JSON.stringify(liftm(mul, "*")(m(3), m(4))) // {"value": 12, "source": "(3*4)"}
// A function that evaluates simple array expressions;
function exp(value) {
return (array.isArray(value)) ? value[0](value[1], value[2]) : value;
}
var sae = [mul, 5, 11];
exp(sae) // 55
exp(42) // 42
// Modifies exp to evaluate nested array expressions
function exp(value) {
return (Array.isArray(value)) ? value[0](exp(value[1]), exp(value[2]) : value;
}
var nae = [
Math.sqrt,
[
add,
[square, 3],
[square, 4]
]
];
exp(nae) // 5
// A function that adds from many invocations, until it sees an empty invocation.
function addg(first) {
function more(next) {
if (next === undefined) {
return first;
}
first += next;
return more;
}
if (first !== undefined) {
return more;
}
}
addg() // undefined
addg(2)() // 2
addg(2)(7)() // 9
// A function that will take a binary function and apply it to many invocations.
function liftg(binary) {
return function(first) {
if (first === undefined) {
return first;
}
return function more(next) {
if (next === undefined) {
return first;
}
first = binary(first, next);
return more;
}
}
}
liftg(mul)() // undefined
liftg(mul)(3)() // 3
liftg(mul)(1)(2)(4)(8)() // 64
// A function that will build an array from many invocations.
function arrayg(first) {
var array = [];
function more(next) {
if (next === undefined) {
return array;
}
array.push(next);
return more;
}
return more(first);
}
arrayg() // []
arrayg(3)() // [3]
arrayg(3)(4)(5)() // [3, 4, 5]
// A function that takes a unary function, and returns a function that takes a callback and an
// some arguments
function continuize(any) {
return function(callback, ...x) {
return callback(any(...x));
};
}
sqrtc = continuize(Math.sqrt);
sqrtc(console.log, 81) // 9