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

results matching ""

    No results matching ""