Source: lib/index.js

// Copyright (c) 2013-2014 Quildreen Motta <quildreen@gmail.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/**
 * Provides pure functional combinators.
 *
 * @module Core/Lambda
 */

// -- Aliases ----------------------------------------------------------
var toArray = Function.call.bind([].slice)


// -- Implementation ---------------------------------------------------

/**
 * The identity combinator. Always returns the argument it's given.
 *
 * @example
 *   identity(3)        // => 3
 *   identity([1])      // => [1]
 *
 * @summary a → a
 */
exports.identity = identity
function identity(a) {
  return a
}


/**
 * The constant combinator. Always returns the first argument it's given.
 *
 * @example
 *   constant(3)(2)             // => 3
 *   constant('foo')([1])       // => 'foo'
 *
 * @summary a → b → a
 */
exports.constant = curry(2, constant)
function constant(a, b) {
  return a
}


/**
 * Applies a function to an argument.
 *
 * @example
 *   var inc = (a) => a + 1
 *   apply(inc)(3)      // => 4
 *
 * @summary (a → b) → a → b
 */
exports.apply = curry(2, apply)
function apply(f, a) {
  return f(a)
}


/**
 * Inverts the order of the parameters of a binary function.
 *
 * @example
 *   var subtract = (a) => (b) => a - b
 *   subtract(3)(2)             // => 1
 *   flip(subtract)(3)(2)       // => -1
 *
 * @summary (a → b → c) → (b → a → c)
 */
exports.flip = curry(3, flip)
function flip(f, a, b) {
  return f(b)(a)
}


/**
 * Composes two functions together.
 *
 * @example
 *   var inc    = (a) => a + 1
 *   var square = (a) => a * a
 *   compose(inc)(square)(2)    // => 5,        inc(square(2))
 *
 * @summary (b → c) → (a → b) → (a → c)
 */
exports.compose = curry(3, compose)
function compose(f, g, a) {
  return f(g(a))
}


/**
 * Transforms any function on tuples into a curried function.
 *
 * @example
 *   var sub3 = (a, b, c) => a - b - c
 *   curry(3, sub3)(5)(2)(1)   // => 2
 *
 * @summary Number → ((a1, a2, ..., aN) → b) → (a1 → a2 → ... → aN → b)
 */
exports.curry = curry(2, curry)
function curry(n, f) {
  return curried([])

  function curried(args) {
    return function() {
      var newArgs  = toArray(arguments)
      var allArgs  = args.concat(newArgs)
      var argCount = allArgs.length

      return argCount < n?    curried(allArgs)
      :      argCount === n?  f.apply(null, allArgs)
      :      /* > n */        f.apply(null, allArgs.slice(0, n))
                               .apply(null, allArgs.slice(n)) }}
}


/**
 * Transforms a curried function into one accepting a list of arguments.
 *
 * @example
 *   var add = (a) => (b) => a + b
 *   spread(add)([3, 2])        // => 5
 *
 * @summary (a1 → a2 → ... → aN → b) → (#[a1, a2, ..., aN) → b)
 */
exports.spread = curry(2, spread)
function spread(f, as) {
  return as.reduce(function(g, a) { return g(a) }, f)
}


/**
 * Transforms a curried function into a function on tuples.
 *
 * @example
 *   var add = (a) => (b) => a + b
 *   uncurry(add)(3, 2)         // => 5
 *
 * @summary (a1 → a2 → ... → aN → b) → ((a1, a2, ..., aN) → b)
 */
exports.uncurry = uncurry
function uncurry(f) {
  return function() { return spread(f, toArray(arguments)) }
}


/**
 * Applies an unary function to both arguments of a binary function.
 *
 * @example
 *   var xss = [[1, 2], [3, 1], [-2, 4]]
 *   sortBy(upon(compare, first))
 *
 * @summary (b → b → c) → (a → b) → (a → a → c)
 */
exports.upon = curry(4, upon)
function upon(f, g, a, b) {
  return f(g(a))(g(b))
}