[gnome-js-common] Add functional.js to modules



commit 805f9f5148b5f45803c6067d1f7a9b78d26594bf
Author: Robert Carr <racarr svn gnome org>
Date:   Mon May 25 05:24:48 2009 -0400

    Add functional.js to modules
---
 modules/functional.js |  821 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 821 insertions(+), 0 deletions(-)

diff --git a/modules/functional.js b/modules/functional.js
new file mode 100644
index 0000000..b3c1c88
--- /dev/null
+++ b/modules/functional.js
@@ -0,0 +1,821 @@
+/*
+ * Author: Oliver Steele
+ * Copyright: Copyright 2007 by Oliver Steele.  All rights reserved.
+ * License: MIT License
+ * Homepage: http://osteele.com/javascripts/functional
+ * Source: http://osteele.com/javascripts/functional/functional.js
+ * Changes: http://osteele.com/javascripts/functional/CHANGES
+ * Created: 2007-07-11
+ * Version: 1.0.2
+ *
+ *
+ * This file defines some higher-order methods and functions for
+ * functional and function-level programming.
+ */
+window = this;
+/// `Functional` is the namespace for higher-order functions.
+var Functional = this;
+
+/**
+ * This function copies all the public functions in `Functional` except itself
+ * into the global namespace.  If the optional argument $except$ is present,
+ * functions named by its property names are not copied.
+ * >> Functional.install()
+ */
+Functional.install = function(except) {
+    var source = Functional,
+        target = window;
+    for (var name in source)
+        name == 'install'
+        || name.charAt(0) == '_'
+        || except && name in except
+        || {}[name] // work around Prototype
+        || (target[name] = source[name]);
+}
+
+/// ^ Higher-order functions
+
+/**
+ * Returns a function that applies the last argument of this
+ * function to its input, and the penultimate argument to the
+ * result of the application, and so on.
+ * == compose(f1, f2, f3..., fn)(args) == f1(f2(f3(...(fn(args...)))))
+ * :: (a2 -> a1) (a3 -> a2)... (a... -> a_{n}) -> a... -> a1
+ * >> compose('1+', '2*')(2) -> 5
+ */
+Functional.compose = function(/*fn...*/) {
+    var fns = Functional.map(Function.toFunction, arguments),
+        arglen = fns.length;
+    return function() {
+        for (var i = arglen; --i >= 0; )
+            arguments = [fns[i].apply(this, arguments)];
+        return arguments[0];
+    }
+}
+
+/**
+ * Same as `compose`, except applies the functions in argument-list order.
+ * == sequence(f1, f2, f3..., fn)(args...) == fn(...(f3(f2(f1(args...)))))
+ * :: (a... -> a1) (a1 -> a2) (a2 -> a3)... (a_{n-1} -> a_{n})  -> a... -> a_{n}
+ * >> sequence('1+', '2*')(2) -> 6
+ */
+Functional.sequence = function(/*fn...*/) {
+    var fns = Functional.map(Function.toFunction, arguments),
+        arglen = fns.length;
+    return function() {
+        for (var i = 0; i < arglen; i++)
+            arguments = [fns[i].apply(this, arguments)];
+        return arguments[0];
+    }
+}
+
+/**
+ * Applies `fn` to each element of `sequence`.
+ * == map(f, [x1, x2...]) = [f(x, 0), f(x2, 1), ...]
+ * :: (a ix -> boolean) [a] -> [a]
+ * >> map('1+', [1,2,3]) -> [2, 3, 4]
+ *
+ * If `object` is supplied, it is the object of the call.
+ *
+ * The fusion rule:
+ * >> map('+1', map('*2', [1,2,3])) -> [3, 5, 7]
+ * >> map(compose('+1', '*2'), [1,2,3]) -> [3, 5, 7]
+ */
+Functional.map = function(fn, sequence, object) {
+    fn = Function.toFunction(fn);
+    var len = sequence.length,
+        result = new Array(len);
+    for (var i = 0; i < len; i++)
+        result[i] = fn.apply(object, [sequence[i], i]);
+    return result;
+}
+
+/**
+ * Applies `fn` to `init` and the first element of `sequence`,
+ * and then to the result and the second element, and so on.
+ * == reduce(f, init, [x0, x1, x2]) == f(f(f(init, x0), x1), x2)
+ * :: (a b -> a) a [b] -> a
+ * >> reduce('x y -> 2*x+y', 0, [1,0,1,0]) -> 10
+ */
+Functional.reduce = function(fn, init, sequence, object) {
+    fn = Function.toFunction(fn);
+    var len = sequence.length,
+        result = init;
+    for (var i = 0; i < len; i++)
+        result = fn.apply(object, [result, sequence[i]]);
+    return result;
+}
+
+/**
+ * Returns a list of those elements $x$ of `sequence` such that
+ * $fn(x)$ returns true.
+ * :: (a -> boolean) [a] -> [a]
+ * >> select('%2', [1,2,3,4]) -> [1, 3]
+ */
+Functional.select = function(fn, sequence, object) {
+    fn = Function.toFunction(fn);
+    var len = sequence.length,
+        result = [];
+    for (var i = 0; i < len; i++) {
+        var x = sequence[i];
+        fn.apply(object, [x, i]) && result.push(x);
+    }
+    return result;
+}
+
+/// A synonym for `select`.
+Functional.filter = Functional.select;
+
+/// A synonym for `reduce`.
+Functional.foldl = Functional.reduce;
+
+/**
+ * Same as `foldl`, but applies the function from right to left.
+ * == foldr(f, init, [x0, x1, x2]) == fn(x0, f(x1, f(x2, init)))
+ * :: (a b -> b) b [a] -> b
+ * >> foldr('x y -> 2*x+y', 100, [1,0,1,0]) -> 104
+ */
+Functional.foldr = function(fn, init, sequence, object) {
+    fn = Function.toFunction(fn);
+    var len = sequence.length,
+        result = init;
+    for (var i = len; --i >= 0; )
+        result = fn.apply(object, [sequence[i], result]);
+    return result;
+}
+
+/// ^^ Predicates
+
+/**
+ * Returns a function that returns `true` when all the arguments, applied
+ * to the returned function's arguments, returns true.
+ * == and(f1, f2...)(args...) == f1(args...) && f2(args...)...
+ * :: [a -> boolean] a -> a
+ * >> and('>1', '>2')(2) -> false
+ * >> and('>1', '>2')(3) -> true
+ * >> and('>1', 'error()')(1) -> false
+ */
+Functional.and = function(/*functions...*/) {
+    var args = Functional.map(Function.toFunction, arguments),
+        arglen = args.length;
+    return function() {
+        var value = true;
+        for (var i = 0; i < arglen; i++)
+            if (!(value = args[i].apply(this, arguments)))
+                break;
+        return value;
+    }
+}
+
+/**
+ * Returns a function that returns `true` when any argument, applied
+ * to the returned function's arguments, returns true.
+ * == or(f1, f2...)(args...) == f1(args...) || f2(args...)...
+ * :: [a -> boolean] a -> a
+ * >> or('>1', '>2')(1) -> false
+ * >> or('>1', '>2')(2) -> true
+ * >> or('>1', 'error()')(2) -> true
+ */
+Functional.or = function(/*functions...*/) {
+    var args = Functional.map(Function.toFunction, arguments),
+        arglen = args.length;
+    return function() {
+        var value = false;
+        for (var i = 0; i < arglen; i++)
+            if ((value = args[i].apply(this, arguments)))
+                break;
+        return value;
+    }
+}
+
+/**
+ * Returns true when $fn(x)$ returns true for some element $x$ of
+ * `sequence`.  The returned function short-circuits.
+ * == some(f, [x1, x2, x3, ...]) == f(x1) || f(x2) || f(x3)...
+ * :: (a -> boolean) [a] -> boolean
+ * >> some('>2', [1,2,3]) -> true
+ * >> some('>10', [1,2,3]) -> false
+ */
+Functional.some = function(fn, sequence, object) {
+    fn = Function.toFunction(fn);
+    var len = sequence.length,
+        value = false;
+    for (var i = 0; i < len; i++)
+        if ((value = fn.call(object, sequence[i])))
+            break;
+    return value;
+}
+
+/**
+ * Returns true when $fn(x)$ returns true for every element $x$ of
+ * `sequence`.  The returned function short-circuits.
+ * == every(f, [x1, x2, x3, ...]) == f(x1) && f(x2) && f(x3)...
+ * :: (a -> boolean) [a] -> boolean
+ * >> every('<2', [1,2,3]) -> false
+ * >> every('<10', [1,2,3]) -> true
+ */
+Functional.every = function(fn, sequence, object) {
+    fn = Function.toFunction(fn);
+    var len = sequence.length,
+        value = true;
+    for (var i = 0; i < len; i++)
+        if (!(value = fn.call(object, sequence[i])))
+            break;
+    return value;
+}
+
+/**
+ * Returns a function that returns `true` when $fn()$ returns false.
+ * == f.not()(args...) == !f(args...)
+ * :: (a -> boolean) -> (a -> boolean)
+ * >> not(Functional.K(true))() -> false
+ * >> not(Functional.K(false))() -> true
+ */
+Functional.not = function(fn) {
+    fn = Function.toFunction(fn);
+    return function() {
+        return !fn.apply(null, arguments);
+    }
+}
+
+/**
+ * Returns a function that returns true when this function's arguments
+ * applied to that functions are always the same.  The returned function
+ * short-circuits.
+ * == equal(f1, f2...)(args...) == f1(args...) == f2(args...)...
+ * :: [a... -> b] -> a... -> b
+ * >> equal()() -> true
+ * >> equal(K(1))() -> true
+ * >> equal(K(1), K(1))() -> true
+ * >> equal(K(1), K(2))() -> false
+ * >> equal(K(1), K(2), 'error()')() -> false
+ */
+Functional.equal = function(/*fn...*/) {
+    var arglen = arguments.length,
+        args = Functional.map(Function.toFunction, arguments);
+    if (!arglen) return Functional.K(true);
+    // if arglen == 1 it's also constant true, but
+    // call it for effect.
+    return function() {
+        var value = args[0].apply(this, arguments);
+        for (var i = 1; i < arglen; i++)
+            if (value != args[i].apply(this, args))
+                return false;
+        return true;
+    }
+}
+
+
+/// ^^ Utilities
+
+/**
+  * Returns its argument coerced to a function.
+  * >> lambda('1+')(2) -> 3
+  * >> lambda(function(n){return n+1})(2) -> 3
+  */
+Functional.lambda = function(object) {
+    return object.toFunction();
+}
+
+
+/**
+ * Returns a function that takes an object as an argument, and applies
+ * `object`'s `methodName` method to `arguments`.
+ * == invoke(name)(object, args...) == object[name](args...)
+ * :: name args... -> object args2... -> object[name](args... args2...)
+ * >> invoke('toString')(123) -> "123"
+ */
+Functional.invoke = function(methodName/*, arguments*/) {
+    var args = Array.slice(arguments, 1);
+    return function(object) {
+        return object[methodName].apply(object, Array.slice(arguments, 1).concat(args));
+    }
+}
+
+/**
+ * Returns a function that takes an object, and returns the value of its
+ * `name` property.  `pluck(name)` is equivalent to `'_.name'.lambda()`.
+ * == pluck(name)(object) == object[name]
+ * :: name -> object -> object[name]
+ * >> pluck('length')("abc") -> 3
+ */
+Functional.pluck = function(name) {
+    return function(object) {
+        return object[name];
+    }
+}
+
+/**
+ * Returns a function that, while $pred(value)$ is true, applies `fn` to
+ * $value$ to produce a new value, which is used as an input for the next round.
+ * The returned function returns the first $value$ for which $pred(value)$
+ * is false.
+ * :: (a -> boolean) (a -> a) -> a
+ * >> until('>10', '2*')(1) -> 16
+ */
+Functional.until = function(pred, fn) {
+    fn = Function.toFunction(fn);
+    pred = Function.toFunction(pred);
+    return function(value) {
+        while (!pred.call(null, value))
+            value = fn.call(null, value);
+        return value;
+    }
+}
+
+/**
+ * :: [a] [b]... -> [[a b]...]
+ * == zip(a, b...) == [[a0, b0], [a1, b1], ...]
+ * Did you know that `zip` can transpose a matrix?
+ * >> zip.apply(null, [[1,2],[3,4]]) -> [[1, 3], [2, 4]]
+ */
+Functional.zip = function(/*args...*/) {
+    var n = Math.min.apply(null, Functional.map('.length', arguments));
+    var results = new Array(n);
+    for (var i = 0; i < n; i++) {
+        var key = String(i);
+        results[key] = Functional.map(pluck(key), arguments);
+    };
+    return results;
+}
+
+Functional._startRecordingMethodChanges = function(object) {
+    var initialMethods = {};
+    for (var name in object)
+        initialMethods[name] = object[name];
+    return {getChangedMethods: function() {
+        var changedMethods = {};
+        for (var name in object)
+        if (object[name] != initialMethods[name])
+            changedMethods[name] = object[name];
+        return changedMethods;
+    }};
+}
+
+// For each method that this file defined on `Function.prototype`,
+// define a function on `Functional` that delegates to it.
+Functional._attachMethodDelegates = function(methods) {
+    for (var name in methods)
+        Functional[name] = Functional[name] || (function(name) {
+            var fn = methods[name];
+            return function(object) {
+                return fn.apply(Function.toFunction(object), Array.slice(arguments, 1));
+            }
+        })(name);
+}
+
+// Record the current contents of `Function.prototype`, so that we
+// can see what we've added later.
+Functional.__initalFunctionState = Functional._startRecordingMethodChanges(Function.prototype);
+
+/// ^ Higher-order methods
+
+/// ^^ Partial function application
+
+/**
+ * Returns a bound method on `object`, optionally currying `args`.
+ * == f.bind(obj, args...)(args2...) == f.apply(obj, [args..., args2...])
+ */
+Function.prototype.bind = function(object/*, args...*/) {
+    var fn = this;
+    var args = Array.slice(arguments, 1);
+    return function() {
+        return fn.apply(object, args.concat(Array.slice(arguments, 0)));
+    }
+}
+
+/**
+ * Returns a function that applies the underlying function to `args`, and
+ * ignores its own arguments.
+ * :: (a... -> b) a... -> (... -> b)
+ * == f.saturate(args...)(args2...) == f(args...)
+ * >> Math.max.curry(1, 2)(3, 4) -> 4
+ * >> Math.max.saturate(1, 2)(3, 4) -> 2
+ * >> Math.max.curry(1, 2).saturate()(3, 4) -> 2
+ */
+Function.prototype.saturate = function(/*args*/) {
+    var fn = this;
+    var args = Array.slice(arguments, 0);
+    return function() {
+        return fn.apply(this, args);
+    }
+}
+
+/**
+ * Invoking the function returned by this function only passes `n`
+ * arguments to the underlying function.  If the underlying function
+ * is not saturated, the result is a function that passes all its
+ * arguments to the underlying function.  (That is, `aritize` only
+ * affects its immediate caller, and not subsequent calls.)
+ * >> '[a,b]'.lambda()(1,2) -> [1, 2]
+ * >> '[a,b]'.lambda().aritize(1)(1,2) -> [1, undefined]
+ * >> '+'.lambda()(1,2)(3) -> error
+ * >> '+'.lambda().ncurry(2).aritize(1)(1,2)(3) -> 4
+ *
+ * `aritize` is useful to remove optional arguments from a function that
+ * is passed to a higher-order function that supplies *different* optional
+ * arguments.
+ *
+ * For example, many implementations of `map` and other collection
+ * functions, including those in this library, call the function argument
+ *  with both the collection element
+ * and its position.  This is convenient when expected, but can wreak
+ * havoc when the function argument is a curried function that expects
+ * a single argument from `map` and the remaining arguments from when
+ * the result of `map` is applied.
+ */
+Function.prototype.aritize = function(n) {
+    var fn = this;
+    return function() {
+        return fn.apply(this, Array.slice(arguments, 0, n));
+    }
+}
+
+/**
+ * Returns a function that, applied to an argument list $arg2$,
+ * applies the underlying function to $args ++ arg2$.
+ * :: (a... b... -> c) a... -> (b... -> c)
+ * == f.curry(args1...)(args2...) == f(args1..., args2...)
+ *
+ * Note that, unlike in languages with true partial application such as Haskell,
+ * `curry` and `uncurry` are not inverses.  This is a repercussion of the
+ * fact that in JavaScript, unlike Haskell, a fully saturated function is
+ * not equivalent to the value that it returns.  The definition of `curry`
+ * here matches semantics that most people have used when implementing curry
+ * for procedural languages.
+ *
+ * This implementation is adapted from
+ * [http://www.coryhudson.com/blog/2007/03/10/javascript-currying-redux/].
+ */
+Function.prototype.curry = function(/*args...*/) {
+    var fn = this;
+    var args = Array.slice(arguments, 0);
+    return function() {
+        return fn.apply(this, args.concat(Array.slice(arguments, 0)));
+    };
+}
+
+/*
+ * Right curry.  Returns a function that, applied to an argument list $args2$,
+ * applies the underlying function to $args2 + args$.
+ * == f.curry(args1...)(args2...) == f(args2..., args1...)
+ * :: (a... b... -> c) b... -> (a... -> c)
+ */
+Function.prototype.rcurry = function(/*args...*/) {
+    var fn = this;
+    var args = Array.slice(arguments, 0);
+    return function() {
+        return fn.apply(this, Array.slice(arguments, 0).concat(args));
+    };
+}
+
+/**
+ * Same as `curry`, except only applies the function when all
+ * `n` arguments are saturated.
+ */
+Function.prototype.ncurry = function(n/*, args...*/) {
+    var fn = this;
+    var largs = Array.slice(arguments, 1);
+    return function() {
+        var args = largs.concat(Array.slice(arguments, 0));
+        if (args.length < n) {
+            args.unshift(n);
+            return fn.ncurry.apply(fn, args);
+        }
+        return fn.apply(this, args);
+    };
+}
+
+/**
+ * Same as `rcurry`, except only applies the function when all
+ * `n` arguments are saturated.
+ */
+Function.prototype.rncurry = function(n/*, args...*/) {
+    var fn = this;
+    var rargs = Array.slice(arguments, 1);
+    return function() {
+        var args = Array.slice(arguments, 0).concat(rargs);
+        if (args.length < n) {
+            args.unshift(n);
+            return fn.rncurry.apply(fn, args);
+        }
+        return fn.apply(this, args);
+    };
+}
+
+/**
+ * `_` (underscore) is bound to a unique value for use in `partial`, below.
+ * This is a global variable, but it's also a property of `Function` in case
+ * you overwrite or bind over the global one.
+ */
+_ = Function._ = {};
+
+/**
+ * Returns a function $f$ such that $f(args2)$ is equivalent to
+ * the underlying function applied to a combination of $args$ and $args2$.
+ *
+ * `args` is a partially-specified argument: it's a list with "holes",
+ * specified by the special value `_`.  It is combined with $args2$ as
+ * follows:
+ *
+ * From left to right, each value in $args2$ fills in the leftmost
+ * remaining hole in `args`.  Any remaining values
+ * in $args2$ are appended to the result of the filling-in process
+ * to produce the combined argument list.
+ *
+ * If the combined argument list contains any occurrences of `_`, the result
+ * of the application of $f$ is another partial function.  Otherwise, the
+ * result is the same as the result of applying the underlying function to
+ * the combined argument list.
+ */
+Function.prototype.partial = function(/*args*/) {
+    var fn = this;
+    var _ = Function._;
+    var args = Array.slice(arguments, 0);
+    //substitution positions
+    var subpos = [], value;
+    for (var i = 0; i < arguments.length; i++)
+        arguments[i] == _ && subpos.push(i);
+    return function() {
+        var specialized = args.concat(Array.slice(arguments, subpos.length));
+        for (var i = 0; i < Math.min(subpos.length, arguments.length); i++)
+            specialized[subpos[i]] = arguments[i];
+        for (var i = 0; i < specialized.length; i++)
+            if (specialized[i] == _)
+                return fn.partial.apply(fn, specialized);
+        return fn.apply(this, specialized);
+    }
+}
+
+/// ^^ Combinators
+
+/// ^^^ Combinator Functions
+
+/**
+ * The identity function: $x -> x$.
+ * == I(x) == x
+ * == I == 'x'.lambda()
+ * :: a -> a
+ * >> Functional.I(1) -> 1
+ */
+Functional.I = function(x) {return x};
+
+/**
+ * Returns a constant function that returns `x`.
+ * == K(x)(y) == x
+ * :: a -> b -> a
+ * >> Functional.K(1)(2) -> 1
+ */
+Functional.K = function(x) {return function() {return x}};
+
+/// A synonym for `Functional.I`
+Functional.id = Functional.I;
+
+/// A synonym for `Functional.K`
+Functional.constfn = Functional.K;
+
+
+/**
+ * Returns a function that applies the first function to the
+ * result of the second, but passes all its arguments too.
+ * == S(f, g)(args...) == f(g(args...), args...)
+ *
+ * This is useful for composing functions when each needs access
+ * to the arguments to the composed function.  For example,
+ * the following function multiples its last two arguments,
+ * and adds the first to that.
+ * >> Function.S('+', '_ a b -> a*b')(2,3,4) -> 14
+ *
+ * Curry this to get a version that takes its arguments in
+ * separate calls:
+ * >> Function.S.curry('+')('_ a b -> a*b')(2,3,4) -> 14
+ */
+Function.S = function(f, g) {
+    f = Function.toFunction(f);
+    g = Function.toFunction(g);
+    return function() {
+        return f.apply(this, [g.apply(this, arguments)].concat(Array.slice(arguments, 0)));
+    }
+}
+
+/// ^^^ Combinator methods
+
+/**
+ * Returns a function that swaps its first two arguments before
+ * passing them to the underlying function.
+ * == f.flip()(a, b, c...) == f(b, a, c...)
+ * :: (a b c...) -> (b a c...)
+ * >> ('a/b'.lambda()).flip()(1,2) -> 2
+ *
+ * For more general derangements, you can also use `prefilterSlice`
+ * with a string lambda:
+ * >> '100*a+10*b+c'.lambda().prefilterSlice('a b c -> [b, c, a]')(1,2,3) -> 231
+ */
+Function.prototype.flip = function() {
+    var fn = this;
+    return function() {
+        var args = Array.slice(arguments, 0);
+        args = args.slice(1,2).concat(args.slice(0,1)).concat(args.slice(2));
+        return fn.apply(this, args);
+    }
+}
+
+/**
+ * Returns a function that applies the underlying function to its
+ * first argument, and the result of that application to the remaining
+ * arguments.
+ * == f.uncurry(a, b...) == f(a)(b...)
+ * :: (a -> b -> c) -> (a, b) -> c
+ * >> 'a -> b -> a/b'.lambda().uncurry()(1,2) -> 0.5
+ *
+ * Note that `uncurry` is *not* the inverse of `curry`.
+ */
+Function.prototype.uncurry = function() {
+    var fn = this;
+    return function() {
+        var f1 = fn.apply(this, Array.slice(arguments, 0, 1));
+        return f1.apply(this, Array.slice(arguments, 1));
+    }
+}
+
+/**
+ * ^^ Filtering
+ *
+ * Filters intercept a value before it is passed to a function, and apply the
+ * underlying function to the modified value.
+ */
+
+/**
+ * `prefilterObject` returns a function that applies the underlying function
+ * to the same arguments, but to an object that is the result of appyling
+ * `filter` to the invocation object.
+ * == fn.prefilterObject(filter).apply(object, args...) == fn.apply(filter(object), args...)
+ * == fn.bind(object) == compose(fn.prefilterObject, Functional.K(object))
+ * >> 'this'.lambda().prefilterObject('n+1').apply(1) -> 2
+ */
+Function.prototype.prefilterObject = function(filter) {
+    filter = Function.toFunction(filter);
+    var fn = this;
+    return function() {
+        return fn.apply(filter(this), arguments);
+    }
+}
+
+/**
+ * `prefilterAt` returns a function that applies the underlying function
+ * to a copy of the arguments, where the `index`th argument has been
+ * replaced by the value of `filter(argument[index])`.
+ * == fn.prefilterAt(i, filter)(a1, a2, ..., a_{n}) == fn(a1, a2, ..., filter(a_{i}), ..., a_{n})
+ * >> '[a,b,c]'.lambda().prefilterAt(1, '2*')(2,3,4) -> [2, 6, 4]
+ */
+Function.prototype.prefilterAt = function(index, filter) {
+    filter = Function.toFunction(filter);
+    var fn = this;
+    return function() {
+        var args = Array.slice(arguments, 0);
+        args[index] = filter.call(this, args[index]);
+        return fn.apply(this, args);
+    }
+}
+
+/**
+ * `prefilterSlice` returns a function that applies the underlying function
+ * to a copy of the arguments, where the arguments `start` through
+ * `end` have been replaced by the value of `filter(argument.slice(start,end))`,
+ * which must return a list.
+ * == fn.prefilterSlice(i0, i1, filter)(a1, a2, ..., a_{n}) == fn(a1, a2, ..., filter(args_{i0}, ..., args_{i1}), ..., a_{n})
+ * >> '[a,b,c]'.lambda().prefilterSlice('[a+b]', 1, 3)(1,2,3,4) -> [1, 5, 4]
+ * >> '[a,b]'.lambda().prefilterSlice('[a+b]', 1)(1,2,3) -> [1, 5]
+ * >> '[a]'.lambda().prefilterSlice(compose('[_]', Math.max))(1,2,3) -> [3]
+ */
+Function.prototype.prefilterSlice = function(filter, start, end) {
+    filter = Function.toFunction(filter);
+    start = start || 0;
+    var fn = this;
+    return function() {
+        var args = Array.slice(arguments, 0);
+        var e = end < 0 ? args.length + end : end || args.length;
+        args.splice.apply(args, [start, (e||args.length)-start].concat(filter.apply(this, args.slice(start, e))));
+        return fn.apply(this, args);
+    }
+}
+
+/// ^^ Method Composition
+
+/**
+ * `compose` returns a function that applies the underlying function
+ * to the result of the application of `fn`.
+ * == f.compose(g)(args...) == f(g(args...))
+ * >> '1+'.lambda().compose('2*')(3) -> 7
+ *
+ * Note that, unlike `Functional.compose`, the `compose` method on
+ * function only takes a single argument.
+ * == Functional.compose(f, g) == f.compose(g)
+ * == Functional.compose(f, g, h) == f.compose(g).compose(h)
+ */
+Function.prototype.compose = function(fn) {
+    var self = this;
+    fn = Function.toFunction(fn);
+    return function() {
+        return self.apply(this, [fn.apply(this, arguments)]);
+    }
+}
+
+/**
+ * `sequence` returns a function that applies the underlying function
+ * to the result of the application of `fn`.
+ * == f.sequence(g)(args...) == g(f(args...))
+ * == f.sequence(g) == g.compose(f)
+ * >> '1+'.lambda().sequence('2*')(3) -> 8
+ *
+ * Note that, unlike `Functional.compose`, the `sequence` method on
+ * function only takes a single argument.
+ * == Functional.sequence(f, g) == f.sequence(g)
+ * == Functional.sequence(f, g, h) == f.sequence(g).sequence(h)
+ */
+Function.prototype.sequence = function(fn) {
+    var self = this;
+    fn = Function.toFunction(fn);
+    return function() {
+        return fn.apply(this, [self.apply(this, arguments)]);
+    }
+}
+
+/**
+ * Returns a function that is equivalent to the underlying function when
+ * `guard` returns true, and otherwise is equivalent to the application
+ * of `otherwise` to the same arguments.
+ *
+ * `guard` and `otherwise` default to `Functional.I`.  `guard` with
+ * no arguments therefore returns a function that applies the
+ * underlying function to its value only if the value is true,
+ * and returns the value otherwise.
+ * == f.guard(g, h)(args...) == f(args...), when g(args...) is true
+ * == f.guard(g ,h)(args...) == h(args...), when g(args...) is false
+ * >> '[_]'.lambda().guard()(1) -> [1]
+ * >> '[_]'.lambda().guard()(null) -> null
+ * >> '[_]'.lambda().guard(null, Functional.K('n/a'))(null) -> "n/a"
+ * >> 'x+1'.lambda().guard('<10', Functional.K(null))(1) -> 2
+ * >> 'x+1'.lambda().guard('<10', Functional.K(null))(10) -> null
+ * >> '/'.lambda().guard('p q -> q', Functional.K('n/a'))(1, 2) -> 0.5
+ * >> '/'.lambda().guard('p q -> q', Functional.K('n/a'))(1, 0) -> "n/a"
+ * >> '/'.lambda().guard('p q -> q', '-> "n/a"')(1, 0) -> "n/a"
+ */
+Function.prototype.guard = function(guard, otherwise) {
+    var fn = this;
+    guard = Function.toFunction(guard || Functional.I);
+    otherwise = Function.toFunction(otherwise || Functional.I);
+    return function() {
+        return (guard.apply(this, arguments) ? fn : otherwise).apply(this, arguments);
+    }
+}
+
+/// ^^ Utilities
+
+/**
+ * Returns a function identical to this function except that
+ * it prints its arguments on entry and its return value on exit.
+ * This is useful for debugging function-level programs.
+ */
+Function.prototype.traced = function(name) {
+    var self = this;
+    name = name || self;
+    return function() {
+        window.console && console.info('[', name, 'apply(', this!=window && this, ',', arguments, ')');
+        var result = self.apply(this, arguments);
+        window.console && console.info(']', name, ' -> ', result);
+        return result;
+    }
+}
+
+
+/**
+ * ^^ Function methods as functions
+ *
+ * In addition to the functions defined above, every method defined
+ * on `Function` is also available as a function in `Functional`, that
+ * coerces its first argument to a `Function` and applies
+ * the remaining arguments to this.
+ *
+ * A few examples make this clearer:
+ * == curry(fn, args...) == fn.curry(args...)
+ * >> Functional.flip('a/b')(1, 2) -> 2
+ * >> Functional.curry('a/b', 1)(2) -> 0.5
+
+ * For each method that this file defined on Function.prototype,
+ * define a function on Functional that delegates to it.
+ */
+Functional._attachMethodDelegates(Functional.__initalFunctionState.getChangedMethods());
+delete Functional.__initalFunctionState;
+
+
+// In case to-function.js isn't loaded.
+Function.toFunction = Function.toFunction || Functional.K;
+
+if (!Array.slice) { // mozilla already supports this
+    Array.slice = (function(slice) {
+        return function(object) {
+            return slice.apply(object, slice.call(arguments, 1));
+        };
+    })(Array.prototype.slice);
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]