Functional Javascript: API Documentation Usage
Functional Javascript: API Documentation Usage
http://osteele.com/sources/javascript/functional/
Functional Javascript
header | announcement | project page | download: compressed (<3K min gz) | source
Try it! Enter an expression below, or click on a line of code (from the examples above or the documentation below) to copy it here.
[3, 4]
Usage (source)
String lambdas
lambda creates a function from a string that contains a single expression. This function can then be applied to an argument list, either immediately: 'x+1'.lambda()(2); 3 'x+2*y'.lambda()(2, 3); 8
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()
Higher-order functions
Functional.compose = function(fn...) Type: (a2 a1) (a3 -> a2) (a -> an) -> a -> a1
Explicit parameters If the string contains a ->, this separates the parameters from the body.
'x 'y y -> x+2*y'.lambda()(2, 3); 8 x -> x+2*y'.lambda()(2, 3); 7
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(f 1, f2, f3, fn)(args) =def f 1(f2(f3((fn(args))))) compose('1+', '2*')(2) 5
Otherwise, if the string contains a _, it's a unary function and _ is name of the parameter:
'_ '_ -> _+1'.lambda()(2); 3 -> _*_'.lambda()(3); 9
Functional.sequence = function(fn...) Type: (a a1) (a1 -> a2) (a2 -> a3) (an-1 -> an) -> a -> an
Implicit parameters If the string doesn't specify explicit parameters, they are implicit. If the string starts with an operator or relation besides -, or ends with an operator or relation, then its implicit arguments are placed at the beginning and/or end:
'*2'.lambda()(2); 4 '/2'.lambda()(4); 2 '2/'.lambda()(4); 0.5 '/'.lambda()(2, 4); 0.5
Otherwise, the variables in the string, in order of occurrence, are its parameters.
'x+1'.lambda()(2); 3 'x*x'.lambda()(3); 9 'x + 2*y'.lambda()(1, 2); 5 'y + 2*x'.lambda()(1, 2); 5
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]) =def f(f(f(init, x0), x1), x2) reduce('x y -> 2*x+y', 0, [1,0,1,0]) 10
Returns a list of those elements x of sequence such that fn(x) returns true.
select('%2', [1,2,3,4]) [1, 3]
1 von 7
06.12.2011 19:38
Functional Javascript
http://osteele.com/sources/javascript/functional/
Functional.filter = function()
'x 'x
Higher-order functions
The Functional namespace defines the higher-order functions (functionals): map, reduce, select, and a bunch of others.
Functional.map(function(x){return x+1}, [1,2,3]); [2, 3, 4]
Lambda strings are useful as arguments to functionals. Functionals convert the string to a function once per call, not once per application.
Functional.map('_+1', [1,2,3]); [2, 3, 4] Functional.install() imports the functionals into the global namespace (window), so that they needn't be qualified with Functional.* each time. Functional.install(); map('+1', [1,2,3]); [2, 3, 4] map('_.length', 'here are some words'.split(' ')); [4, 3, 4, 5] select('>2', [1,2,3,4]); [3, 4] reduce('2*x+y', 0, [1,0,1,0]); 10 some('>2', [1,2,3,4]); true every('>2', [1,2,3,4]); false compose and sequence compose sequences of functions backwards and forwards, respectively: compose('+1', '*2')(1); 3 sequence('+1', '*2')(1); 4 compose('+1', '*2', '_.length')('a string'); 17 compose.apply(null, map('x -> y -> x*y+1', [2,3,4]))(1); 33 foldr (right reduce) could have handled the last example: foldr('x -> y -> x*y+1'.lambda().uncurry(), 1, [2,3,4]); 33 foldr('x*y+1', 1, [2,3,4]); 33 pluck and invoke turn methods into functions: map(pluck('length'), ['two', 'words']); [3, 5] map(invoke('toUpperCase'), ['two', 'words']); ["TWO", "WORDS"] lambda can do this too: map('.length', ['two', 'words']); [3, 5] map('.toUpperCase()', ['two', 'words']); ["TWO", "WORDS"] and pluck and lambda can both implement projections: var cities = [['NYC', 'NY'], ['Boston', 'MA']]; map('_[0]',cities); ["NYC", "Boston"] map(pluck(0), cities); ["NYC", "Boston"] map(pluck(1), cities); ["NY", "MA"] map('_.x', [{x:10, y:20}, {x:15, y:25}, {x:0, y:-5}]); [10, 15, 0] map(pluck('x'), [{x:10, y:20}, {x:15, y:25}, {x:0, y:-5}]); [10, 15, 0] map(pluck('y'), [{x:10, y:20}, {x:15, y:25}, {x:0, y:-5}]); [20, 25, -5]
Predicates
Functional.and = function(functions...) Type: [a boolean] a -> a
Returns a function that returns true when all the arguments, applied to the returned function's arguments, returns true.
and(f1, f2)(args) =def f1(args) && f 2(args) and('>1', '>2')(2) false and('>1', '>2')(3) true and('>1', 'error()')(1) false
Returns a function that returns true when any argument, applied to the returned function's arguments, returns true.
or(f1, f2)(args) =def f1(args) || f2(args) or('>1', '>2')(1) false or('>1', '>2')(2) true or('>1', 'error()')(2) true
Returns true when fn(x) returns true for some element x of sequence. The returned function short-circuits.
some(f, [x1, x2, x3, ]) =def f(x1) || f(x2) || f(x3) some('>2', [1,2,3]) true some('>10', [1,2,3]) false
Returns true when fn(x) returns true for every element x of sequence. The returned function short-circuits.
every(f, [x1, x2, x3, ]) =def f(x1) && f(x2) && f(x3) every('<2', [1,2,3]) false every('<10', [1,2,3]) true
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) =def f1(args) == f2(args) 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
Utilities
Functional.lambda = function(object)
Function methods
Functional attaches a number of methods to Function, that are useful for functional method chaining and functional-level programming. Here are a few. Guards The first expression below (without guard) attempts the reciprocal of all the list items. The second expression guards the division so that it's not applied to null.
map('1/', [1,2,null,4]); [1, 0.5, Infinity, 0.25] map(guard('1/'), [1,2,null,4]);
Functional.invoke = function(methodName, arguments) Type: name args object args2 -> object[name](args args2)
Returns a function that takes an object as an argument, and applies object's methodName method to arguments.
invoke(name)(object, args) =def object[name](args)
2 von 7
06.12.2011 19:38
Functional Javascript
http://osteele.com/sources/javascript/functional/
invoke('toString')(123) "123"
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) =def object[name] pluck('length')("abc") 3
used to replace them by null, but leave the indices of the remaining elements unchanged:
filter('%2', xs); [1, 3] map(guard(Functional.K(null), '%2'), xs); [null, 2, null, 4]
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.
until('>10', '2*')(1) 16
although we could also use any one of these for the last one:
map(curry('o[ix]', ['even', 'odd']).compose('%2'), xs); ["odd", "even", "odd", "even"] map(curry('o[i%2]', ['even', 'odd']), xs); ["odd", "even", "odd", "even"] map('["even","odd"][_%2]', xs); ["odd", "even", "odd", "even"]
Functional.zip = function(args...) Type: [a] [b] [[a b]] zip(a, b) =def [[a0, b0], [a1, b1], ]
Curry
curry creates a new function that applies the original arguments, and then the new arguments: var right = list.curry(1, 2); right(3,4); [1, 2, 3, 4] var left = list.rcurry(1, 2); left(3, 4); [3, 4, 1, 2]
Higher-order methods
Partial function application
Function.prototype.bind = function(object, args...)
Use rcurry ("right curry") to create halve and double functions from divide:
function divide(a, b) {return a/b} var halve = divide.rcurry(2); var double = divide.rcurry(1/2); halve(10); 5 double(10); 20 ncurry and rncurry wait until they're fully saturated before applying the function. list.ncurry(4,1,2)(3); function() list.ncurry(4,1,2)(3)(4); [1, 2, 3, 4] list.ncurry(4,1,2)(3,4); [1, 2, 3, 4] list.rncurry(4,1,2)(3); function() list.rncurry(4,1,2)(3,4); [3, 4, 1, 2]
Returns a function that applies the underlying function to args, and ignores its own arguments.
f.saturate(args)(args2) =def 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.aritize = function(n)
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
[r]curry can't do this because it doesn't in general know the polyadicity of the underlying function. (Sometimes fn.length works, but some functions, especially constructed functions, don't declare all their arguments, so fn.length this lies.)
list.curry(1,2)(3); [1, 2, 3, undefined]
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.curry = function(args...) Type: (a b c) a -> (b -> c)
arguments. We'll create partially applied (specialized) versions of this function below.
function list(a,b,c,d) {return [a,b,c,d]};
Specialize the first and third parameters. This creates a new function, that interleaves its arguments with the 1 and 2:
var finterleave = list.partial(1,_,2,_); finterleave(3, 4); [1, 3, 2, 4]
Returns a function that, applied to an argument list arg2, applies the underlying function to args ++ arg2.
f.curry(args1)(args2) =def 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.ncurry = function(n, args...)
Specialize the outer two parameters, to produce a function whose arguments supply the middle two arguments to list:
var finners = list.partial(1,_,_,2); finners(3, 4); [1, 3, 4, 2]
Same as curry, except only applies the function when all n arguments are saturated.
Function.prototype.rncurry = function(n, args...)
3 von 7
06.12.2011 19:38
Functional Javascript
http://osteele.com/sources/javascript/functional/
Same as rcurry, except only applies the function when all n arguments are saturated.
Function.prototype.partial = function(args) _ (underscore) is bound to a unique value for use in partial, below. This is a
[2, 4, 3, 1]
Two specializations of String's replace method. The function replaces vowels in its object with the value of its argument:
var replaceVowels = "".replace.partial(/[aeiou]/g, _); This is a method, so use call to invoke it on an object: replaceVowels.call("change my vowels to underscores", '_'); "ch_ng_ my v_w_ls t_ _nd_rsc_r_s"
global variable, but it's also a property of Function in case you overwrite or bind over the global one. 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
The second function replaces slices that match the pattern of its argument, with 'th':
var replaceWithCoronalFricatives = "".replace.partial(_, 'th'); var str = "substitute my esses with tee-aitches" replaceWithCoronalFricatives.call(str, /s/g); "thubthtitute my eththeth with tee-aitcheth"
The syntax for partials is meant to suggest the hyphen placeholders in abstract algebra:
Hom(F-, -) = Hom(-, G_)
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.
This isn't unification or pattern-matching, though. All the hyphens have to be at the top level (as arguments, not items in lists, etc.). And there's no facility for binding two hyphens to the same parameter position. Methods on Function are object-free functions too The higher-order methods on Function are also available as functions, that take a function as their first argument. These functions are in the Functional namespace. Functional.install also installs these functions in the global namespace. Unlike the methods on Function, these functions can be applied to string lambdas too:
'+'.lambda().curry(1)(2); 3 curry('+', 1)(2); 3 bind('-> this', 1)(); Error: ReferenceError: bind is not defined
Combinators
Combinator Functions Functional.I = function(x) Type: a a
Functional.id = function()
These next three statements have the same effect. Click on a of the buttons to execute the corresponding function.
Event.observe('e1', 'click', function(){ alert('1'); }); onclick('e2', function(){ alert('2'); }); onclick('e3', alert.bind(null).saturate('3'));
Returns a function that applies the first function to the result of the second, but passes all its arguments too.
S(f, g)(args) =def 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
Returns a function that swaps its first two arguments before passing them to the underlying function.
f.flip()(a, b, c) =def f(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
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) =def f(a)(b) 'a -> b -> a/b'.lambda().uncurry()(1,2) 0.5
Filtering
4 von 7
06.12.2011 19:38
Functional Javascript
http://osteele.com/sources/javascript/functional/
Filters intercept a value before it is passed to a function, and apply the underlying function to the modified value.
Function.prototype.prefilterObject = function(filter) 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) =def fn.apply(filter(object), args) fn.bind(object) =def compose(fn.prefilterObject, Functional.K(object)) 'this'.lambda().prefilterObject('n+1').apply(1) 2
Function.prototype.prefilterAt = function(index, filter) prefilterAt returns a function that applies the underlying function to a copy of the arguments, where the indexth argument has been replaced by the value of filter(argument[index]). fn.prefilterAt(i, filter)(a1, a2, , an) =def fn(a1, a2, , filter(ai), , an) '[a,b,c]'.lambda().prefilterAt(1, '2*')(2,3,4) [2, 6, 4]
Function.prototype.prefilterSlice = function(filter, start, end) 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, , an) =def fn(a1, a2, , filter(argsi0, , argsi1), , an) '[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]
Method Composition
Function.prototype.compose = function(fn) compose returns a function that applies the underlying function to the result of the application of fn. f.compose(g)(args) =def 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) =def f.compose(g) Functional.compose(f, g, h) =def f.compose(g).compose(h)
Function.prototype.sequence = function(fn) sequence returns a function that applies the underlying function to the result of
Note that, unlike Functional.compose, the sequence method on function only takes a single argument.
Functional.sequence(f, g) =def f.sequence(g) Functional.sequence(f, g, h) =def f.sequence(g).sequence(h)
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) =def f(args), when g(args) is true f.guard(g ,h)(args) =def 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"
Utilities
Function.prototype.traced = function(name)
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.
5 von 7
06.12.2011 19:38
Functional Javascript
http://osteele.com/sources/javascript/functional/
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) =def 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.
String lambdas
String.prototype.lambda = function()
Turns a string that contains a JavaScript expression into a Function that returns the value of that expression. If the string contains a ->, this separates the parameters from the body:
'x -> x + 1'.lambda()(1) 2 'x y -> x + 2*y'.lambda()(1, 2) 5 'x, y -> x + 2*y'.lambda()(1, 2) 5
Otherwise if the string begins or ends with an operator or relation, prepend or append a parameter. (The documentation refers to this type of string as a "section".)
'/2'.lambda()(4) 2 '2/'.lambda()(4) 0.5 '/'.lambda()(2,4) 0.5
Sections can end, but not begin with, -. (This is to avoid interpreting e.g. -2*x as a section). On the other hand, a string that either begins or ends with / is a section, so an expression that begins or ends with a regular expression literal needs an explicit parameter. Otherwise, each variable name is an implicit parameter:
'x + 1'.lambda()(1) 2 'x + 2*y'.lambda()(1, 2) 5 'y + 2*x'.lambda()(1, 2) 5
Implicit parameter detection ignores strings literals, variable names that start with capitals, and identifiers that precede : or follow .:
map('"im"+root', ["probable", "possible"]) ["improbable", "impossible"] 'Math.cos(angle)'.lambda()(Math.PI) -1 'point.x'.lambda()({x:1, y:2}) 1 '({x:1, y:2})[key]'.lambda()('x') 1
Implicit parameter detection mistakenly looks inside regular expression literals for variable names. It also doesn't know to ignore JavaScript keywords and bound variables. (The only way you can get these last two is with a function literal inside the string. This is outside the intended use case for string lambdas.) Use _ (to define a unary function) or ->, if the string contains anything that looks like a free variable but shouldn't be used as a parameter, or to specify parameters that are ordered differently from their first occurrence in the string. Chain ->s to create a function in uncurried form:
'x -> y -> x + 2*y'.lambda()(1)(2) 5 'x -> y -> z -> x + 2*y+3*z'.lambda()(1)(2)(3) 14 this and arguments are special: 'this'.call(1) 1 '[].slice.call(arguments, 0)'.call(null,1,2) [1, 2]
String.prototype.lambda.cache = function()
Turn on caching for string -> Function conversion. Duck-Typing Strings support call and apply. This duck-types them as functions, to some callers.
String.prototype.apply = function(thisArg, args)
String.prototype.call = function()
Coercion
6 von 7
06.12.2011 19:38
Functional Javascript
http://osteele.com/sources/javascript/functional/
String.prototype.toFunction = function()
Returns a Function that perfoms the action described by this string. If the string contains a return, applies new Function to it. Otherwise, this function returns
the result of `this.lambda()`. '+1'.toFunction()(2) 3 'return 1'.toFunction()(1) 1
Function.prototype.toFunction = function()
Function.toFunction = function(value)
Coerces fn into a function if it is not already one, by calling its toFunction method.
Function.toFunction(function() {return 1})() 1 Function.toFunction('+1')(2) 3 Function.toFunction requires an argument that can be coerced to a function. A nullary version can be constructed via guard: Function.toFunction.guard()('1+') function() Function.toFunction.guard()(null) null Function.toFunction doesn't coerce arbitrary values to functions. It might
seem convenient to treat Function.toFunction(value) as though it were the constant function that returned value, but it's rarely useful and it hides errors. Use Functional.K(value) instead, or a lambda string when the value is a compile-time literal:
Functional.K('a string')() "a string" Function.toFunction('"a string"')() "a string"
Copyright 2007 by Oliver Steele. This work is licensed under the MIT license. View/run tests. View source.
7 von 7
06.12.2011 19:38