Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

Functional Javascript

http://osteele.com/sources/javascript/functional/

Functional Javascript
header | announcement | project page | download: compressed (<3K min gz) | source

Functional supports higher-order programming:


map('x*x', [1,2,3,4]) [1, 4, 9, 16] select('>2', [1,2,3,4]) [3, 4] reduce('x*2+y', 0, [1,0,1,0]) 10 map(guard('2*', not('%2')), [1,2,3,4]) [1, 4, 3, 8]

as well as function-level (or pointless) style:


until('>100', 'x*x')(2) 256 var squareUntil = until.partial(_, 'x*x'); var square2Until = squareUntil.uncurry().flip().curry(2); var firstSquare2Over = compose(square2Until, 'n -> i -> i > n'); firstSquare2Over(100) 256

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.

map('1+', [2, 3])

[3, 4]

API Documentation (source)


var Functional; Functional is the namespace for higher-order functions. var Functional; Functional is the namespace for higher-order functions. Functional.install = function(except)

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()

or (more usefully) later:


var square = 'x*x'.lambda(); square(3); 9 square(4); 16

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

Same as compose, except applies the functions in argument-list order.


sequence(f 1, f2, f3, fn)(args) =def fn((f3(f2(f 1(args))))) sequence('1+', '2*')(2) 6

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

Functional.map = function(fn, sequence, object) Type: (a ix boolean) [a] -> [a]

Applies fn to each element of sequence.


map(f, [x1, x2]) = [f(x, 0), f(x2, 1), ] 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]

'.' counts as a right operator:


'.x'.lambda()({x:1, y:2}); 1

Functional.reduce = function(fn, init, sequence, object) Type: (a b a) a [b] -> a

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

Functional.select = function(fn, sequence, object) Type: (a boolean) [a] -> [a]

Returns a list of those elements x of sequence such that fn(x) returns true.
select('%2', [1,2,3,4]) [1, 3]

Chaining Chain -> to create curried functions.


'x y -> x+y'.lambda()(2, 3); 5

1 von 7

06.12.2011 19:38

Functional Javascript

http://osteele.com/sources/javascript/functional/

Functional.filter = function()

A synonym for select.


Functional.foldl = function()

'x 'x

-> y -> x+y'.lambda()(2)(3); 5 -> y -> x+y'.lambda()(2); function()

A synonym for reduce.


Functional.foldr = function(fn, init, sequence, object) Type: (a b b) b [a] -> b

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]

Same as foldl, but applies the function from right to left.


foldr(f, init, [x0, x1, x2]) =def fn(x0, f(x1, f(x2, init))) foldr('x y -> 2*x+y', 100, [1,0,1,0]) 104

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

Functional.or = function(functions...) Type: [a boolean] a -> a

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

Functional.some = function(fn, sequence, object) Type: (a boolean) [a] -> boolean

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

Functional.every = function(fn, sequence, object) Type: (a boolean) [a] -> boolean

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

Functional.not = function(fn) Type: (a boolean) -> (a -> boolean)

Returns a function that returns true when fn() returns false.


f.not()(args) =def !f(args) not(Functional.K(true))() false not(Functional.K(false))() true

Functional.equal = function(fn...) Type: [a b] -> a -> b

Functional iteration with until:


until('>10', '*2')(1); 16 until('>100', 'x*x')(2); 256 until(not('<100'), 'x*x')(2); 256 var fwhile = compose(until.ncurry(2), not).uncurry(); fwhile('<100', 'x*x')(2); 256

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

Higher order higher-order programming, and the fusion rule:


map('_(1)', map('_.lambda()', ['x+1', 'x-1'])); [2, 0] map(compose('_(1)', '_.lambda()'), ['x+1', 'x-1']); [2, 0]

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]);

Returns its argument coerced to a function.


lambda('1+')(2) 3 lambda(function(n){return n+1})(2) 3

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"

[1, 0.5, null, 0.25]

Double only the even numbers:


Functional.pluck = function(name) Type: name object -> object[name] var xs = [1,2,3,4]; map(guard('2*', not('%2')), xs); [1, 4, 3, 8] filter creates a list with only the predicated elements, while guard can be

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]

Functional.until = function(pred, fn) Type: (a boolean) (a -> a) -> a

Replace odd numbers by 'odd'


map(guard(Functional.K('odd'), '%2'), xs); ["odd", 2, "odd", 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

Or label "even" and "odd":


map(guard(Functional.K('odd'), '%2', Functional.K('even')), xs); ["odd", "even", "odd", "even"]

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], ]

Did you know that zip can transpose a matrix?


zip.apply(null, [[1,2],[3,4]]) [[1, 3], [2, 4]]

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...)

Returns a bound method on object, optionally currying args.


f.bind(obj, args)(args2) =def f.apply(obj, [args, args2])

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]

Function.prototype.saturate = function(args) Type: (a b) a -> ( -> b)

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

Partial function application


curry is a special case of partial function application. partial is the general case. partial can specialize parameters in the middle of the parameter list; the curry functions have to fill them in from the end. list is an unspecialized function that returns an array of its (four)

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]

if not all the parameters are supplied, the result is a function...


finterleave(4); function()

...which can be applied repeatedly until the argument list is saturated:


finterleave(3)(4); [1, 3, 2, 4] finners(_,3); function() finners(_,3)(4); [1, 4, 3, 2] finners(3)(4); [1, 3, 4, 2] list.partial(_,_,_,1)(2,_,3)(4);

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

The identity function: x -> x.


I(x) =def x I =def 'x'.lambda() Functional.I(1) 1

Using Functional with Prototype


Prototype defines a larger set of collection functions than Functional, and attaches them to Array so that they can be chained. Invoke lambda on a string to create a function for Prototype:
[1, 2, 3].map('x*x'.lambda()); execution did not get this far [1, 2, 3].map('x*x'.lambda()).map('x+1'.lambda()); execution did not get this far

Functional.K = function(x) Type: a b -> a

Returns a constant function that returns x.


K(x)(y) =def x Functional.K(1)(2) 1

Define an onclick function that abbreviates Event.observe(_, 'click', ...):


var onclick = Event.observe.bind(Event).partial(_, 'click');

Functional.id = function()

A synonym for Functional.I


Functional.constfn = 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'));

A synonym for Functional.K


Function.S = function(f, g)

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

Combinator methods Function.prototype.flip = function() Type: (a b c) (b a c)

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

Function.prototype.uncurry = function() Type: (a b -> c) -> (a, b) -> c

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

Note that uncurry is not the inverse of curry.

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

the application of fn.


f.sequence(g)(args) =def g(f(args)) f.sequence(g) =def 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) =def f.sequence(g) Functional.sequence(f, g, h) =def f.sequence(g).sequence(h)

Function.prototype.guard = function(guard, otherwise)

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 contains a _, this is the parameter:


'_ + 1'.lambda()(1) 2

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)

Coerce the string to a function and then apply it.


'x+1'.apply(null, [2]) 3 '/'.apply(null, [2, 4]) 0.5

String.prototype.call = function()

Coerce the string to a function and then call it.


'x+1'.call(null, 2) 3 '/'.call(null, 2, 4) 0.5

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()

Returns this function. Function.toFunction calls this.


'+1'.lambda().toFunction()(2) 3

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

You might also like