Udon is a library providing basic support for functional programming idioms in JavaScript.
Downloads
Current version: 1.2.0.
- Development version
- Production version 4.5kb packed, 1.0kb gzipped
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]