oboe.js-demo
Version:
A website for Oboe.js
187 lines (152 loc) • 3.73 kB
JavaScript
/**
* Like cons in Lisp
*/
function cons(x, xs) {
/* Internally lists are linked 2-element Javascript arrays.
So that lists are all immutable we Object.freeze in newer
Javascript runtimes.
In older engines freeze should have been polyfilled as the
identity function. */
return Object.freeze([x,xs]);
}
/**
* The empty list
*/
var emptyList = null,
/**
* Get the head of a list.
*
* Ie, head(cons(a,b)) = a
*/
head = attr(0),
/**
* Get the tail of a list.
*
* Ie, head(cons(a,b)) = a
*/
tail = attr(1);
/**
* Converts an array to a list
*
* asList([a,b,c])
*
* is equivalent to:
*
* cons(a, cons(b, cons(c, emptyList)))
**/
function arrayAsList(inputArray){
return reverseList(
inputArray.reduce(
flip(cons),
emptyList
)
);
}
/**
* A varargs version of arrayAsList. Works a bit like list
* in LISP.
*
* list(a,b,c)
*
* is equivalent to:
*
* cons(a, cons(b, cons(c, emptyList)))
*/
var list = varArgs(arrayAsList);
/**
* Convert a list back to a js native array
*/
function listAsArray(list){
return foldR( function(arraySoFar, listItem){
arraySoFar.unshift(listItem);
return arraySoFar;
}, [], list );
}
/**
* Map a function over a list
*/
function map(fn, list) {
return list
? cons(fn(head(list)), map(fn,tail(list)))
: emptyList
;
}
/**
* foldR implementation. Reduce a list down to a single value.
*
* @pram {Function} fn (rightEval, curVal) -> result
*/
function foldR(fn, startValue, list) {
return list
? fn(foldR(fn, startValue, tail(list)), head(list))
: startValue
;
}
/**
* foldR implementation. Reduce a list down to a single value.
*
* @pram {Function} fn (rightEval, curVal) -> result
*/
function foldR1(fn, list) {
return tail(list)
? fn(foldR1(fn, tail(list)), head(list))
: head(list)
;
}
/**
* Return a list like the one given but with the first instance equal
* to item removed
*/
function without(list, test, removedFn) {
return withoutInner(list, removedFn || noop);
function withoutInner(subList, removedFn) {
return subList
? ( test(head(subList))
? (removedFn(head(subList)), tail(subList))
: cons(head(subList), withoutInner(tail(subList), removedFn))
)
: emptyList
;
}
}
/**
* Returns true if the given function holds for every item in
* the list, false otherwise
*/
function all(fn, list) {
return !list ||
( fn(head(list)) && all(fn, tail(list)) );
}
/**
* Call every function in a list of functions with the same arguments
*
* This doesn't make any sense if we're doing pure functional because
* it doesn't return anything. Hence, this is only really useful if the
* functions being called have side-effects.
*/
function applyEach(fnList, arguments) {
if( fnList ) {
head(fnList).apply(null, arguments);
applyEach(tail(fnList), arguments);
}
}
/**
* Reverse the order of a list
*/
function reverseList(list){
// js re-implementation of 3rd solution from:
// http://www.haskell.org/haskellwiki/99_questions/Solutions/5
function reverseInner( list, reversedAlready ) {
if( !list ) {
return reversedAlready;
}
return reverseInner(tail(list), cons(head(list), reversedAlready))
}
return reverseInner(list, emptyList);
}
function first(test, list) {
return list &&
(test(head(list))
? head(list)
: first(test,tail(list)));
}