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; 
      var 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; 
      var 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;
      };
    };
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset