Book Image

Functional Programming in JavaScript

By : Dan Mantyla
Book Image

Functional Programming in JavaScript

By: Dan Mantyla

Overview of this book

<p>This is a fast-paced guide that will help you to write real-world applications by utilizing a wide range of functional techniques and styles.</p> <p>The book first explores the core concepts of functional programming common to all functional languages, with examples of their use in JavaScript. It's followed by a comprehensive roundup of functional programming libraries for JavaScript that minimizes the burden of digging deep into JavaScript to expose a set of tools that makes functional programming not just possible but highly convenient. The book then rounds off with an overview of methods to effectively use and mix functional programming with object-oriented programming.</p>
Table of Contents (16 chapters)
Functional Programming in JavaScript
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Common Functions for Functional Programming in JavaScript
Glossary of Terms
Index

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;
      };
    };