Udon is a library providing basic support for functional programming idioms in JavaScript.

Downloads

Current version: 1.2.0.

This version adds the following new functions to the library: id, concat, sum, product, elem, notElem, reverse, intersperse and intercalate.

Programming with Udon

All the functions in this library are namespaced by the Udon object, so a reference in the documentation to e.g. map should be read as Udon.map. This means it should play nicely with other JavaScript libraries.

Udon is known to work in all major browsers, as well as on Node. As well as being available from the download links above, you can install it with npm.

npm install -g udon

The Node library exports the Udon object as the module, so usage should follow this pattern.

var Udon = require('udon');

Udon.zip([1, 2, 3], ['a', 'b', 'c']);
// -> [[1, 'a'], [2, 'b'], [3, 'c']]

Udon is designed for pure functional programming, so none of its public functions (i.e. those listed below) modify the values passed to them.

API summary

Function operations

Passing functions around as first-class objects is one of the cornerstones of functional programming. Udon provides several handy mechanisms for enabling function composition and reuse.

curry

Currying is the process of converting a function of arity n into a nested set of functions with arity 1, i.e. making it partially applicable. The curry function relies on the length property of the function to generate partially applicable functions, for example converting a function which accepts two arguments to a function which accepts one argument and returns a new function which also accepts one argument.

var add = function(a, b) {
return a + b;
};

var plus10 = Udon.curry(add)(10);

ncurry

The basic curry function will be fine for many circumstances, but sometimes (for example when dealing with variadic functions or functions with optional arguments) one needs to be explicit about the number of arguments a curried function can accept. The ncurry function is a generator for currying functions: it accepts a number and returns a curry function that transforms functions of that arity to, effectively, a nest of partially applicable functions, each of which has arity 1.

var add3 = function(a, b, c) {
return a + b + c;
};

var curry3 = Udon.ncurry(3),
add3c = curry3(add3);

add3c(1)(2)(3); // -> 6

compose

The compose function allows one to easily generate ‘pipelines’ of functions through which a value is passed. Note that the last function in the pipeline will be the first to be applied; this mirrors both the way the code would be written without compose, as a nest of function calls.

var tcs = Udon.compose([Math.sin, Math.cos, Math.tan]);

tcs(0.7); // -> 0.6176546934901699

It accepts an optional arity argument; if this is greater than 1 then the function pipeline will be made partially applicable.

var ceilMax = Udon.compose([Math.ceil, Math.max], 2);

ceilMax(0.7)(1.1); // -> 2

id

It’s occasionally useful to be able to pass a function that simply returns its argument to a higher-order function. id is that function.

Udon.id(5) == 5;
Udon.id("foobar") == "foobar";

Passing id to map produces a nice fixed point, as we can see from the following example where xs0, xs1 and xs2 are all equivalent.

var xs0 = [1, 4, 9],
xs1 = Udon.id([1, 4, 9]),
xs2 = Udon.map(Udon.id, [1, 4, 9]);

List operations

Technically, these aren’t really list operations, because the underlying data structures are JavaScript arrays, not singly-linked lists (as in Lisps, ML, Haskell etc.). They are, however, close enough for most practical purposes.

foldl

Both fold functions accept a function and use it to reduce a list to another value. For example, you could use it to implement a sum function which adds all the elements of a list together:

var sum = function(ns) {
return Udon.foldl(function(a, b) {
return a + b;
}, 0, ns);
};

foldl1

This is a version of foldl which uses the first element of the supplied array as the initial element. It thus requires the array to be nonempty.

var join = function(a, b) {
return a.toString() + '-' + b.toString();
};

Udon.foldl1(join, [2011, 05, 08]);
// -> "2011-05-08"

foldr

As the name implies, foldl is a left-associative function, which foldr is right-associative. So, for example, you could use foldr to convert an array into a singly-linked list.

var array2list = function(arr) {
return Udon.foldr(function(head, tail) {
return {
car: head,
cdr: tail
};
}, {car: null, cdr: null}, arr);
};

You can read more about folds on Wikipedia.

concat

Flattens an array one level.

Udon.concat([[1, 2], [3, 4]]);
// [1, 2, 3, 4]
Udon.concat([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]);
// [[1, 2], [3, 4], [5, 6], [7, 8]]

maximum

Selects the largest number from an array. It uses Math.max under the hood so it won’t work for e.g. strings. Use Udon.maximumBy if you need your own comparison function.

Udon.maximum([1, 3, 2, 17, 12]);
// -> 17

minimum

