Common Functions for Functional Programming in JavaScript - Functional Programming in JavaScript (2015)

Functional Programming in JavaScript (2015)

Appendix A. Common Functions for Functional Programming in JavaScript

This Appendix covers common functions for functional programming in JavaScript:

· Array Functions:

· var flatten = function(arrays) {

· return arrays.reduce( function(p,n){

· return p.concat(n);

· });

· };

·

· var invert = function(arr) {

· return arr.map(function(x, i, a) {

· return a[a.length - (i+1)];

· });

};

· Binding Functions:

· var bind = Function.prototype.call.bind(Function.prototype.bind);

· var call = bind(Function.prototype.call, Function.prototype.call);

var apply = bind(Function.prototype.call, Function.prototype.apply);

· Category Theory:

· var checkTypes = function( typeSafeties ) {

· arrayOf(func)(arr(typeSafeties));

· var argLength = typeSafeties.length;

· return function(args) {

· arr(args);

· if (args.length != argLength) {

· throw new TypeError('Expected '+ argLength + ' arguments');

· }

· var results = [];

· for (var i=0; i<argLength; i++) {

· results[i] = typeSafeties[i](args[i]);

· }

· return results;

· };

· };

·

· var homoMorph = function( /* arg1, arg2, ..., argN, output */ ) {

· var before = checkTypes(arrayOf(func)(Array.prototype.slice.call(arguments, 0, arguments.length-1)));

· var after = func(arguments[arguments.length-1])

· return function(middle) {

· return function(args) {

· return after(middle.apply(this, before([].slice.apply(arguments))));

· };

· };

};

· Composition:

· Function.prototype.compose = function(prevFunc) {

· var nextFunc = this;

· return function() {

· return nextFunc.call(this,prevFunc.apply(this,arguments));

· };

· };

·

· Function.prototype.sequence = function(prevFunc) {

· var nextFunc = this;

· return function() {

· return prevFunc.call(this,nextFunc.apply(this,arguments));

· };

};

· Currying:

· Function.prototype.curry = function (numArgs) {

· var func = this;

· numArgs = numArgs || func.length;

· // recursively acquire the arguments

· function subCurry(prev) {

· return function (arg) {

· var args = prev.concat(arg);

· if (args.length < numArgs) {

· // recursive case: we still need more args

· return subCurry(args);

· }

· else {

· // base case: apply the function

· return func.apply(this, args);

· }

· };

· };

· return subCurry([]);

};

· Functors:

· // map :: (a -> b) -> [a] -> [b]

· var map = function(f, a) {

· return arr(a).map(func(f));

· }

·

· // strmap :: (str -> str) -> str -> str

· var strmap = function(f, s) {

· return str(s).split('').map(func(f)).join('');

· }

·

· // fcompose :: (a -> b)* -> (a -> b)

· var fcompose = function() {

· var funcs = arrayOf(func)(arguments);

· return function() {

· var argsOfFuncs = arguments;

· for (var i = funcs.length; i > 0; i -= 1) {

· argsOfFuncs = [funcs[i].apply(this, args)];

· }

· return args[0];

· };

};

· Lenses:

· var lens = function(get, set) {

· var f = function (a) {return get(a)};

· f.get = function (a) {return get(a)};

· f.set = set;

· f.mod = function (f, a) {return set(a, f(get(a)))};

· return f;

· };

·

· // usage:

· var first = lens(

· function (a) { return arr(a)[0]; }, // get

· function (a, b) { return [b].concat(arr(a).slice(1)); } // set

);

· Maybes:

· var Maybe = function(){};

· Maybe.prototype.orElse = function(y) {

· if (this instanceof Just) {

· return this.x;

· }

· else {

· return y;

· }

· };

·

· var None = function(){};

· None.prototype = Object.create(Maybe.prototype);

· None.prototype.toString = function(){return 'None';};

· var none = function(){return new None()};

· // and the Just instance, a wrapper for an object with a value

· var Just = function(x){return this.x = x;};

· Just.prototype = Object.create(Maybe.prototype);

· Just.prototype.toString = function(){return "Just "+this.x;};

· var just = function(x) {return new Just(x)};

· var maybe = function(m){

· if (m instanceof None) {

· return m;

· }

· else if (m instanceof Just) {

· return just(m.x);

· }

· else {

· throw new TypeError("Error: Just or None expected, " + m.toString() + " given.");

· }

· };

·

· var maybeOf = function(f){

· return function(m) {

· if (m instanceof None) {

· return m;

· }

· else if (m instanceof Just) {

· return just(f(m.x));

· }

· else {

· throw new TypeError("Error: Just or None expected, " + m.toString() + " given.");

· }

· };

};

· Mixins:

· Object.prototype.plusMixin = function(mixin) {

· var newObj = this;

· newObj.prototype = Object.create(this.prototype);

· newObj.prototype.constructor = newObj;

· for (var prop in mixin) {

· if (mixin.hasOwnProperty(prop)) {

· newObj.prototype[prop] = mixin[prop];

· }

· }

· return newObj;

};

· Partial Application:

· function bindFirstArg(func, a) {

· return function(b) {

· return func(a, b);

· };

· };

·

· Function.prototype.partialApply = function(){

· var func = this;

· args = Array.prototype.slice.call(arguments);

· return function(){

· return func.apply(this, args.concat(

· Array.prototype.slice.call(arguments)

· ));

· };

· };

·

· Function.prototype.partialApplyRight = function(){

· var func = this;

· args = Array.prototype.slice.call(arguments);

· return function(){

· return func.apply(

· this,

· Array.protype.slice.call(arguments, 0)

· .concat(args));

· };

};

· Trampolining:

· var trampoline = function(f) {

· while (f && f instanceof Function) {

· f = f.apply(f.context, f.args);

· }

· return f;

· };

·

· var thunk = function (fn) {

· return function() {

· var args = Array.prototype.slice.apply(arguments);

· return function() { return fn.apply(this, args); };

· };

};

· Type Safeties:

· var typeOf = function(type) {

· return function(x) {

· if (typeof x === type) {

· return x;

· }

· else {

· throw new TypeError("Error: "+type+" expected, "+typeof x+" given.");

· }

· };

· };

·

· var str = typeOf('string'),

· num = typeOf('number'),

· func = typeOf('function'),

· bool = typeOf('boolean');

·

· var objectTypeOf = function(name) {

· return function(o) {

· if (Object.prototype.toString.call(o) === "[object "+name+"]") {

· return o;

· }

· else {

· throw new TypeError("Error: '+name+' expected, something else given.");

· }

· };

· };

· var obj = objectTypeOf('Object');

· var arr = objectTypeOf('Array');

· var date = objectTypeOf('Date');

· var div = objectTypeOf('HTMLDivElement');

·

· // arrayOf :: (a -> b) -> ([a] -> [b])

· var arrayOf = function(f) {

· return function(a) {

· return map(func(f), arr(a));

· }

};

· Y-combinator:

· var Y = function(F) {

· return (function (f) {

· return f(f);

· }(function (f) {

· return F(function (x) {

· return f(f)(x);

· });

· }));

· };

·

· // Memoizing Y-Combinator:

· var Ymem = function(F, cache) {

· if (!cache) {

· cache = {} ; // Create a new cache.

· }

· return function(arg) {

· if (cache[arg]) {

· // Answer in cache

· return cache[arg] ;

· }

· // else compute the answer

· var answer = (F(function(n){

· return (Ymem(F,cache))(n);

· }))(arg); // Compute the answer.

· cache[arg] = answer; // Cache the answer.

· return answer;

· };

};