puffnfresh/bilby.js
{ "createdAt": "2012-09-08T02:11:24Z", "defaultBranch": "master", "description": "Serious functional programming library for JavaScript.", "fullName": "puffnfresh/bilby.js", "homepage": "http://bilby.brianmckenna.org/", "language": "JavaScript", "name": "bilby.js", "pushedAt": "2014-08-20T09:29:15Z", "stargazersCount": 590, "topics": [], "updatedAt": "2025-03-22T20:48:55Z", "url": "https://github.com/puffnfresh/bilby.js"}% bilby.js

Build status
Section titled “Build status”Description
Section titled “Description”bilby.js is a serious functional programming library. Serious, meaning it applies category theory to enable highly abstract and generalised code. Functional, meaning that it enables referentially transparent programs.
Some features include:
- Immutable multimethods for ad-hoc polymorphism
- Functional data structures
- Automated specification testing (ScalaCheck, QuickCheck)
- Fantasy Land compatible

node.js:
var bilby = require('bilby');Browser:
<script unsrc="bilby-min.js"></script>Development
Section titled “Development”Download the code with git:
git clone https://github.com/puffnfresh/bilby.js.gitInstall the development dependencies with npm:
npm installRun the tests with grunt:
npm testBuild the concatenated scripts with grunt:
$(npm bin)/gruntGenerate the documentation with emu:
$(npm bin)/emu < bilby.jsEnvironment
Section titled “Environment”Environments are very important in bilby. The library itself is implemented as a single environment.
An environment holds methods and properties.
Methods are implemented as multimethods, which allow a form of ad-hoc polymorphism. Duck typing is another example of ad-hoc polymorphism but only allows a single implementation at a time, via prototype mutation.
A method instance is a product of a name, a predicate and an implementation:
var env = bilby.environment() .method( // Name 'negate', // Predicate function(n) { return typeof n == 'number'; }, // Implementation function(n) { return -n; } );
env.negate(100) == -100;We can now override the environment with some more implementations:
var env2 = env .method( 'negate', function(b) { return typeof b == 'boolean'; }, function(b) { return !b; } );
env2.negate(100) == -100;env2.negate(true) == false;The environments are immutable; references to env won’t see an
implementation for boolean. The env2 environment could have
overwritten the implementation for number and code relying on env
would still work.
Properties can be accessed without dispatching on arguments. They can almost be thought of as methods with predicates that always return true:
var env = bilby.environment() .property('name', 'Brian');
env.name == 'Brian';This means that bilby’s methods can be extended:
function MyData(data) { this.data = data;}
var _ = bilby.method( 'equal', bilby.isInstanceOf(MyData), function(a, b) { return this.equal(a.data, b.data); });
_.equal( new MyData(1), new MyData(1)) == true;
_.equal( new MyData(1), new MyData(2)) == false;environment(methods = {}, properties = {})
Section titled “environment(methods = {}, properties = {})”- method(name, predicate, f) - adds an multimethod implementation
- property(name, value) - sets a property to value
- envConcat(extraMethods, extraProperties) - adds methods + properties
- envAppend(e) - combines two environemts, biased to
e
Helpers
Section titled “Helpers”The helpers module is a collection of functions used often inside of bilby.js or are generally useful for programs.
functionName(f)
Section titled “functionName(f)”Returns the name of function f.
functionLength(f)
Section titled “functionLength(f)”Returns the arity of function f.
bind(f)(o)
Section titled “bind(f)(o)”Makes this inside of f equal to o:
bilby.bind(function() { return this; })(a)() == aAlso partially applies arguments:
bilby.bind(bilby.add)(null, 10)(32) == 42curry(f)
Section titled “curry(f)”Takes a normal function f and allows partial application of its
named arguments:
var add = bilby.curry(function(a, b) { return a + b; }), add15 = add(15);
add15(27) == 42;Retains ability of complete application by calling the function when enough arguments are filled:
add(15, 27) == 42;flip(f)
Section titled “flip(f)”Flips the order of arguments to f:
var concat = bilby.curry(function(a, b) { return a + b; }), prepend = flip(concat);identity(o)
Section titled “identity(o)”Identity function. Returns o:
forall a. identity(a) == aconstant(c)
Section titled “constant(c)”Constant function. Creates a function that always returns c, no
matter the argument:
forall a b. constant(a)(b) == acompose(f, g)
Section titled “compose(f, g)”Creates a new function that applies f to the result of g of the
input argument:
forall f g x. compose(f, g)(x) == f(g(x))create(proto)
Section titled “create(proto)”Partial polyfill for Object.create - creates a new instance of the given prototype.
getInstance(self, constructor)
Section titled “getInstance(self, constructor)”Always returns an instance of constructor.
Returns self if it is an instanceof constructor, otherwise constructs an object with the correct prototype.
tagged(name, fields)
Section titled “tagged(name, fields)”Creates a simple constructor for a tagged object.
var Tuple = tagged('Tuple', ['a', 'b']);var x = Tuple(1, 2);var y = new Tuple(3, 4);x instanceof Tuple && y instanceof Tuple;taggedSum(constructors)
Section titled “taggedSum(constructors)”Creates a disjoint union of constructors, with a catamorphism.
var List = taggedSum({ Cons: ['car', 'cdr'], Nil: []});function listLength(l) { return l.cata({ Cons: function(car, cdr) { return 1 + listLength(cdr); }, Nil: function() { return 0; } });}listLength(List.Cons(1, new List.Cons(2, List.Nil()))) == 2;error(s)
Section titled “error(s)”Turns the throw new Error(s) statement into an expression.
zip(a, b)
Section titled “zip(a, b)”Takes two lists and pairs their values together into a “tuple” (2 length list):
zip([1, 2, 3], [4, 5, 6]) == [[1, 4], [2, 5], [3, 6]]singleton(k, v)
Section titled “singleton(k, v)”Creates a new single object using k as the key and v as the
value. Useful for creating arbitrary keyed objects without
mutation:
singleton(['Hello', 'world'].join(' '), 42) == {'Hello world': 42}extend(a, b)
Section titled “extend(a, b)”Right-biased key-value concat of objects a and b:
bilby.extend({a: 1, b: 2}, {b: true, c: false}) == {a: 1, b: true, c: false}isTypeOf(s)(o)
Section titled “isTypeOf(s)(o)”Returns true iff o has typeof s.
isFunction(a)
Section titled “isFunction(a)”Returns true iff a is a Function.
isBoolean(a)
Section titled “isBoolean(a)”Returns true iff a is a Boolean.
isNumber(a)
Section titled “isNumber(a)”Returns true iff a is a Number.
isString(a)
Section titled “isString(a)”Returns true iff a is a String.
isArray(a)
Section titled “isArray(a)”Returns true iff a is an Array.
isEven(a)
Section titled “isEven(a)”Returns true iff a is even.
isOdd(a)
Section titled “isOdd(a)”Returns true iff a is odd.
isInstanceOf(c)(o)
Section titled “isInstanceOf(c)(o)”Returns true iff o is an instance of c.
AnyVal
Section titled “AnyVal”Sentinal value for when any type of primitive value is needed.
Sentinal value for when a single character string is needed.
arrayOf(type)
Section titled “arrayOf(type)”Sentinel value for when an array of a particular type is needed:
arrayOf(Number)isArrayOf(a)
Section titled “isArrayOf(a)”Returns true iff a is an instance of arrayOf.
objectLike(props)
Section titled “objectLike(props)”Sentinal value for when an object with specified properties is needed:
objectLike({ age: Number, name: String})isObjectLike(a)
Section titled “isObjectLike(a)”Returns true iff a is an instance of objectLike.
or(a)(b)
Section titled “or(a)(b)”Curried function for ||.
and(a)(b)
Section titled “and(a)(b)”Curried function for &&.
add(a)(b)
Section titled “add(a)(b)”Curried function for +.
strictEquals(a)(b)
Section titled “strictEquals(a)(b)”Curried function for ===.
not(a)
Section titled “not(a)”Returns true iff a is falsy.
fill(s)(t)
Section titled “fill(s)(t)”Curried function for filling array.
range(a, b)
Section titled “range(a, b)”Create an array with a given range (length).
liftA2(f, a, b)
Section titled “liftA2(f, a, b)”Lifts a curried, binary function f into the applicative passes
a and b as parameters.
sequence(m, a)
Section titled “sequence(m, a)”Sequences an array, a, of values belonging to the m monad:
bilby.sequence(Array, [ [1, 2], [3], [4, 5] ]) == [ [1, 3, 4], [1, 3, 5], [2, 3, 4], [2, 3, 5] ]Do (operator overloading)
Section titled “Do (operator overloading)”Adds operator overloading for functional syntax:
-
>=- monad flatMap/bind:bilby.Do()(bilby.some(1) >= function(x) {return x < 0 ? bilby.none : bilby.some(x + 2);}).getOrElse(0) == 3; -
>>- kleisli:bilby.Do()(function(x) {return x < 0 ? bilby.none : bilby.some(x + 1);} >> function(x) {return x % 2 != 0 ? bilby.none : bilby.some(x + 1);})(1).getOrElse(0) == 3; -
<- functor map:bilby.Do()(bilby.some(1) < add(2)).getOrElse(0) == 3; -
*- applicative ap(ply):bilby.Do()(bilby.some(add) * bilby.some(1) * bilby.some(2)).getOrElse(0) == 3; -
+- semigroup concat:bilby.Do()(bilby.some(1) + bilby.some(2)).getOrElse(0) == 3;
Do()(a)
Section titled “Do()(a)”Creates a new syntax scope. The a expression is allowed multiple
usages of a single operator per Do call:
>=- flatMap>>- kleisli<- map*- ap+- concat
The associated name will be called on the bilby environment with the operands. For example:
bilby.Do()(bilby.some(1) + bilby.some(2))Desugars into:
bilby.concat(bilby.some(1), bilby.some(2))Do.setValueOf(proto)
Section titled “Do.setValueOf(proto)”Used to mutate the valueOf property on proto. Necessary to do
the Do block’s operator overloading. Uses the object’s existing
valueOf if not in a Do block.
Warning: this mutates proto. May not be safe, even though it
tries to default back to the normal behaviour when not in a Do
block.
Trampoline
Section titled “Trampoline”Reifies continutations onto the heap, rather than the stack. Allows efficient tail calls.
Example usage:
function loop(n) { function inner(i) { if(i == n) return bilby.done(n); return bilby.cont(function() { return inner(i + 1); }); }
return bilby.trampoline(inner(0));}Where loop is the identity function for positive numbers. Without
trampolining, this function would take n stack frames.
done(result)
Section titled “done(result)”Result constructor for a continuation.
cont(thunk)
Section titled “cont(thunk)”Continuation constructor. thunk is a nullary closure, resulting
in a done or a cont.
trampoline(bounce)
Section titled “trampoline(bounce)”The beginning of the continuation to call. Will repeatedly evaluate
cont thunks until it gets to a done value.
- concat(b) - semigroup concat
- map(f) - functor map
- ap(b) - applicative ap(ply)
- chain(f) - chain value
- arb() - arbitrary value
isId(a)
Section titled “isId(a)”Returns true if a is Id.
idOf(type)
Section titled “idOf(type)”Sentinel value for when an Id of a particular type is needed:
idOf(Number)isIdOf(a)
Section titled “isIdOf(a)”Returns true iff a is an instance of idOf.
Option
Section titled “Option”Option a = Some a + NoneThe option type encodes the presence and absence of a value. The
some constructor represents a value and none represents the
absence.
- fold(a, b) - applies
ato value ifsomeor defaults tob - getOrElse(a) - default value for
none - isSome -
trueiffthisissome - isNone -
trueiffthisisnone - toLeft(r) -
left(x)ifsome(x),right(r)if none - toRight(l) -
right(x)ifsome(x),left(l)if none - flatMap(f) - monadic flatMap/bind
- map(f) - functor map
- ap(s) - applicative ap(ply)
- concat(s, plus) - semigroup concat
Constructor of Monad creating Option with value of x.
some(x)
Section titled “some(x)”Constructor to represent the existence of a value, x.
Represents the absence of a value.
isOption(a)
Section titled “isOption(a)”Returns true if a is a some or none.
Either
Section titled “Either”Either a b = Left a + Right bRepresents a tagged disjunction between two sets of values; a or
b. Methods are right-biased.
- fold(a, b) -
aapplied to value ifleft,bifright - swap() - turns
leftintorightand vice-versa - isLeft -
trueiffthisisleft - isRight -
trueiffthisisright - toOption() -
noneifleft,somevalue ofright - toArray() -
[]ifleft, singleton value ifright - flatMap(f) - monadic flatMap/bind
- map(f) - functor map
- ap(s) - applicative ap(ply)
- concat(s, plus) - semigroup concat
left(x)
Section titled “left(x)”Constructor to represent the left case.
right(x)
Section titled “right(x)”Constructor to represent the (biased) right case.
isEither(a)
Section titled “isEither(a)”Returns true iff a is a left or a right.
Validation
Section titled “Validation”Validation e v = Failure e + Success vThe Validation data type represents a “success” value or a semigroup of “failure” values. Validation has an applicative functor which collects failures’ errors or creates a new success value.
Here’s an example function which validates a String:
function nonEmpty(field, string) { return string ? λ.success(string) : λ.failure([field + " must be non-empty"]);}We might want to give back a full-name from a first-name and last-name if both given were non-empty:
function getWholeName(firstName) { return function(lastName) { return firstName + " " + lastName; }}λ.ap( λ.map(nonEmpty("First-name", firstName), getWholeName), nonEmpty("Last-name", lastName));When given a non-empty firstName (“Brian”) and lastName
(“McKenna”):
λ.success("Brian McKenna");If given only an invalid firstname:
λ.failure(['First-name must be non-empty']);If both values are invalid:
λ.failure([ 'First-name must be non-empty', 'Last-name must be non-empty']);- map(f) - functor map
- ap(b, concat) - applicative ap(ply)
success(value)
Section titled “success(value)”Represents a successful value.
failure(errors)
Section titled “failure(errors)”Represents a failure.
errors must be a semigroup (i.e. have an concat
implementation in the environment).
success(x)
Section titled “success(x)”Constructor to represent the existance of a value, x.
failure(x)
Section titled “failure(x)”Constructor to represent the existance of a value, x.
isValidation(a)
Section titled “isValidation(a)”Returns true iff a is a success or a failure.
Lenses
Section titled “Lenses”Lenses allow immutable updating of nested data structures.
store(setter, getter)
Section titled “store(setter, getter)”A store is a combined getter and setter that can be composed with
other stores.
isStore(a)
Section titled “isStore(a)”Returns true iff a is a store.
lens(f)
Section titled “lens(f)”A total lens takes a function, f, which itself takes a value
and returns a store.
- run(x) - gets the lens’
storefromx - compose(l) - lens composition
isLens(a)
Section titled “isLens(a)”Returns true iff a is a lens.
objectLens(k)
Section titled “objectLens(k)”Creates a total lens over an object for the k key.
Input/output
Section titled “Input/output”Purely functional IO wrapper.
Pure wrapper around a side-effecting f function.
- perform() - action to be called a single time per program
- flatMap(f) - monadic flatMap/bind
isIO(a)
Section titled “isIO(a)”Returns true iff a is an io.
Tuples
Section titled “Tuples”Tuples are another way of storing multiple values in a single value. They have a fixed number of elements (immutable), and so you can’t cons to a tuple. Elements of a tuple do not need to be all of the same type
Example usage:
bilby.Tuple2(1, 2); bilby.Tuple3(1, 2, 3); bilby.Tuple4(1, 2, 3, 4); bilby.Tuple5(1, 2, 3, 4, 5);- arb() - arbitrary value
Tuple2
Section titled “Tuple2”- flip() - flip values
- concat() - Semigroup (value must also be a Semigroup)
- map() - functor map
Tuple3
Section titled “Tuple3”- concat() - Semigroup (value must also be a Semigroup)
- map() - functor map
Tuple4
Section titled “Tuple4”- concat() - Semigroup (value must also be a Semigroup)
- map() - functor map
Tuple5
Section titled “Tuple5”- concat() - Semigroup (value must also be a Semigroup)
- map() - functor map
isTuple2(a)
Section titled “isTuple2(a)”Returns true if a is Tuple2.
isTuple4(a)
Section titled “isTuple4(a)”Returns true if a is Tuple3.
isTuple4(a)
Section titled “isTuple4(a)”Returns true if a is Tuple4.
isTuple5(a)
Section titled “isTuple5(a)”Returns true if a is Tuple5.
Promise(fork)
Section titled “Promise(fork)”Promise is a constructor which takes a fork function. The fork
function takes one argument:
fork(resolve)Where resolve is a side-effecting callback.
fork(resolve)
Section titled “fork(resolve)”The resolve callback gets called when a value is resolved.
Creates a Promise that contains a successful value.
chain(f)
Section titled “chain(f)”Returns a new promise that evaluates f when the current promise
is successfully fulfilled. f must return a new promise.
map(f)
Section titled “map(f)”Returns a new promise that evaluates f on a value and passes it
through to the resolve function.
isPromise(a)
Section titled “isPromise(a)”Returns true if a is Promise.
State(run)
Section titled “State(run)”- chain() - TODO
- evalState() - evaluate state
- execState() - execute on state
- map() - functor map
- ap() - applicative ap(ply)
isState(a)
Section titled “isState(a)”Returns true if a is State.
List a = Cons a + NilThe list type data type constructs objects which points to values. The cons
constructor represents a value, the left is the head (car, the first element)
and the right represents the tail (cdr, the second element). The nil
constructor is defined as an empty list.
The following example creates a list of values 1 and 2, where the nil terminates the list:
cons(1, cons(2, nil));The following can also represent tree like structures (Binary Trees):
cons(cons(1, cons(2, nil)), cons(3, cons(4, nil)));
* / \ * * / \ / \ 1 2 3 4- concat(a) - semigroup concat
- fold(a, b) - applies
ato value ifconsor defaults tob - map(f) - functor map
- fold(f) - applies f to values
- flatMap(f) - monadic flatMap
- append(a) - append
- appendAll(a) - append values
- prepend(a) - prepend value
- prependAll(a) - prepend values
- reverse() - reverse
- exists() - test by predicate
- filter() - filter by predicate
- partition() - partition by predicate
- size() - size of the list
cons(a, b)
Section titled “cons(a, b)”Constructor to represent the existence of a value in a list, a
and a reference to another b.
Represents an empty list (absence of a list).
isList(a)
Section titled “isList(a)”Returns true if a is a cons or nil.
QuickCheck
Section titled “QuickCheck”QuickCheck is a form of automated specification testing. Instead of manually writing tests cases like so:
assert(0 + 1 == 1);assert(1 + 1 == 2);assert(3 + 3 == 6);We can just write the assertion algebraicly and tell QuickCheck to automaticaly generate lots of inputs:
bilby.forAll( function(n) { return n + n == 2 * n; }, [Number]).fold( function(fail) { return "Failed after " + fail.tries + " tries: " + fail.inputs.toString(); }, "All tests passed!")failureReporter
Section titled “failureReporter”- inputs - the arguments to the property that failed
- tries - number of times inputs were tested before failure
forAll(property, args)
Section titled “forAll(property, args)”Generates values for each type in args using bilby.arb and
then passes them to property, a function returning a
Boolean. Tries goal number of times or until failure.
Returns an Option of a failureReporter:
var reporter = bilby.forAll( function(s) { return isPalindrome(s + s.split('').reverse().join('')); }, [String]);The number of successful inputs necessary to declare the whole property a success:
var _ = bilby.property('goal', 1000);Default is 100.