Selects the smallest number from an array. It uses Math.min under the hood so it won’t work for e.g. strings. Use Udon.minimumBy if you need your own comparison function.

Udon.minimum([1, 3, 2, 17, 12]);
// -> 1

maximumBy

Uses a given comparison function to select the largest element of an array. Comparison functions should take two arguments, and return 0 if they are considered equal, 1 if the first argument is greater than the second, and -1 if the second argument is greater than the first.

var longerThan = function(x, y) {
if (x.length === y.length) {
return 0;
} else if (x.length > y.length) {
return 1;
} else {
return -1;
}
};

Udon.maximumBy(longerThan, ["foo", "foobar", "foobarbaz"]);
// -> "foobarbaz"

minimumBy

Uses a given comparison function to select the smallest element of an array. Comparison functions should operate the same way as those given to maximumBy.

Udon.minimumBy(longerThan, ["foo", "foobar", "foobarbaz"]);
// -> "foo"

map

Returns the result of applying a given function to each element of a list.

Udon.map(function(n) {
return n * n;
}, [1,2,3]);
// -> [1,4,9]

reverse

The reverse function provides a safe alternative to the [reverse method], as it does not mutate the input array.

Udon.reverse(['a', 'b', 'c', 'd']);
// -> ['d', 'c', 'b', 'a']

intersperse

Insert a separating element between the existing elements, like the join method does but without converting the array to a string.

Udon.intersperse(10, [1, 2, 3, 4]);
// -> [1, 10, 2, 10, 3, 10, 4]

intercalate

Effectively intersperse for arrays of arrays, but flattening the resultant array one level.

Udon.intercalate([1, 2, 3], [[10, 20], [30, 40], [50, 60]]);
// -> [10, 20, 1, 2, 3, 30, 40, 1, 2, 3, 50, 60]

filter

Returns the elements of a list which satisfy some predicate.

Udon.filter(function(n) {
return n < 5;
}, [4,7,3,9,21,2]);
// -> [4,3,2]

any

Check whether any element of a list satisfies some predicate.

Udon.any(function(regex) {
return regex.exec("http://");
}, [new RegExp("[a-z]+:\/\/"), new RegExp("^ftp:/")]);
// -> true

all

Determine whether all the elements of a list satisfy some predicate.

Udon.all(function(str) {
return str.match(/^[A-Z][a-z]+$/);
}, ["One", "Two", "three"]);
// -> false

none

Check that no element of a list satisfies a predicate.

Udon.none(function(c) {
return c === Math.PI;
}, [1, 0, -1, Math.LN2, Math.E]);
// -> true

sum

Adds the elements of a numeric list together.

Udon.sum([]);
// -> 0
Udon.sum([1, 2, 3, 4]);
// -> 10

product

Multiplies the elements of a numeric list together.

Udon.product([]);
// -> 1
Udon.sum([1, 2, 3, 4]);
// -> 24

elem

Returns true if the given element is in the array, false otherwise.

Udon.elem(2, [1, 2, 3]) === true;

Udon.elem(4, [1, 2, 3]) === false;

notElem

Returns false if the given element is in the array, and true if it is not.

Udon.notElem('Z', ['a', 'b', 'c']) === true;

Udon.notElem('Z', ['X', 'Y', 'Z']) === false;

partition

Separates a list into lists of those elements which do and do not satisfy some predicate.

Udon.partition(function(n) {
return n < 5;
}, [4,7,3,9,21,2]);
// -> [[4,3,2], [7,9,21]]

unfoldr

Builds a list by repeatedly applying a function to a seed value. The function should return a pair of values: the first is an element to append to the list under construction, while the second is the seed value to pass to the next function call. The function must return null when it’s done, at which point unfoldr will return the constructed list.

var randomInts = function(ceil, n) {
return Udon.unfoldr(function(i) {
return i < 1 ? null : [Math.floor(Math.random() * ceil), i - 1];
}, n);
};

randomInts(5, 4); // -> [5,1,4,5] e.g.

zip

Transforms a pair of lists into a list of pairs. If the lists are of differing lengths, any elements left over after each element in the shorter list has been paired up with one from the longer list will be discarded.

Udon.zip([1,2,3], ["a", "b", "c"]); // -> [[1, "a"], [2, "b"], [3, "c"]]

zipWith

The zipWith function is a generalisation of zip: it returns the result of applying a function to each pair of elements from two lists.

Udon.zipWith(function(a, b) {
return a * b;
}, [1,2,3], [4,5,6]);
// -> [4,10,18]