Full Code of raganwald/allong.es for AI

master 92b19a9993af cached
37 files
183.9 KB
55.6k tokens
111 symbols
1 requests
Download .txt
Repository: raganwald/allong.es
Branch: master
Commit: 92b19a9993af
Files: 37
Total size: 183.9 KB

Directory structure:
gitextract_ii3k_5xa/

├── .gitignore
├── Gruntfile.coffee
├── README.md
├── _site/
│   ├── Gruntfile.coffee
│   ├── README.md
│   ├── functions.txt
│   ├── lib/
│   │   └── allong.es.js
│   ├── package.json
│   ├── release_notes.md
│   └── spec/
│       ├── apply.spec.coffee
│       ├── arity.spec.coffee
│       ├── basic.spec.coffee
│       ├── callbacks.spec.coffee
│       ├── core.spec.coffee
│       ├── decorating-classes.spec.coffee
│       ├── folding.spec.coffee
│       ├── iterators.spec.coffee
│       ├── promises.spec.coffee
│       └── trampoline.spec.coffee
├── hello/
│   └── world.txt
├── lib/
│   └── allong.es.js
├── package.json
└── spec/
    ├── andand-oror.spec.coffee
    ├── apply.spec.coffee
    ├── arity.spec.coffee
    ├── core.spec.coffee
    ├── decorating-classes.spec.coffee
    ├── folding.spec.coffee
    ├── iterators.spec.coffee
    ├── map-arguments-with.spec.coffee
    ├── sequence-objects.spec.coffee
    ├── sequence.advanced.spec.coffee
    ├── sequence.callback.spec.coffee
    ├── sequence.decorataors.spec.coffee
    ├── sequence.then.spec.coffee
    ├── sequence.transformers.spec.coffee
    └── trampoline.spec.coffee

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store


================================================
FILE: Gruntfile.coffee
================================================
module.exports = (grunt) ->

  grunt.initConfig 
    pkg: grunt.file.readJSON('package.json'),
    uglify:
      options:
        banner: '/*! http://github.com/raganwald/<%= pkg.name %> v<%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> (c) 2012-2013 Reg Braithwaite MIT Licensed */\n'
      build:
        src: 'lib/<%= pkg.name %>.js',
        dest: 'lib/<%= pkg.name %>.min.js'

  grunt.loadNpmTasks('grunt-contrib-uglify')

  grunt.registerTask('default', ['uglify'])

================================================
FILE: README.md
================================================
# `allong.es`

The `allong.es` library is a collection of functions designed to facilitate writing JavaScript and/or CoffeeScript with functions as first-class values. The emphasis in `allong.es` is on composing and decomposing functions using combinators and decorators. `allong.es` is designed to complement libraries like [Underscore](http://underscorejs.org), not compete with them.

## Currying and Partial Application

At the heart of `allong.es` are the functions that curry and partially apply other functions. The two most important to understand are `call` and `apply`. They work very much like the `.call` and `.apply` methods that every JavaScript function implements:

```javascript
function greet (how, whom) {
  return '' + how + ', ' + whom + '!';
};
  
call(greet, 'Hello', 'Tom')
  //=> 'Hello, Tom!'
  
apply(greet, ['Hello', 'Tom'])
  //=> 'Hello, Tom!'
```

Their "special sauce" is that they automatically *curry* the supplied function, so if you provide fewer or no arguments, you get back a partially applied or curried function:

```javascript
call(greet)('Hello')('Tom')
  //=> 'Hello, Tom!'
  
call(greet, 'Hello')('Tom')
  //=> 'Hello, Tom!'
  
apply(greet, [])('Hello')('Tom')
  //=> 'Hello, Tom!'
  
apply(greet, ['Hello'])('Tom')
  //=> 'Hello, Tom!'
```

### immediate application

If you don't want the currying/partial application behaviour, there is an immediate application version named (appropriately), `callNow` (and also another named `applyNow`, not shown):

```javascript
callNow(greet, 'Hello', 'Tom')
  //=> 'Hello, Tom!'
  
callNow(greet, 'Hello')
  //=> 'Hello, undefined!'
```

### variations on the order of applying the arguments

`callRight` applies any arguments supplied to the right. If you supply all the arguments, it's the same as `call`, but if you supply fewer arguments, you get a right partial application:

```javascript
callRight(greet, 'Hello', 'Tom')
  //=> 'Hello, Tom!'
  
callRight(greet, 'Hello')('Tom')
  //=> 'Tom, Hello!'
```

`callFlipped` applies the arguments backwards, even when curried:

```javascript
callFlipped(greet, 'Hello', 'Tom')
  //=> 'Tom, Hello!'
  
callFlipped(greet, 'Hello')('Tom')
  //=> 'Tom, Hello!'
  
callFlipped(greet)('Hello')('Tom')
  //=> 'Tom, Hello!'
```

### more partial application

`callLeft` is actually synonymous with `call`: It applies arguments given to the left. We've seen `callRight` above. Both are *variadic*: You can supply as many arguments as you want.

`callFirst` and `callLast` are just like `callLeft` and `callRight`, but they are *binary* functions: They accept a function and exactly one argument. This is sometimes useful when combining functions together.

`callFirst` and `callLast` both have "flipped and curried" versions (`callFirstWith` and `callLastWith`). `callLastWith` is especially useful for working with functions written in "collection - operation" style. Here we take advantage of the fact that they are "automatically curried" to implement the popular `pluck` function.

### currying

`allong.es` does support the `curry` function, it is implemented as the unary form of `call`:

```javascript
var curry = unary(call);
```

### with

`splat` was present in earlier versions of `allong.es` but has been deprecated as being too cryptic. Instead, there is a general naming convention that works as follows. Many binary functions such as `map` and `filter` are historically written to take a noun or collection as the first argument and a verb as the second.

However, reversing and currying these functions is super-useful as it makes composeable functions out of them. That's why `callFlipped` is so important. But to save you the trouble of writing `callFlipped map` everywhere, many such functions in `allong.es` have a clipped version pre-defined and named with the suffix `With`:

```
map(list, function)       <=> mapWith(function, list)
filter(list, function)    <=> filterWith(function, list)
get(object, propertyName) <=> getWith(propertyName, object)
pluck(list, propertyName) <=> pluckWith(propertyName, list)
```

So you "map" a list, but "mapWith" a function. And of course, they are all curried. For example:

```
map(list)(function)       <=> mapWith(function)(list)
deepMap(list)(function)   <=> deepMapWith(function)(list)
filter(list)(function)    <=> filterWith(function)(list)
get(object)(propertyName) <=> getWith(propertyName)(object)
pluck(list)(propertyName) <=> pluckWith(propertyName)(list)
```

Thus if you have a collection such as:

```javascript
var users = [
  { name: 'Huey' },
  { name: 'Dewey' },
  { name: 'Louie' }
]
```

You can get the names with either:

```javascript
pluck(users, 'name')
  //=> ['Huey', 'Dewey', 'Louie']
```

Or:

```javascript
pluckWith('name', users)
  //=> ['Huey', 'Dewey', 'Louie']
```

The latter is interesting because `pluck` and `pluckWith` are both automatically curried (like almost everything that isn't named "now"). Thus, we could also write:

```javascript
var namesOf = pluckWith('name');

// ...
namesOf(users)
  //=> ['Huey', 'Dewey', 'Louie']
```

## Arity Function Decorators

### variadic

Makes a function into a variadic (accepts any number of arguments). The last named parameter will be given an array of arguments.

```javascript
var variadic = require('allong.es').allong.es.variadic;

var fn = variadic(function (a) { return a })

fn()
  //=> []
fn(1, 2, 3)
  //=> [1,2,3]

fn = variadic(function (a,b) { return { a: a, b: b } })

fn()
  //=> { a: undefined, b: [] }
fn(1)
  //=> { a: 1, b: [] }
fn(1,2,3)
  //=> { a: 1, b: [2, 3] }
```

### variadic, part ii

When given just the function, `variadic` returns a function with an arity of zero. This is consistent with JavaScript programming practice. There are times when you wish to report an arity, meaning that you want the returned function to have its `length` getibute set.

You do this by prefacing the function argument with a length:

```javascript
fn = variadic(function (a,b) { return { a: a, b: b } });

fn.length
  //=> 0
  
fn2 = variadic(1, function (a,b) { return { a: a, b: b } }); 

fn2.length
  //=> 1
```

### unary, binary, and ternary

Sometimes, you have a function that takes multiple arguments, but you only want it to accept one, or two, or maybe three arguments and ignore the rest. For example, `parseInt` takes a radix as an optional second parameter. And that is havoc if you try to use it with `Array.map`:

```javascript
['1', '2', '3', '4', '5'].map(parseInt)
  //=> [ 1,
  //     NaN,
  //     NaN,
  //     NaN,
  //     NaN ]
```

Use `unary(parseInt)` to solve the problem:

```javascript
['1', '2', '3', '4', '5'].map(unary(parseInt))
  //=> [ 1, 2, 3, 4, 5 ]
```

`binary` has similar uses when working with `Array.reduce` and its habit of passing three parameters to your supplied function.

## Miscellaneous Combinators

### bound

```javascript
var bound = require('allong.es').allong.es.bound;
    
bound(fn, args...)(obj)
  //=> fn.bind(obj, args...)
```

### getWith

```javascript
var getWith = require('allong.es').allong.es.getWith;
    
array.map(getWith('property'))
  //=> array.map(function (element) {
  //               return element['property']
  //             })
```

## Functional Composition

```javascript
var compose = require('allong.es').allong.es.compose,
    sequence = require('allong.es').allong.es.sequence;
    
compose(a, b, c)
  //=> function (x) {
  //     return a(b(c(x)))
  //   }
 
sequence(a, b, c)
  //=> function (x) {
  //     return c(b(a(x)))
  //   }
```

## List Combinators

### mapWith and deepMapWith

```javascript
var mapWith = require('allong.es').allong.es.mapWith,
    deepMapWith = require('allong.es').allong.es.deepMapWith;
    
var squareList = mapWith(function (x) { return x * x })

squareList([1, 2, 3, 4])
  //=> [1, 4, 9, 16]
  
var squareTree = deepMapWith(function (x) { return x * x })

squareTree([1, 2, [3, 4]])
  //=> [1, 4, [9, 16]]
```

## Function/Method Decorators

### maybe

```javascript
var maybe = require('allong.es').allong.es.maybe;
    
var safeFirst = maybe(function (arr) { return arr[0] })

safeFirst([1, 2, 3])
  //=> 1
safeFirst(null)
  //=> null
```

### tap

```javascript
var tap = require('allong.es').allong.es.tap;
    
tap([1, 2, 3, 4, 5], send('pop'))
  //=> [1, 2, 3, 4]
```

### fluent

```javascript
var fluent = require('allong.es').allong.es.fluent;
    
Role = function () {}

Role.prototype.set = fluent( function (property, name) { 
  this[property] = name 
})

var doomed = new Role()
  .set('name', "Fredo")
  .set('relationship', 'brother')
  .set('parts', ['I', 'II'])
```

### once

```javascript
var once = require('allong.es').allong.es.once;
    
var message = once( function () { console.log("Hello, it's me") })

message()
  //=> "Hello, it's me"
message()
  //=>
message()
  //=>
message()
  //=>
```

## Decorating Classes/Constructors

```javascript
var mixin = require('allong.es').allong.es.mixin,
    classDecorator = require('allong.es').allong.es.classDecorator;
    
function Todo (name) {
  var self = this instanceof Todo
             ? this
             : new Todo();
  self.name = name || 'Untitled';
  self.done = false;
};

Todo.prototype.do = fluent( function () {
  this.done = true;
});

Todo.prototype.undo = fluent( function () {
  this.done = false;
});

var AddLocation = mixin({
      setLocation: fluent( function (location) {
        this.location = location;
      }),
      getLocation: function () { return this.location; }
    });

AddLocation.call(Todo.prototype);
// Or use AddLocation(Todo.prototype)

new Todo("Vacuum").setLocation('Home');
  //=> { name: 'Vacuum',
  //     done: false,
  //     location: 'Home' }

var AndColourCoded = classDecorator({
  setColourRGB: fluent( function (r, g, b) {
    this.colourCode = { r: r, g: g, b: b };
  }),
  getColourRGB: function () {
    return this.colourCode;
  }
});

var ColourTodo = AndColourCoded(Todo);

new ColourTodo('Use More Decorators').setColourRGB(0, 255, 0);
  //=> { name: 'Use More Decorators',
  //     done: false,
  //     colourCode: { r: 0, g: 255, b: 0 } }
```

Note: `classDecorator` works with JavaScript constructors that have a default implementation (they work properly with no arguments), and are new-agnostic (they can be called with new or as a normal function). `Todo` above has both properties.

## Functional Iterators

Functional iterators are stateful functions that "iterate over" the values in some ordered data set. You call the iterator repeatedly to obtain the values, and it will either never stop returning values (an infinite data set) or return `undefined` when there are no more values to return.

The functional iterators utilities are all namespaced:

```javascript
var iterators = require('allong.es').allong.es.iterators;
```

### FlatArrayIterator and RecursiveArrayIterator

Making functional iterators from arrays:

```javascript
var FlatArrayIterator = iterators.FlatArrayIterator,
    RecursiveArrayIterator = iterators.RecursiveArrayIterator;
    
var i = FlatArrayIterator([1, 2, 3, 4, 5]);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined
    
var i = FlatArrayIterator([1, [2, 3, 4], 5]);

i();
  //=> 1
i();
  //=> [2, 3, 4]
i();
  //=> 5
i();
  //=> undefined
    
var i = RecursiveArrayIterator([1, [2, 3, 4], 5]);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined
```

### range and numbers

```javascript
var range = iterators.range,
    numbers = iterators.numbers;

var i = range(1, 5);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined

var i = range(1, 5, 2);

i();
  //=> 1
i();
  //=> 3
i();
  //=> 5
i();
  //=> undefined

var i = range(5, 1);

i();
  //=> 5
i();
  //=> 4
i();
  //=> 3
i();
  //=> 2
i();
  //=> 1
i();
  //=> undefined

var i = range(1);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...

var i = numbers();

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...

var i = numbers(0);

i();
  //=> 0
i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...
```

### unfold and unfoldWithReturn

Unfold makes an iterator out of a seed by successively applying a function to the seed value. Here's an example duplicating the "numbers" feature:

```javascript
var unfold = iterators.unfold,
    unfoldWithReturn = iterators.unfoldWithReturn;
    
var i = unfold(1, function (n) { return n + 1; });

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...
    
var i = unfoldWithReturn(1, function (n) { 
  return [n + 1, n + n]; 
});

i();
  //=> 2
i();
  //=> 4
i();
  //=> 6
// ...
```

A richer example of `unfoldWithReturn`:

```javaascript
var cards = ['A', 2, 3, 4, 5, 6, 7, 8, 9, '10', 'J', 'Q', 'K'];

function pickCard (deck) {
  var position;
  
  if (deck.length === 0) {
    return [[], void 0];
  }
  else {
    position = Math.floor(Math.random() * deck.length);
    return [
      deck.slice(0, position).concat(deck.slice(position + 1)),
      deck[position]
    ];
  }
};

var i = unfoldWithReturn(cards, pickCard);

i();
  //=> 5
i();
  //=> 4
i();
  //=> 2
i();
  //=> J
  
// ...
```

### map

Stateless mapping of an iterator to another iterator:

```javascript
var map = iterators.map;
    
var squares = map(numbers, function (n) { return n * n; });

squares();
  //=> 1
squares();
  //=> 4
squares();
  //=> 9
// ...
```

### accumulate

Accumulating an iterator to another iterator, a/k/a stateful mapping, with an optional seed:

```javascript
var accumulate = iterators.accumulate;
    
var runningTotal = accumulate(numbers, function (accumulation, n) { 
      return accumulation + n; 
    });

runningTotal();
  //=> 1
runningTotal();
  //=> 3
runningTotal();
  //=> 6
runningTotal();
  //=> 10
runningTotal();
  //=> 15
// ...

var runningTotal = accumulate(numbers, function (accumulation, n) { 
      return accumulation + n; 
    }, 5);

runningTotal();
  //=> 6
runningTotal();
  //=> 8
runningTotal();
  //=> 11
runningTotal();
  //=> 15
runningTotal();
  //=> 20
// ...
```

### accumulateWithReturn

This code transforms filters duplicates out of an iterator of numbers by turning them into "false." It consumes space proportional to the time it runs and the size of the set of possible numbers in its iterator.

```javascript
var accumulateWithReturn = iterators.accumulateWithReturn;
    
var randomNumbers = function () {
  return Math.floor(Math.random() * 10);
};

randomNumbers();
  //=> 7
randomNumbers();
  //=> 0
randomNumbers();
  //=> 1
randomNumbers();
  //=> 1
randomNumbers();
  //=> 6
// ...

var uniques = accumulateWithReturn(randomNumbers, function (alreadySeen, number) {
  var key = number.toString();
  
  if (alreadySeen[key]) {
    return [alreadySeen, false];
  }
  else {
    alreadySeen[key] = true;
    return [alreadySeen, number];
  }
}, {});

uniques();
  //=> 7
uniques();
  //=> 5
uniques();
  //=> 1
uniques();
  //=> false
uniques();
  //=> 9
uniques();
  //=> 4
uniques();
  //=> false
// ...
```

### select and reject

```javascript
var select = iterators.select,
    reject = iterators.reject;

function isEven (number) {
  return number === 0 || !isEven(number - 1);
};

var evens = select(randomNumbers, isEven);

evens();
  //=> 0
evens();
  //=> 6
evens();
  //=> 0
evens();
  //=> 2
evens();
  //=> 4
// ...

var odds = reject(randomNumbers, isEven);

odds();
  //=> 3
odds();
  //=> 1
odds();
  //=> 7
odds();
  //=> 9
odds();
  //=> 9
// ...
```

Note: `select` and `reject` will enter an "infinite loop" if the iterator does not terminate and also does not have any elements matching the condition.

### slice

```javascript
var slice = iterators.slice,
    numbers = unfold(1, function (n) { return n + 1; });

var i = slice(numbers, 3);

i();
  //=> 4
i();
  //=> 5
i();
  //=> 6

i = slice(numbers, 3, 2);

i();
  //=> 10
i();
  //=> 11
i();
  //=> undefined
```

### take

```javascript
var take = iterators.take,
    numbers = unfold(1, function (n) { return n + 1; });

var i = take(numbers);

i();
  //=> 1
i();
  //=> undefined

var i = take(numbers);

i();
  //=> 2
i();
  //=> undefined

var i = take(numbers, 3);

i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined
// ...
```

### drop

```javascript
var drop = iterators.drop,
    numbers = unfold(1, function (n) { return n + 1; });

drop(numbers);

numbers();
  //=> 2
numbers();
  //=> 3
numbers();
  //=> 4

drop(numbers);

numbers();
  //=> 6
numbers();
  //=> 7

drop(numbers, 3);

numbers();
  //=> 11
numbers();
  //=> 12
// ...
```

## Trampolining

```
var trampoline = require('allong.es').allong.es.trampoline,
    tailCall = require('allong.es').allong.es.tailCall;
    
function factorial (n) {
  var _factorial = trampoline( function myself (acc, n) {
    return n > 0
      ? tailCall(myself, acc * n, n - 1)
      : acc
  });
  
  return _factorial(1, n);
};

factorial(10);
  //=> 3628800
```


================================================
FILE: _site/Gruntfile.coffee
================================================
module.exports = (grunt) ->

  grunt.initConfig 
    pkg: grunt.file.readJSON('package.json'),
    uglify:
      options:
        banner: '/*! http://github.com/raganwald/<%= pkg.name %> v<%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> (c) 2012-2013 Reg Braithwaite MIT Licensed */\n'
      build:
        src: 'lib/<%= pkg.name %>.js',
        dest: 'lib/<%= pkg.name %>.min.js'

  grunt.loadNpmTasks('grunt-contrib-uglify')

  grunt.registerTask('default', ['uglify'])

================================================
FILE: _site/README.md
================================================
# `allong.es`

The `allong.es` library is a collection of functions designed to facilitate writing JavaScript and/or CoffeeScript with functions as first-class values. The emphasis in `allong.es` is on composing and decomposing functions using combinators and decorators. `allong.es` is designed to complement libraries like [Underscore](http://underscorejs.org), not compete with them.

## Currying and Partial Application

At the heart of `allong.es` are the functions that curry and partially apply other functions. The two most important to understand are `call` and `apply`. They work very much like the `.call` and `.apply` methods that every JavaScript function implements:

```javascript
function greet (how, whom) {
  return '' + how + ', ' + whom + '!';
};
  
call(greet, 'Hello', 'Tom')
  //=> 'Hello, Tom!'
  
apply(greet, ['Hello', 'Tom'])
  //=> 'Hello, Tom!'
```

Their "special sauce" is that they automatically *curry* the supplied function, so if you provide fewer or no arguments, you get back a partially applied or curried function:

```javascript
call(greet)('Hello')('Tom')
  //=> 'Hello, Tom!'
  
call(greet, 'Hello')('Tom'])
  //=> 'Hello, Tom!'
  
apply(greet, [])('Hello')('Tom')
  //=> 'Hello, Tom!'
  
apply(greet, ['Hello'])('Tom'])
  //=> 'Hello, Tom!'
```

### immediate application

If you don't want the currying/partial application behaviour, there is an immediate application version named (appropriately), `callNow` (and also another named `applyNow`, not shown):

```javascript
callNow(greet, 'Hello', 'Tom')
  //=> 'Hello, Tom!'
  
callNow(greet, 'Hello')
  //=> 'Hello, undefined!'
```

### variations on the order of applying the arguments

`callRight` applies any arguments supplied to the right. If you supply all the arguments, it's the same as `call`, but if you supply fewer arguments, you get a right partial application:

```javascript
callRight(greet, 'Hello', 'Tom')
  //=> 'Hello, Tom!'
  
callRight(greet, 'Hello')('Tom')
  //=> 'Tom, Hello!'
```

`callFlipped` applies the arguments backwards, even when curried:

```javascript
callFlipped(greet, 'Hello', 'Tom')
  //=> 'Tom, Hello!'
  
callFlipped(greet, 'Hello')('Tom')
  //=> 'Tom, Hello!'
  
callFlipped(greet)('Hello')('Tom')
  //=> 'Tom, Hello!'
```

### more partial application

`callLeft` is actually synonymous with `call`: It applies arguments given to the left. We've seen `callRight` above. Both are *variadic*: You can supply as many arguments as you want.

`callFirst` and `callLast` are just like `callLeft` and `callRight`, but they are *binary* functions: They accept a function and exactly one argument. This is sometimes useful when combining functions together.

`callFirst` and `callLast` both have "flipped and curried" versions (`callFirstWith` and `callLastWith`). `callLastWith` is especially useful for working with functions written in "collection - operation" style. Here we take advantage of the fact that they are "automatically curried" to implement the popular `pluck` function.

### currying

`allong.es` does support the `curry` function, it is implemented as the unary form of `call`:

```javascript
var curry = unary(call);
```

### with

`splat` was present in earlier versions of `allong.es` but has been deprecated as being too cryptic. Instead, there is a general naming convention that works as follows. Many binary functions such as `map` and `filter` are historically written to take a noun or collection as the first argument and a verb as the second.

However, reversing and currying these functions is super-useful as it makes composeable functions out of them. That's why `callFlipped` is so important. But to save you the trouble of writing `callFlipped map` everywhere, many such functions in `allong.es` have a clipped version pre-defined and named with the suffix `With`:

```
map(list, function)       <=> mapWith(function, list)
filter(list, function)    <=> filterWith(function, list)
get(object, propertyName) <=> getWith(propertyName, object)
pluck(list, propertyName) <=> pluckWith(propertyName, list)
```

So you "map" a list, but "mapWith" a function. And of course, they are all curried. For example:

```
map(list)(function)       <=> mapWith(function)(list)
deepMap(list)(function)   <=> deepMapWith(function)(list)
filter(list)(function)    <=> filterWith(function)(list)
get(object)(propertyName) <=> getWith(propertyName)(object)
pluck(list)(propertyName) <=> pluckWith(propertyName)(list)
```

Thus if you have a collection such as:

```javascript
var users = [
  { name: 'Huey' },
  { name: 'Dewey' },
  { name: 'Louie' }
]
```

You can get the names with either:

```javascript
pluck(users, 'name')
  //=> ['Huey', 'Dewey', 'Louie']
```

Or:

```javascript
pluckWith('name', users)
  //=> ['Huey', 'Dewey', 'Louie']
```

The latter is interesting because `pluck` and `pluckWith` are both automatically curried (like almost everything that isn't named "now"). Thus, we could also write:

```javascript
var namesOf = pluckWith('name');

// ...
namesOf(users)
  //=> ['Huey', 'Dewey', 'Louie']
```

## Arity Function Decorators

### variadic

Makes a function into a variadic (accepts any number of arguments). The last named parameter will be given an array of arguments.

```javascript
var variadic = require('allong.es').allong.es.variadic;

var fn = variadic(function (a) { return a })

fn()
  //=> []
fn(1, 2, 3)
  //=> [1,2,3]

fn = variadic(function (a,b) { return { a: a, b: b } })

fn()
  //=> { a: undefined, b: [] }
fn(1)
  //=> { a: 1, b: [] }
fn(1,2,3)
  //=> { a: 1, b: [2, 3] }
```

### variadic, part ii

When given just the function, `variadic` returns a function with an arity of zero. This is consistent with JavaScript programming practice. There are times when you wish to report an arity, meaning that you want the returned function to have its `length` getibute set.

You do this by prefacing the function argument with a length:

```javascript
fn = variadic(function (a,b) { return { a: a, b: b } });

fn.length
  //=> 0
  
fn2 = variadic(1, function (a,b) { return { a: a, b: b } }); 

fn2.length
  //=> 1
```

### unary, binary, and ternary

Sometimes, you have a function that takes multiple arguments, but you only want it to accept one, or two, or maybe three arguments and ignore the rest. For example, `parseInt` takes a radix as an optional second parameter. And that is havoc if you try to use it with `Array.map`:

```javascript
['1', '2', '3', '4', '5'].map(parseInt)
  //=> [ 1,
  //     NaN,
  //     NaN,
  //     NaN,
  //     NaN ]
```

Use `unary(parseInt)` to solve the problem:

```javascript
['1', '2', '3', '4', '5'].map(unary(parseInt))
  //=> [ 1, 2, 3, 4, 5 ]
```

`binary` has similar uses when working with `Array.reduce` and its habit of passing three parameters to your supplied function.

## Miscellaneous Combinators

### bound

```javascript
var bound = require('allong.es').allong.es.bound;
    
bound(fn, args...)(obj)
  //=> fn.bind(obj, args...)
```

### getWith

```javascript
var getWith = require('allong.es').allong.es.getWith;
    
array.map(getWith('property'))
  //=> array.map(function (element) {
  //               return element['property']
  //             })
```

## Functional Composition

```javascript
var compose = require('allong.es').allong.es.compose,
    sequence = require('allong.es').allong.es.sequence;
    
compose(a, b, c)
  //=> function (x) {
  //     return a(b(c(x)))
  //   }
 
sequence(a, b, c)
  //=> function (x) {
  //     return c(b(a(x)))
  //   }
```

## List Combinators

### mapWith and deepMapWith

```javascript
var mapWith = require('allong.es').allong.es.mapWith,
    deepMapWith = require('allong.es').allong.es.deepMapWith;
    
var squareList = mapWith(function (x) { return x * x })

squareList([1, 2, 3, 4])
  //=> [1, 4, 9, 16]
  
var squareTree = deepMapWith(function (x) { return x * x })

squareTree([1, 2, [3, 4]])
  //=> [1, 4, [9, 16]]
```

## Function/Method Decorators

### maybe

```javascript
var maybe = require('allong.es').allong.es.maybe;
    
var safeFirst = maybe(function (arr) { return arr[0] })

safeFirst([1, 2, 3])
  //=> 1
safeFirst(null)
  //=> null
```

### tap

```javascript
var tap = require('allong.es').allong.es.tap;
    
tap([1, 2, 3, 4, 5], send('pop'))
  //=> [1, 2, 3, 4]
```

### fluent

```javascript
var fluent = require('allong.es').allong.es.fluent;
    
Role = function () {}

Role.prototype.set = fluent( function (property, name) { 
  this[property] = name 
})

var doomed = new Role()
  .set('name', "Fredo")
  .set('relationship', 'brother')
  .set('parts', ['I', 'II'])
```

### once

```javascript
var once = require('allong.es').allong.es.once;
    
var message = once( function () { console.log("Hello, it's me") })

message()
  //=> "Hello, it's me"
message()
  //=>
message()
  //=>
message()
  //=>
```

## Decorating Classes/Constructors

```javascript
var mixin = require('allong.es').allong.es.mixin,
    classDecorator = require('allong.es').allong.es.classDecorator;
    
function Todo (name) {
  var self = this instanceof Todo
             ? this
             : new Todo();
  self.name = name || 'Untitled';
  self.done = false;
};

Todo.prototype.do = fluent( function () {
  this.done = true;
});

Todo.prototype.undo = fluent( function () {
  this.done = false;
});

var AddLocation = mixin({
      setLocation: fluent( function (location) {
        this.location = location;
      }),
      getLocation: function () { return this.location; }
    });

AddLocation.call(Todo.prototype);
// Or use AddLocation(Todo.prototype)

new Todo("Vacuum").setLocation('Home');
  //=> { name: 'Vacuum',
  //     done: false,
  //     location: 'Home' }

var AndColourCoded = classDecorator({
  setColourRGB: fluent( function (r, g, b) {
    this.colourCode = { r: r, g: g, b: b };
  }),
  getColourRGB: function () {
    return this.colourCode;
  }
});

var ColourTodo = AndColourCoded(Todo);

new ColourTodo('Use More Decorators').setColourRGB(0, 255, 0);
  //=> { name: 'Use More Decorators',
  //     done: false,
  //     colourCode: { r: 0, g: 255, b: 0 } }
```

Note: `classDecorator` works with JavaScript constructors that have a default implementation (they work properly with no arguments), and are new-agnostic (they can be called with new or as a normal function). `Todo` above has both properties.

## Functional Iterators

Functional iterators are stateful functions that "iterate over" the values in some ordered data set. You call the iterator repeatedly to obtain the values, and it will either never stop returning values (an infinite data set) or return `undefined` when there are no more values to return.

The functional iterators utilities are all namespaced:

```javascript
var iterators = require('allong.es').allong.es.iterators;
```

### FlatArrayIterator and RecursiveArrayIterator

Making functional iterators from arrays:

```javascript
var FlatArrayIterator = iterators.FlatArrayIterator,
    RecursiveArrayIterator = iterators.RecursiveArrayIterator;
    
var i = FlatArrayIterator([1, 2, 3, 4, 5]);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined
    
var i = FlatArrayIterator([1, [2, 3, 4], 5]);

i();
  //=> 1
i();
  //=> [2, 3, 4]
i();
  //=> 5
i();
  //=> undefined
    
var i = RecursiveArrayIterator([1, [2, 3, 4], 5]);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined
```

### range and numbers

```javascript
var range = iterators.range,
    numbers = iterators.numbers;

var i = range(1, 5);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined

var i = range(1, 5, 2);

i();
  //=> 1
i();
  //=> 3
i();
  //=> 5
i();
  //=> undefined

var i = range(5, 1);

i();
  //=> 5
i();
  //=> 4
i();
  //=> 3
i();
  //=> 2
i();
  //=> 1
i();
  //=> undefined

var i = range(1);

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...

var i = numbers();

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...

var i = numbers(0);

i();
  //=> 0
i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...
```

### unfold and unfoldWithReturn

Unfold makes an iterator out of a seed by successively applying a function to the seed value. Here's an example duplicating the "numbers" feature:

```javascript
var unfold = iterators.unfold,
    unfoldWithReturn = iterators.unfoldWithReturn;
    
var i = unfold(1, function (n) { return n + 1; });

i();
  //=> 1
i();
  //=> 2
i();
  //=> 3
// ...
    
var i = unfoldWithReturn(1, function (n) { 
  return [n + 1, n + n]; 
});

i();
  //=> 2
i();
  //=> 4
i();
  //=> 6
// ...
```

A richer example of `unfoldWithReturn`:

```javaascript
var cards = ['A', 2, 3, 4, 5, 6, 7, 8, 9, '10', 'J', 'Q', 'K'];

function pickCard (deck) {
  var position;
  
  if (deck.length === 0) {
    return [[], void 0];
  }
  else {
    position = Math.floor(Math.random() * deck.length);
    return [
      deck.slice(0, position).concat(deck.slice(position + 1)),
      deck[position]
    ];
  }
};

var i = unfoldWithReturn(cards, pickCard);

i();
  //=> 5
i();
  //=> 4
i();
  //=> 2
i();
  //=> J
  
// ...
```

### map

Stateless mapping of an iterator to another iterator:

```javascript
var map = iterators.map;
    
var squares = map(numbers, function (n) { return n * n; });

squares();
  //=> 1
squares();
  //=> 4
squares();
  //=> 9
// ...
```

### accumulate

Accumulating an iterator to another iterator, a/k/a stateful mapping, with an optional seed:

```javascript
var accumulate = iterators.accumulate;
    
var runningTotal = accumulate(numbers, function (accumulation, n) { 
      return accumulation + n; 
    });

runningTotal();
  //=> 1
runningTotal();
  //=> 3
runningTotal();
  //=> 6
runningTotal();
  //=> 10
runningTotal();
  //=> 15
// ...

var runningTotal = accumulate(numbers, function (accumulation, n) { 
      return accumulation + n; 
    }, 5);

runningTotal();
  //=> 6
runningTotal();
  //=> 8
runningTotal();
  //=> 11
runningTotal();
  //=> 15
runningTotal();
  //=> 20
// ...
```

### accumulateWithReturn

This code transforms filters duplicates out of an iterator of numbers by turning them into "false." It consumes space proportional to the time it runs and the size of the set of possible numbers in its iterator.

```javascript
var accumulateWithReturn = iterators.accumulateWithReturn;
    
var randomNumbers = function () {
  return Math.floor(Math.random() * 10);
};

randomNumbers();
  //=> 7
randomNumbers();
  //=> 0
randomNumbers();
  //=> 1
randomNumbers();
  //=> 1
randomNumbers();
  //=> 6
// ...

var uniques = accumulateWithReturn(randomNumbers, function (alreadySeen, number) {
  var key = number.toString();
  
  if (alreadySeen[key]) {
    return [alreadySeen, false];
  }
  else {
    alreadySeen[key] = true;
    return [alreadySeen, number];
  }
}, {});

uniques();
  //=> 7
uniques();
  //=> 5
uniques();
  //=> 1
uniques();
  //=> false
uniques();
  //=> 9
uniques();
  //=> 4
uniques();
  //=> false
// ...
```

### select and reject

```javascript
var select = iterators.select,
    reject = iterators.reject;

function isEven (number) {
  return number === 0 || !isEven(number - 1);
};

var evens = select(randomNumbers, isEven);

evens();
  //=> 0
evens();
  //=> 6
evens();
  //=> 0
evens();
  //=> 2
evens();
  //=> 4
// ...

var odds = reject(randomNumbers, isEven);

odds();
  //=> 3
odds();
  //=> 1
odds();
  //=> 7
odds();
  //=> 9
odds();
  //=> 9
// ...
```

Note: `select` and `reject` will enter an "infinite loop" if the iterator does not terminate and also does not have any elements matching the condition.

### slice

```javascript
var slice = iterators.slice,
    numbers = unfold(1, function (n) { return n + 1; });

var i = slice(numbers, 3);

i();
  //=> 4
i();
  //=> 5
i();
  //=> 6

i = slice(numbers, 3, 2);

i();
  //=> 10
i();
  //=> 11
i();
  //=> undefined
```

### take

```javascript
var take = iterators.take,
    numbers = unfold(1, function (n) { return n + 1; });

var i = take(numbers);

i();
  //=> 1
i();
  //=> undefined

var i = take(numbers);

i();
  //=> 2
i();
  //=> undefined

var i = take(numbers, 3);

i();
  //=> 3
i();
  //=> 4
i();
  //=> 5
i();
  //=> undefined
// ...
```

### drop

```javascript
var drop = iterators.drop,
    numbers = unfold(1, function (n) { return n + 1; });

drop(numbers);

numbers();
  //=> 2
numbers();
  //=> 3
numbers();
  //=> 4

drop(numbers);

numbers();
  //=> 6
numbers();
  //=> 7

drop(numbers, 3);

numbers();
  //=> 11
numbers();
  //=> 12
// ...
```

## Trampolining

```
var trampoline = require('allong.es').allong.es.trampoline,
    tailCall = require('allong.es').allong.es.tailCall;
    
function factorial (n) {
  var _factorial = trampoline( function myself (acc, n) {
    return n > 0
      ? tailCall(myself, acc * n, n - 1)
      : acc
  });
  
  return _factorial(1, n);
};

factorial(10);
  //=> 3628800
```


================================================
FILE: _site/functions.txt
================================================
{ variadic: [Function],
  unvariadic: [Function: unvariadic],
  curryWithLeftAndRight: [Function: curryWithLeftAndRight],
  unary: [Function: unary],
  binary: [Function: binary],
  ternary: [Function: ternary],
  quaternary: [Function: quaternary],
  selfCurrying: [Function: selfCurrying],
  compose: [Function],
  sequence: [Function],
  callFlipped: 
   { [Function]
     unary: [Function: unary],
     binary: [Function: binary],
     ternary: [Function: ternary],
     quaternary: [Function: quaternary],
     callWithLeftFlipped: [Function: callWithLeftFlipped] },
  flip: [Function: myself],
  applyNow: [Function: myself],
  callNow: 
   { [Function]
     unary: [Function: unary],
     binary: [Function: binary],
     ternary: [Function: ternary],
     quaternary: [Function: quaternary] },
  call: [Function],
  callLeft: [Function],
  callRight: [Function],
  applyN: [Function: myself],
  applyLeftNow: [Function: myself],
  callLeftNow: [Function],
  applyLeftNowWith: [Function: myself],
  applyRightNow: [Function: myself],
  callRightNow: [Function],
  applyRightNowWith: [Function: myself],
  callFirst: [Function: myself],
  callLast: [Function: myself],
  callFirstWith: [Function: myself],
  callLastWith: [Function: myself],
  bound: [Function],
  defaults: [Function],
  args: [Function: args],
  curry: [Function: myself],
  map: [Function: myself],
  mapWith: [Function: myself],
  filterWith: [Function: filterWith],
  deepMap: [Function: myself],
  deepMapWith: [Function: myself],
  maybe: [Function: maybe],
  tap: [Function: tap],
  fluent: [Function: fluent],
  returnFirst: [Function],
  tee: [Function: tee],
  once: [Function: once],
  memoized: [Function: memoized],
  mixin: [Function: mixin],
  classDecorator: [Function: classDecorator],
  bind: [Function: bind],
  unbind: [Function: unbind],
  invoke: [Function: invoke],
  get: [Function: get],
  getWith: [Function: getWith],
  send: [Function],
  pluckWith: [Function],
  pluck: [Function: myself],
  trampoline: [Function: trampoline],
  tailCall: [Function],
  Thunk: [Function: Thunk],
  iterators: 
   { accumulate: [Function: accumulate],
     accumulateWithReturn: [Function: accumulateWithReturn],
     fold: [Function: fold],
     unfold: [Function: unfold],
     unfoldWithReturn: [Function: unfoldWithReturn],
     map: [Function: map],
     select: [Function: select],
     reject: [Function: reject],
     filter: [Function: select],
     find: [Function: find],
     slice: [Function: slice],
     drop: [Function],
     take: [Function: take],
     FlatArrayIterator: [Function: FlatArrayIterator],
     RecursiveArrayIterator: [Function: RecursiveArrayIterator],
     constant: [Function: K],
     K: [Function: K],
     numbers: [Function: myself],
     range: [Function: range] } }

================================================
FILE: _site/lib/allong.es.js
================================================
/*! http://github.com/raganwald/allong.es (c) 2012-2013 Reg Braithwaite MIT Licensed */
(function (root) {
  
  // Setup
  // -----

  // Establish the root object, `window` in the browser, or `global` on the server.
  // *taken from [Underscore.js](http://underscorejs.org/)*

  var root = this;
  
  var __slice = Array.prototype.slice,
      __map = Array.prototype.map,
      __hasProp = Array.prototype.hasOwnProperty,
      __filter = Array.prototype.filter;
  
  // here's our export object
  var allong = { es: {} };

  // ## Functionalizing
  //
  // The utility functions operate on other functions. They can also operate on string
  // abbreviations for functions by calling `functionalzie(...)` on their inputs.

  // SHIM
  if ('ab'.split(/a*/).length < 2) {
    if (typeof console !== "undefined" && console !== null) {
      console.log("Warning: IE6 split is not ECMAScript-compliant.  This breaks '->1'");
    }
  }

  // on the fence about whether to export this?
  function to_function (str) {
    var expr, leftSection, params, rightSection, sections, v, vars, _i, _len;
    params = [];
    expr = str;
    sections = expr.split(/\s*->\s*/m);
    if (sections.length > 1) {
      while (sections.length) {
        expr = sections.pop();
        params = sections.pop().split(/\s*,\s*|\s+/m);
        sections.length && sections.push('(function(' + params + '){return (' + expr + ')})');
      }
    } else if (expr.match(/\b_\b/)) {
      params = '_';
    } else {
      leftSection = expr.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m);
      rightSection = expr.match(/[+\-*\/%&|\^\.=<>!]\s*$/m);
      if (leftSection || rightSection) {
        if (leftSection) {
          params.push('$1');
          expr = '$1' + expr;
        }
        if (rightSection) {
          params.push('$2');
          expr = expr + '$2';
        }
      } else {
        vars = str.replace(/(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*\s*:|this|arguments|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, '').match(/([a-z_$][a-z_$\d]*)/gi) || [];
        for (_i = 0, _len = vars.length; _i < _len; _i++) {
          v = vars[_i];
          params.indexOf(v) >= 0 || params.push(v);
        }
      }
    }
    return new Function(params, 'return (' + expr + ')');
  };

  function functionalize (fn) {
    if (typeof fn === 'function') {
      return fn;
    } else if (typeof fn === 'string' && /^[_a-zA-Z]\w*$/.test(fn)) {
      return function() {
        var args, receiver, _ref;
        receiver = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
        return (_ref = receiver[fn]).call.apply(_ref, [receiver].concat(__slice.call(args)));
      };
    } else if (typeof fn === 'string') {
      return to_function(fn);
    } else if (typeof fn.lambda === 'function') {
      return fn.lambda();
    } else if (typeof fn.toFunction === 'function') {
      return fn.toFunction();
    }
  };

  function extend () {
    var consumer = arguments[0],
        providers = __slice.call(arguments, 1),
        key,
        i,
        provider,
        except;

    for (i = 0; i < providers.length; ++i) {
      provider = providers[i];
      except = provider['except'] || [];
      except.push('except');
      for (key in provider) {
        if (except.indexOf(key) < 0 && provider.hasOwnProperty(key)) {
          consumer[key] = provider[key];
        };
      };
    };
    return consumer;
  };
      
  function rotate (array, n) {
    var copy = array.slice(0),
        i, 
        pull, 
        push;
    
    if (n !== 0) {
      n || (n = 1);
      if (n > 0) {
        pull = 'shift';
        push = 'push';
      }
      else {
        n = -n;
        pull = 'pop';
        push = 'unshift'
      }
      for (i = 0; i < n; ++i) {
        copy[push](copy[pull]());
      }
    }
    
    return copy;
  };
  
  function reverse (array) {
    return array.reduce(function (acc, element) {
      acc.unshift(element);
      return acc;
    }, []);
  };
  
  // # ARITY
  
  function invokeImmediately (fn) { return fn(); };

  // ### "Variadic"
  //
  //    fn = variadic(function (args) { return args })
  //
  //    fn()        //=> []
  //    fn(1)       //=> [1]
  //    fn(1, 2)    //=> [1, 2]
  //    fn(1, 2, 3) //=> [1, 2, 3]
  //
  //    fn = variadic(function (first, rest) { return [first, rest]})
  //
  //    fn()        //=> [undefined, []]
  //    fn(1)       //=> [1, []]
  //    fn(1, 2)    //=> [1, [2]]
  //    fn(1, 2, 3) //=> [1, [2, 3]]
  //
  //    fn = variadic(function (first, second, rest) { return [first, second, rest]})
  //
  //    fn()        //=> [undefined, undefined, []]
  //    fn(1)       //=> [1, undefined, []]
  //    fn(1, 2)    //=> [1, 2, []]
  //    fn(1, 2, 3) //=> [1, 2, [3]]
  var variadic = function () {
    var FUNCTIONS = {};
    function oldVariadic (fn) {
      var fnLength = fn.length;

      if (fnLength < 1) {
        return fn;
      }
      else if (fnLength === 1)  {
        return function () {
          return fn.call(this, __slice.call(arguments, 0))
        }
      }
      else {
        return function () {
          var numberOfArgs = arguments.length,
              namedArgs = __slice.call(arguments, 0, fnLength - 1),
              numberOfMissingNamedArgs = Math.max(fnLength - numberOfArgs - 1, 0),
              argPadding = new Array(numberOfMissingNamedArgs),
              variadicArgs = __slice.call(arguments, fn.length - 1);

          return fn.apply(this, namedArgs.concat(argPadding).concat([variadicArgs]))
        }
      }
    };
    return function (arity, fn) {
      if (fn == null) {
        fn = functionalize(arity);
        arity = 0;
      }
      else fn = functionalize(fn);
      var fnLength = fn.length;
      if (arity === 0) {
        return oldVariadic(fn);
      }
      else if (fnLength <= arity) {
        var fixedParams = fnLength - 1;
        var index = '' + arity + '-' + fixedParams;
        var code;
      
        if (FUNCTIONS[index] == null) {
          var parameters = new Array(arity);
          for (var i = 0; i < arity; ++i) {
            parameters[i] = "__" + i;
          }
          var pstr = parameters.join();
          if (fnLength > 1) {
            var cstr = parameters.slice(0, fnLength - 1).join();
            code = "return function ("+pstr+") { return fn.call("+cstr+", [].slice.call(arguments,"+(fnLength - 1)+")); };";
          }
          else code = "return function ("+pstr+") { return fn.call(this, [].slice.call(arguments, 0)); };";
          FUNCTIONS[index] = new Function(['fn'], code);
        }
        return FUNCTIONS[index](fn);
      }
      else throw 'not supported yet'
    };
  }();
  
  // sets a fixed arity for a function, without currying
  var unvariadic = (function () {
    var FUNCTIONS = {};
    return function unvariadic (arity, fn) {
      if (FUNCTIONS[arity] == null) {
        var parameters = new Array(arity);
        for (var i = 0; i < arity; ++i) {
          parameters[i] = "__" + i;
        }
        var pstr = parameters.join();
        var code = "return function ("+pstr+") { return fn.apply(this, arguments); };";
        FUNCTIONS[arity] = new Function(['fn'], code);
      }
      if (fn == null) {
        return function (fn) { return unvariadic(arity, fn); };
      }
      else return FUNCTIONS[arity](functionalize(fn));
    };
  })();

  // a kind of optional semantics: unary(f)(value) === f(value), unary(f)() === unary(f)
  function unary (fn) {
    return function unary (a) {
      if (a == null) {
        return unary;
      }
      else return fn(a);
    }
  };

  function binary (fn) {
    return function binary (a, b) {
      if (a == null) {
        return binary;
      }
      else if (b == null) {
        return unary(function (b) { return fn(a, b); });
      }
      else return fn(a, b);
    }
  };

  function ternary (fn) {
    return function ternary (a, b, c) {
      if (a == null) {
        return ternary;
      }
      else if (b == null) {
        return binary(function (b, c) { return fn(a, b, c); });
      }
      else if (c == null) {
        return unary(function (c) { return fn(a, b, c); });
      }
      else return fn(a, b, c);
    }
  };

  function quaternary (fn) {
    return function quaternary (a, b, c, d) {
      if (a == null) {
        return quaternary;
      }
      else if (b == null) {
        return ternary(function (b, c, d) { return fn(a, b, c, d); });
      }
      else if (c == null) {
        return binary(function (c, d) { return fn(a, b, c, d); });
      }
      else if (d == null) {
        return unary(function (d) { return fn(a, b, c, d); });
      }
      else return fn(a, b, c, d);
    }
  };
  
  var byArity = [
        invokeImmediately,
        unary,
        binary,
        ternary,
        quaternary
      ],
      byArityLength = byArity.length;
  
  function curryWithLeftAndRight (fn, leftArgs, rightArgs) {
    leftArgs || (leftArgs = []);
    rightArgs || (rightArgs = []);
    var fnLength = fn.length,
        remainingLength = fnLength - leftArgs.length - rightArgs.length;
    
    if (remainingLength < byArityLength) {
      return byArity[remainingLength](handleRemaining);
    }
    else return unvariadic(remainingLength, handleRemaining);
        
    function handleRemaining () {
      var params = __slice.call(arguments, 0, arguments.length),
          numParams = ((params.indexOf(void 0) >= 0)
            ? params.indexOf(void 0)
            : params.length),
          newLeft = leftArgs.concat(params.slice(0, numParams)),
          args = newLeft.concat(rightArgs),
          argsLength = args.length,
          remainingLength = fnLength - argsLength;
      
      if (remainingLength <= 0) {
        return fn.apply(this, args);
      }
      else return curryWithLeftAndRight(fn, newLeft, rightArgs);
    };
  };
  
  extend(allong.es, { curryWithLeftAndRight: curryWithLeftAndRight });
  
  // TODO: Deprecate
  function selfCurrying(fn) {
    fn = functionalize(fn);
    var fnLength = fn.length;
    
    if (fn.length > 0) {
      return variadic(fnLength, function (args) {
        if (args.length === 0) {
          return curry(fn)
        }
        else return fn.apply(this, args);
      });
    }
    else return fn;
  };
  
  extend(allong.es, {
    variadic: variadic,
    unvariadic: unvariadic,
    unary: unary,
    binary: binary,
    ternary: ternary,
    quaternary: quaternary
  });

  // ### Composition

  //    compose(a, b, c)
  //      //=> function (x) {
  //        return a(b(c(x)))
  //      }
  var compose = variadic( function compose (fns) {
    fns = fns.map(functionalize);
    
    var first, firstLength, second, rest;
    
    if (fns.length === 0) {
      return function () {};
    }
    else if (fns.length === 1) {
      return fns[0];
    }
    else if (fns.length === 2) {
      first = fns[0];
      firstLength = first.length;
      second = fns[1];
      
      if (firstLength === 1) {
        return function (a) { return first(second(a)); };
      }
      else if (firstLength === 2) {
        return function (a, b) { return first(second(a), b); };
      }
      else if (firstLength === 3) {
        return function (a, b, c) { return first(second(a), b, c); };
      }
      else return variadic( function (a, rest) {
        first.apply(this, [second(a)].concat(rest))
      });
    }
    else {
      var first = fns[0],
          butFirst = __slice.call(fns, 1);

      return compose.call(this, first, compose.apply(this, butFirst));
    }
  });

  extend(allong.es, {
    compose: compose
  });
  
  // # CALL_FLIPPED
  
  var callFlipped = (function () {
    
    function nullary (fn) {
      return variadic( function (args) {
        return fn.apply(this, reverse(args));
      });
    };
  
    // a kind of optional semantics: unary(f)(value) === f(value), unary(f)() === unary(f)
    function unary (fn) {
      return function unary (a) {
        if (a == null) {
          return unary;
        }
        else return fn(a);
      }
    };

    function binary (fn) {
      return function binary (a, b) {
        if (a == null) {
          return binary;
        }
        else if (b == null) {
          return unary(function (b) { return fn(b, a); });
        }
        else return fn(b, a);
      }
    };

    function ternary (fn) {
      return function ternary (a, b, c) {
        if (a == null) {
          return ternary;
        }
        else if (b == null) {
          return binary(function (c, b) { return fn(c, b, a); });
        }
        else if (c == null) {
          return unary(function (c) { return fn(c, b, a); });
        }
        else return fn(c, b, a);
      }
    };

    function quaternary (fn) {
      return function quaternary (a, b, c, d) {
        if (a == null) {
          return quaternary;
        }
        else if (b == null) {
          return ternary(function (d, c, b) { return fn(d, c, b, a); });
        }
        else if (c == null) {
          return binary(function (d, c) { return fn(d, c, b, a); });
        }
        else if (d == null) {
          return unary(function (d) { return fn(d, c, b, a); });
        }
        else return fn(d, c, b, a);
      }
    };
  
    var byArity = [
          nullary,
          unary,
          binary,
          ternary,
          quaternary
        ],
        byArityLength = byArity.length;

    function callWithLeftFlipped (fn, leftArgs) {
      leftArgs || ( leftArgs = []);
      var fnLength = fn.length,
          remainingLength = fnLength - leftArgs.length;
    
      if (remainingLength < byArityLength) {
        return byArity[remainingLength](handleRemaining);
      }
      else return unvariadic(remainingLength, handleRemaining);
        
      function handleRemaining () {
        var params = __slice.call(arguments, 0, arguments.length),
            numParams = (params.indexOf(void 0) >= 0)
              ? params.indexOf(void 0)
              : params.length
            args = leftArgs.concat(params.slice(0, numParams));
            argsLength = args.length,
            remainingLength = fnLength - argsLength;
      
        if (remainingLength <= 0) {
          return fn.apply(this, reverse(args));
        }
        else return callWithLeftFlipped(fn, args);
      };
    };
  
    return extend( variadic( function callFlipped (fn, args) {
      fn = functionalize(fn);
      var fnLength = fn.length,
          flipped = (fnLength < byArityLength)
            ? byArity[fnLength](fn)
            : callWithLeftFlipped(fn);
    
      if (args.length === 0) {
        return flipped;
      }
      else return flipped.apply(this, args);
    
    }), {
      unary: unary,
      binary: binary,
      ternary: ternary,
      quaternary: quaternary,
      callWithLeftFlipped: callWithLeftFlipped
    });
  
  })();
  
  // synonymish
  var flip = unary(callFlipped);

  extend(allong.es, {
    callFlipped: callFlipped,
    flip: flip
  });
  
  // # APPLY
  
  // the basics: apply and call
  
  // call that retuns a curried function
  
  var apply = binary( function (fn, args) {
    return curryWithLeftAndRight(fn, args, []);
  });
  
  var call = variadic( function (fn, args) {
    return curryWithLeftAndRight(fn, args, []);
  });
  
  var curry = unary(call);
  
  function urApply (fn, args) {
    return fn.apply(this, args);
  };
  
  var applyNow = call( urApply );
  
  var callNow = extend( variadic(applyNow), {
    unary: unary,
    binary: binary,
    ternary: ternary,
    quaternary: quaternary
  });
  
  // flipped forms
  
  var applyNowFlipped = flip(urApply);
  
  // apply and call left
  
  function urApplyLeft (fn, leftArgs) {
    var remainingLength = fn.length - leftArgs.length;
    
    if (fn.length > 0 && remainingLength > 0) {
      return variadic(remainingLength, function (args) {
        return fn.apply(this, leftArgs.concat(args));
      });
    }
    else return fn.apply(this, leftArgs);
  };
  
  var applyLeftNow = call(urApplyLeft);
  
  var callLeftNow = variadic(urApplyLeft);
  
  // flipped
  
  var applyLeftNowWith = flip(urApplyLeft);
  
  var applyRightNowWith = flip(urApplyRight);
  
  // rightmost
  
  function urApplyRight (fn, rightArgs) {
    var remainingLength = fn.length - rightArgs.length;
    
    if (fn.length > 0 && remainingLength > 0) {
      return variadic(remainingLength, function (args) {
        return fn.apply(this, args.concat(rightArgs));
      });
    }
    else return fn.apply(this, rightArgs);
  }
  
  var applyRightNow = call(urApplyRight);
  
  var callRightNow = variadic(urApplyRight);
  
  var callRight = variadic( function (fn, args) {
    return curryWithLeftAndRight(fn, [], args);
  });
  
  var callFirst = binary(call);
  var callFirstWith = flip(callFirst);
  
  var callLast = binary(callRight);
  var callLastWith = flip(callLast);

  // ### Partial applications that bind

  // A partially applied binding function
  //
  // roughly equivalent to applyRight
  //
  // var fn = function (...) { ... }
  //
  // bound(fn)(x)
  //   //=> fn.bind(x)
  //
  // bound(fn, foo)(x)
  //   //=> fn.bind(x, foo)
  //
  // bound(fn, foo, bar)(x)
  //   //=> fn.bind(x, foo, bar)
  var bound = variadic( function (messageName, args) {
    if (args === []) {
      return function (instance) {
        return instance[messageName].bind(instance)
      }
    }
    else {
      return function (instance) {
        return Function.prototype.bind.apply(
          instance[messageName], [instance].concat(args)
        )
      }
    }
  });
  
  var defaults = variadic( function (fn, values) {
    var fln = fn.length,
        vln = values.length;
    return variadic( function (args) {
      var aln = args.length,
          mln = Math.max(fln - aln, 0);
      args = args.concat(values.slice(vln-mln));
      return fn.apply(this, args);
    })
  });
  
  // collects arguments
  function args (arity) {
    if (arity === 1) {
      return function (a) {
        return [a];
      };
    }
    else if (arity === 2) {
      return function (a, b) {
        return [a, b];
      };
    }
    else if (arity === 3) {
      return function (a, b, c) {
        return [a, b, c];
      };
    }
    else return variadic( function (args) {
      return args;
    });
  };
  
  extend(allong.es, {
    applyNow: applyNow,
    apply: apply,
    callNow: callNow,
    call: call,
    callLeft: call,
    callRight: callRight,
    applyNowFlipped: applyNowFlipped,
    applyLeftNow: applyLeftNow,
    callLeftNow: callLeftNow,
    applyLeftNowWith: applyLeftNowWith,
    applyRightNow: applyRightNow,
    callRightNow: callRightNow,
    applyRightNowWith: applyRightNowWith,
    callFirst: callFirst,
    callLast: callLast,
    callFirstWith: callFirstWith,
    callLastWith: callLastWith,
    bound: bound,
    defaults: defaults,
    args: args,
    curry: curry
  });
  
  // # FOLDING
  
  var filter = binary( function filter (list, fn) {
    fn = functionalize(fn);
    
    return __filter.call(list, fn);
  });
  
  var filterWith = flip(filter);
  
  var map = binary( function map (list, fn) {
    fn = functionalize(fn);
    var fnLength = fn.length;
  
    if (fnLength !== 1) {
      fn = unary(fn);
    }
    return __map.call(list, fn);
  });
  
  var mapWith = flip(map);

  // turns any function into a recursive mapper
  //
  // deepMap(function (x) { return x * x })([1, [2, 3], 4])
  //   //=> [1, [4, 9], 16]
  function deepMap (fn) {
    return function innerDeepMap (tree) {
      return __map.call(tree, function (element) {
        if (Array.isArray(element)) {
          return innerSoak(element);
        }
        else return fn(element);
      });
    };
  };
  
  var deepMap = binary( function (tree, fn) {
    fn = functionalize(fn);
    
    return __map.call(tree, function (element) {
      if (Array.isArray(element)) {
        return deepMap(element, fn);
      }
      else return fn(element);
    });
  });
  
  var deepMapWith = flip(deepMap);
  
  extend(allong.es, {
    map: map,
    mapWith: mapWith,
    filter: filter,
    filterWith: filterWith,
    deepMap: deepMap,
    deepMapWith: deepMapWith
  });
  
  // # DECORATORS

  function maybe (fn) {
    fn = functionalize(fn);
    return function () {
      var i;

      if (arguments.length === 0) {
        return
      }
      else {
        for (i = 0; i < arguments.length; ++i) {
          if (arguments[i] == null) return
        }
        return fn.apply(this, arguments)
      }
    }
  }

  function tap (value, fn) {
    fn = functionalize(fn);

    if (fn === void 0) {
      return curried
    }
    else return curried(fn);

    function curried (fn) {
      if (typeof(fn) === 'function') {
        fn(value)
      }
      return value
    }
  }

  function fluent (fn) {
    fn = functionalize(fn);
    return function () {
      fn.apply(this, arguments);
      return this
    }
  }

  // decorates a function to return its first argument
  var returnFirst = function (fn) {
    fn = functionalize(fn);
    return function () {
      fn.apply(this, arguments);
      return arguments[0];
    }
  }

  function tee (decoration) {
    decoration = functionalize(decoration);
    return function (fn) {
      fn = functionalize(fn);
      return compose(returnFirst(decoration), fn);
    };
  };

  function once (fn) {
    fn = functionalize(fn);
    var done = false,
        testAndSet;

    if (!!fn.name) {
      testAndSet = function () {
        this["__once__"] || (this["__once__"] = {})
        if (this["__once__"][fn.name]) return true;
        this["__once__"][fn.name] = true;
        return false
      }
    }
    else  {
      testAndSet = function (fn) {
        if (done) return true;
        done = true;
        return false
      }
    }

    return function () {
      return testAndSet.call(this) ? void 0 : fn.apply(this, arguments)
    }
  }

  function memoized (fn, keymaker) {
    fn = functionalize(fn);
    var lookupTable = {},
        key,
        value;

    keymaker || (keymaker = function (args) {
      return JSON.stringify(args)
    });

    return function () {
      var key = keymaker.call(this, arguments);

      return lookupTable[key] || (
        lookupTable[key] = fn.apply(this, arguments)
      )
    }
  };
  
  extend(allong.es, {
    maybe: maybe,
    tap: tap,
    fluent: fluent,
    returnFirst: returnFirst,
    tee: tee,
    once: once,
    memoized: memoized
  });
  
  // # MIXIN

  function mixin (decoration) {
    return function decorator () {
      if (arguments[0] !== void 0) {
        return decorator.call(arguments[0]);
      }
      else {
        extend(this, decoration);
        return this;
      }
    };
  };

  function classDecorator (decoration) {
    return function (clazz) {
      function Decorated  () {
        var self = this instanceof Decorated
                   ? this
                   : new Decorated();

        return clazz.apply(self, arguments);
      };
      Decorated.prototype = extend(new clazz(), decoration);
      return Decorated;
    };
  };
  
  extend(allong.es, {
    mixin: mixin,
    classDecorator: classDecorator
  });
  
  // # MORE

  var unbind = function unbind (fn) {
    fn = functionalize(fn);
    return fn.unbound ? unbind(fn.unbound()) : fn
  };

  function bind (fn, context, force) {
    fn = functionalize(fn);
    var unbound, bound;

    if (force) {
      fn = unbind(fn)
    }
    bound = function () {
      return fn.apply(context, arguments)
    };
    bound.unbound = function () {
      return fn;
    };

    return bound;
  }

  function invoke (fn) {
    fn = functionalize(fn);
    var args = __slice.call(arguments, 1);

    return function (instance) {
      return fn.apply(instance, args)
    }
  };

  function get (object, getName) {
    if (getName == null) {
      return function (getName) { return object[getName]; };
    }
    else return object[getName];
  };

  function getWith (getName, object) {
    if (object == null) {
      return function (object) { return object[getName]; };
    }
    else return object[getName];
  };

  var pluckWith = compose(mapWith, getWith),
      pluck = flip(pluckWith);

  // Send a message/invoke a method on the receiver.
  // TODO: Think about what it has in common with callLeft
  var send = variadic( function (methodName, args) {
    return variadic( function (receiver, remainingArgs) {
      var fn = receiver[methodName];
      return fn.apply(receiver, args.concat(remainingArgs))
    })
  });
  
  extend(allong.es, {
    bind: bind,
    unbind: unbind,
    invoke: invoke,
    get: get,
    getWith: getWith,
    send: send,
    pluckWith: pluckWith,
    pluck: pluck
  });
  
  // # TRAMPOLINE
      
  function Thunk (closure) {
    if (!(this instanceof Thunk))
      return new Thunk(closure);
    
    this.closure = closure;
  };
  
  Thunk.prototype.force = function () {
    return this.closure();
  };
      
  function trampoline (fn) {
    var trampolined = variadic( function (args) {
      var result = fn.apply(this, args);
      
      while (result instanceof Thunk) {
        result = result.force();
      }
      
      return result;
    });
    trampolined.__trampolined_fn = fn;
    return trampolined;
  };
  
  var tailCall = variadic( function (fn, args) {
    var context = this;
    if (fn.__trampolined_fn instanceof Function) {
      return new Thunk( function () { 
        return fn.__trampolined_fn.apply(context, args);
      });
    }
    else return new Thunk( function () { 
      return fn.apply(context, args);
    });
  });
  
  extend(allong.es, {
    trampoline: trampoline,
    tailCall: tailCall,
    Thunk: Thunk
  });
  
  // # ITERATORS
  
  (function (allong) {
      
    function fold (iter, binaryFn, seed) {
      var state, element;
      binaryFn = functionalize(binaryFn);
      if (seed !== void 0) {
        state = seed;
      }
      else {
        state = iter();
      }
      element = iter();
      while (element != null) {
        state = binaryFn.call(element, state, element);
        element = iter();
      }
      return state;
    };
  
    var HASNTBEENRUN = {};
  
    function unfold (seed, unaryFn) {
      var state = HASNTBEENRUN;
      unaryFn = functionalize(unaryFn);
      return function () {
        if (state === HASNTBEENRUN) {
          return (state = seed);
        }
        else if (state != null) {
          return (state = unaryFn.call(state, state));
        }
        else return state;
      };
    };
  
    // note that the unfoldWithReturn behaves differently than
    // unfold with respect to the first value returned
    function unfoldWithReturn (seed, unaryFn) {
      var state = seed,
          pair,
          value;
      unaryFn = functionalize(unaryFn);
      return function () {
        if (state != null) {
          pair = unaryFn.call(state, state);
          value = pair[1];
          state = value != null
                  ? pair[0]
                  : void 0
          return value;
        }
        else return void 0;
      };
    };

    function accumulate (iter, binaryFn, initial) {
      var state = initial;
      binaryFn = functionalize(binaryFn);
      return function () {
        element = iter();
        if (element == null) {
          return element;
        }
        else {
          if (state === void 0) {
            return (state = element);
          }
          else return (state = binaryFn.call(element, state, element));
        }
      }
    };
  
    function accumulateWithReturn (iter, binaryFn, initial) {
      var state = initial,
          stateAndReturnValue;
      binaryFn = functionalize(binaryFn);
      return function () {
        element = iter();
        if (element == null) {
          return element;
        }
        else {
          if (state === void 0) {
            return (state = element);
          }
          else {
            stateAndReturnValue = binaryFn.call(element, state, element);
            state = stateAndReturnValue[0];
            return stateAndReturnValue[1];
          }
        }
      }
    };
  
    function map (iter, unaryFn) {
      unaryFn = functionalize(unaryFn);
      return function() {
        var element;
        element = iter();
        if (element != null) {
          return unaryFn.call(element, element);
        } else {
          return void 0;
        }
      };
    };

    function select (iter, unaryPredicateFn) {
      unaryPredicateFn = functionalize(unaryPredicateFn);
      return function() {
        var element;
        element = iter();
        while (element != null) {
          if (unaryPredicateFn.call(element, element)) {
            return element;
          }
          element = iter();
        }
        return void 0;
      };
    };
  
    function reject (iter, unaryPredicateFn) {
      unaryPredicateFn = functionalize(unaryPredicateFn);
      return select(iter, function (something) {
        return !unaryPredicateFn(something);
      });
    };
  
    function find (iter, unaryPredicateFn) {
      unaryPredicateFn = functionalize(unaryPredicateFn);
      return select(iter, unaryPredicateFn)();
    }

    function slice (iter, numberToDrop, numberToTake) {
      var count = 0;
      while (numberToDrop-- > 0) {
        iter();
      }
      if (numberToTake != null) {
        return function() {
          if (++count <= numberToTake) {
            return iter();
          } else {
            return void 0;
          }
        };
      }
      else return iter;
    };
  
    var drop = defaults(binary(slice), 1);
  
    function take (iter, numberToTake) {
      return slice(iter, 0, numberToTake == null ? 1 : numberToTake);
    }

    function FlatArrayIterator (array) {
      var index = 0;
      return function() {
        return array[index++];
      };
    };
  
    function RecursiveArrayIterator (array) {
      var index, myself, state;
      index = 0;
      state = [];
      myself = function() {
        var element, tempState;
        element = array[index++];
        if (element instanceof Array) {
          state.push({
            array: array,
            index: index
          });
          array = element;
          index = 0;
          return myself();
        } else if (element === void 0) {
          if (state.length > 0) {
            tempState = state.pop(), array = tempState.array, index = tempState.index;
            return myself();
          } else {
            return void 0;
          }
        } else {
          return element;
        }
      };
      return myself;
    };
  
    function K (value) {
      return function () {
        return value;
      };
    };

    function upRange (from, to, by) {
      return function () {
        var was;
      
        if (from > to) {
          return void 0;
        }
        else {
          was = from;
          from = from + by;
          return was;
        }
      }
    };

    function downRange (from, to, by) {
      return function () {
        var was;
      
        if (from < to) {
          return void 0;
        }
        else {
          was = from;
          from = from - by;
          return was;
        }
      }
    };
  
    function range (from, to, by) {
      if (from == null) {
        return upRange(1, Infinity, 1);
      }
      else if (to == null) {
        return upRange(from, Infinity, 1);
      }
      else if (by == null) {
        if (from <= to) {
          return upRange(from, to, 1);
        }
        else return downRange(from, to, 1)
      }
      else if (by > 0) {
        return upRange(from, to, by);
      }
      else if (by < 0) {
        return downRange(from, to, Math.abs(by))
      }
      else return k(from);
    };
  
    var numbers = unary(range);

    extend(allong.es, { iterators: {
      accumulate: accumulate,
      accumulateWithReturn: accumulateWithReturn,
      fold: fold,
      unfold: unfold,
      unfoldWithReturn: unfoldWithReturn,
      map: map,
      select: select,
      reject: reject,
      filter: select,
      find: find,
      slice: slice,
      drop: drop,
      take: take,
      FlatArrayIterator: FlatArrayIterator,
      RecursiveArrayIterator: RecursiveArrayIterator,
      constant: K,
      K: K,
      numbers: numbers,
      range: range
    }});
  
  })(allong);
  
  // Monadic Sequencing
  // ------------------
  
  //    sequence(a, b, c)
  //      //=> function (x) {
  //        return c(b(a(x)))
  //      }
  var sequence = callFlipped(compose);

  var Promise = require('promise');

  var Supervisor = (function() {

    function Supervisor(methods) {
      var body, name;
      for (name in methods) {
        if (!__hasProp.call(methods, name)) continue;
        body = methods[name];
        this[name] = body;
      }
      this.of || (this.of = function(value) {
        return value;
      });
      this.map || (this.map = function(fn) {
        return fn;
      });
      this.chain || (this.chain = function(mValue, fn) {
        return this.map(fn)(mValue);
      });
      for (name in this) {
        if (!__hasProp.call(this, name)) continue;
        body = this[name];
        this[name] = body.bind(this);
      }
    }

    return Supervisor;

  })();

  Supervisor.Identity = new Supervisor();

  Supervisor.Maybe = new Supervisor({
    map: function(fn) {
      return function(mValue) {
        if (mValue === null || mValue === void 0) {
          return mValue;
        } else {
          return fn(mValue);
        }
      };
    }
  });

  Supervisor.Writer = new Supervisor({
    of: function(value) {
      return [value, ''];
    },
    map: function(fn) {
      return function(_arg) {
        var newlyWritten, result, value, writtenSoFar, _ref;
        value = _arg[0], writtenSoFar = _arg[1];
        _ref = fn(value), result = _ref[0], newlyWritten = _ref[1];
        return [result, writtenSoFar + newlyWritten];
      };
    }
  });

  Supervisor.List = new Supervisor({
    of: function(value) {
      return [value];
    },
    join: function(mValue) {
      return mValue.reduce(this.concat, this.zero());
    },
    map: function(fn) {
      return function(mValue) {
        return mValue.map(fn);
      };
    },
    zero: function() {
      return [];
    },
    concat: function(ma, mb) {
      return ma.concat(mb);
    },
    chain: function(mValue, fn) {
      return this.join(this.map(fn)(mValue));
    }
  });

  Supervisor.Promise = new Supervisor({
    of: function(value) {
      return new Promise(function(resolve, reject) {
        return resolve(value);
      });
    },
    map: function(fnReturningAPromise) {
      return function(promiseIn) {
        return new Promise(function(resolvePromiseOut, rejectPromiseOut) {
          return promiseIn.then((function(value) {
            return fnReturningAPromise(value).then(resolvePromiseOut, rejectPromiseOut);
          }), rejectPromiseOut);
        });
      };
    }
  });

  Supervisor.Callback = new Supervisor({
    of: function(value) {
      return function(callback) {
        return callback(value);
      };
    },
    map: function(fn) {
      return function(value) {
        return function(callback) {
          return fn(value, callback);
        };
      };
    },
    chain: function(mValue, fn) {
      var _this = this;
      return function(callback) {
        return mValue(function(value) {
          return _this.map(fn)(value)(callback);
        });
      };
    }
  });

  Supervisor.sequence = function() {
    var args, fns, supervisor;
    args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
    if (args[0] instanceof Supervisor) {
      supervisor = args[0], fns = 2 <= args.length ? __slice.call(args, 1) : [];
    } else {
      supervisor = Supervisor.Identity;
      fns = args;
    }
    return function() {
      return fns.reduce(supervisor.chain, supervisor.of.apply(supervisor, arguments));
    };
  };

  extend(allong.es, {
    sequence: sequence,
    Supervisor: Supervisor
  });

  // Exports and sundries
  // --------------------

  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = allong;
    }
    exports.allong = allong;
  } else {
    root.allong = allong;
  }
  
}).call(this);

================================================
FILE: _site/package.json
================================================
{
  "author": "Reg Braithwaite <raganwald@gmail.com> (http://braythwayt.com)",
  "name": "allong.es",
  "description": "Combinators and Function Decorators",
  "version": "0.10.2",
  "homepage": "http://allong.es",
  "repository": {
    "type": "git",
    "url": "git://github.com/raganwald/allong.es.git"
  },
  "main": "lib/allong.es.js",
  "scripts": {
    "test": "jasmine-node --coffee --verbose spec"
  },
  "engines": {
    "node": ""
  },
  "dependencies": {
    "promise": ""
  },
  "devDependencies": {
    "jasmine-node": "",
    "coffee-script": "",
    "grunt": "~0.4.1",
    "grunt-contrib-uglify": "~0.2.0"
  }
}


================================================
FILE: _site/release_notes.md
================================================
# Release Notes

1. Currying is implicit
2. "With" and "This" naming conventions, e.g. `pluckWith` is the should of `pluck`
3. `splat` is now `mapWith`
4. `applyLeft` and `applyFirst` are now `callLeft` and `callFirst`
5. `curry` now falls out of `apply`/`applyLeft`!
6. soak->deepMap

================================================
FILE: _site/spec/apply.spec.coffee
================================================
{ callRight, applyNow, callNow, applyNowFlipped, 
  call, applyLeftNow, callLeftNow, args, applyLeftNowWith,
  applyRightNow, callRightNow, applyRightNowWith,
  callFirst, callFirstWith, callLast, callLastWith, apply
} = require('../lib/allong.es.js').allong.es

echo = (a, b, c) -> "#{a} #{b} #{c}"

five = (a, b, c, d, e) -> [a, b, c, d, e]
three = (a, b, c) -> [a, b, c]
twelve = (a, b, c, d, e, f, g, h, i, j, k, l) ->
vari = (args...) -> args
one = (x) -> x

describe "apply", ->
  
  it "should apply an array of arguments to a function", ->
    expect( apply(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it "should be curried", ->
    expect( apply(three)([1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it "should  be self-currying, it should apply what it gets", ->
    expect( apply(three, [1, 2])(3) ).toEqual three(1, 2, 3)

describe "applyNow", ->
  
  it "should apply an array of arguments to a function", ->
    expect( applyNow(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it "should not be self-currying, it should apply what it gets", ->
    expect( applyNow(three, [1, 2]) ).toEqual three(1, 2)
    expect( applyNow(three, [1]) ).toEqual three(1)
    
  it "should be curried", ->
    expect( applyNow(three)([1, 2, 3]) ).toEqual three(1, 2, 3)
    
  describe "when flipped", ->
    
    it "should apply an array of arguments to a function", ->
      expect( applyNowFlipped([1, 2, 3], three) ).toEqual three(1, 2, 3)

    it "should be curried", ->
      expect( applyNowFlipped([1, 2, 3])(three) ).toEqual three(1, 2, 3)

describe "callNow", ->
  
  it "should apply arguments to a function", ->
    expect( callNow(three, 1, 2, 3) ).toEqual three(1, 2, 3)
    
  it "should not be self-currying, it should apply what it gets", ->
    expect( callNow(three, 1, 2) ).toEqual three(1, 2)
    expect( callNow(three, 1) ).toEqual three(1)
    
  it "should not be curried", ->
    expect( callNow(three) ).toEqual three()
    
  # variadic functions do not have a 'this' predefined at this point.
    
describe "applyLeftNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( applyLeftNow(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( applyLeftNow(three, [1, 2]) ).not.toEqual three(1, 2)
    expect( applyLeftNow(three, [1, 2])(3) ).toEqual three(1, 2, 3)
    
  it 'should not be fully curried', ->
    expect( applyLeftNow(three, [1])(2) ).toEqual three(1, 2)
    
  describe "when flipped", ->
  
    it 'should apply all the arguments if possible', ->
      expect( applyLeftNowWith([1, 2, 3], three) ).toEqual three(1, 2, 3)
    
    it 'should not be full application', ->
      expect( applyLeftNowWith([1, 2], three) ).not.toEqual three(1, 2)
      expect( applyLeftNowWith([1, 2], three)(3) ).toEqual three(1, 2, 3)
    
    it 'should not be fully curried', ->
      expect( applyLeftNowWith([1], three)(2) ).toEqual three(1, 2)
    
describe "callLeftNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( callLeftNow(three, 1, 2, 3) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( callLeftNow(three, 1, 2) ).not.toEqual three(1, 2)
    expect( callLeftNow(three, 1, 2)(3) ).toEqual three(1, 2, 3)
    
  it 'should not be fully curried', ->
    expect( callLeftNow(three, 1)(2) ).toEqual three(1, 2)

describe "applyRightNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( applyRightNow(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( applyRightNow(three, [1, 2]) ).not.toEqual three(1, 2)
    expect( applyRightNow(three, [1, 2])(3) ).toEqual three(3, 1, 2)
    
  it 'should not be fully curried', ->
    expect( applyRightNow(three, [1])(2) ).toEqual three(2, 1)
    
  describe "when flipped", ->
  
    it 'should apply all the arguments if possible', ->
      expect( applyRightNowWith([1, 2, 3], three) ).toEqual three(1, 2, 3)
    
    it 'should not be full application', ->
      expect( applyRightNowWith([1, 2], three) ).not.toEqual three(1, 2)
      expect( applyRightNowWith([1, 2], three)(3) ).toEqual three(3, 1, 2)
    
    it 'should not be fully curried', ->
      expect( applyRightNowWith([1], three)(2) ).toEqual three(2, 1)
    
describe "callRightNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( callRightNow(three, 1, 2, 3) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( callRightNow(three, 1, 2) ).not.toEqual three(1, 2)
    expect( callRightNow(three, 1, 2)(3) ).toEqual three(3, 1, 2)
    
  it 'should not be fully curried', ->
    expect( callRightNow(three, 1)(2) ).toEqual three(2, 1)
    
################################

describe "call", ->
  
  it "should call an array of arguments to a function", ->
    expect( call(echo, 1, 2, 3) ).toEqual "1 2 3"
  
  it "should have a curried nature", ->
    expect( call(five)(1, 2, 3, 4, 5) ).toEqual [1..5]
    expect( call(five)(1, 2, 3)(4, 5) ).toEqual [1..5]
    expect( call(five, 1, 2, 3)(4, 5) ).toEqual [1..5]
    expect( call(five, 1, 2, 3)(4)(5) ).toEqual [1..5]
    
  it "should get the arity right for small amounts", ->
    expect( call(five, 1, 2).length ).toEqual 3

describe "callRight", ->
  
  it "should call an array of arguments to a function", ->
    expect( callRight(echo, 1, 2, 3) ).toEqual "1 2 3"
  
  it "should have a curried nature", ->
    expect( callRight(five)(1, 2, 3, 4, 5) ).toEqual [1..5]
    expect( callRight(five)(1, 2, 3)(4, 5) ).toEqual [1..5]
    
  it "should apply given arguments to the right", ->
    expect( callRight(five, 1)(2, 3, 4, 5) ).toEqual [2, 3, 4, 5, 1]
    expect( callRight(five, 1, 2)(3, 4, 5) ).toEqual [3, 4, 5, 1, 2]
    expect( callRight(five, 1, 2, 3)(4, 5) ).toEqual [4, 5, 1, 2, 3]
    expect( callRight(five, 1, 2, 3, 4)(5) ).toEqual [5, 1, 2, 3, 4]
    expect( callRight(five, 1, 2, 3)(4, 5) ).toEqual [4, 5, 1, 2, 3]
    expect( callRight(five, 1, 2, 3)(4)(5) ).toEqual [4, 5, 1, 2, 3]
    
  it "should get the arity right for small amounts", ->
    expect( callRight(five, 1, 2).length ).toEqual 3
    
describe 'callFirst', ->
  
  it 'should call with the first argument', ->
    expect( callFirst(three, 1)(2, 3) ).toEqual [1..3]
    
  it 'should get the arity right', ->
    expect( callFirst.length ).toEqual 2
    expect( callFirst(three, 1).length ).toEqual 2
    
  it 'should be curried', ->
    expect( callFirst(three)(1)(2, 3) ).toEqual [1..3]
    
describe 'callFirstWith', ->
  
  it 'should call with the first argument', ->
    expect( callFirstWith(1, three)(2, 3) ).toEqual [1..3]
    
  it 'should get the arity right', ->
    expect( callFirstWith.length ).toEqual 2
    expect( callFirstWith(1, three).length ).toEqual 2
    
  it 'should be curried', ->
    expect( callFirstWith(1)(three)(2, 3) ).toEqual [1..3]
    
describe "args", ->
  
  it "should collect arguments into an array", ->
    expect( args(3)(1, 2, 3) ).toEqual [1, 2, 3]

================================================
FILE: _site/spec/arity.spec.coffee
================================================
{unvariadic, variadic, curry} = require('../lib/allong.es.js').allong.es

numberOfArgs = -> arguments.length
threeArguments = (a, b, c) -> arguments.length

describe "unvariadic", ->
  
  it "should clip arguments", ->
    expect( unvariadic(3, numberOfArgs)(1, 2, 3, 4, 5) ).toEqual threeArguments(1, 2, 3, 4, 5)
    
  it "shouldn't pad arguments", ->
    expect( unvariadic(3, numberOfArgs)(1) ).toEqual threeArguments(1)
    
describe "variadic", ->
  
  describe "with an arity", ->
    
    one = (args) -> [args]
    
    two = (arg, args) -> [arg, args]
    
    onev = (args...) -> [args]
    
    twov = (arg, args...) -> [arg, args]
    
    it 'should 1', ->
      expect( variadic(1, one)(1, 2, 3) ).toEqual onev(1, 2, 3)
    
    it 'should 2', ->
      expect( variadic(1, one).length ).toEqual 1
    
    it 'should 3', ->
      expect( variadic(2, one).length ).toEqual 2

describe "curry", ->
  
  three = (a, b, c) -> [a, b, c]
  
  it "should be a function that returns a function", ->
    expect( curry instanceof Function).toEqual true
    expect( curry(three) instanceof Function).toEqual true
  
  it "should allow a full invocation", ->
    expect( curry(three)(1, 2, 3) ).toEqual [1, 2, 3]
  
  it "should allow partial invocation", ->
    expect( curry(three)(1)(2, 3) ).toEqual [1, 2, 3]
    expect( curry(three)()(1, 2, 3) ).toEqual [1, 2, 3]
    expect( curry(three)(1)(2)(3) ).toEqual [1, 2, 3]
    expect( curry(three)(1, 2)(3) ).toEqual [1, 2, 3]
    
    # describe "self-currying", ->
    #   
    #   it "shouldn't affect normal useage", ->
    #     expect(selfCurrying(three)(4, 5, 6)).toEqual three(4, 5, 6)
    #     expect(selfCurrying(three)(4, 5)).toEqual three(4, 5)
    #     expect(selfCurrying(three)(4)).toEqual three(4)
    #   
    #   it "should curry with no arguments", ->
    # expect( selfCurrying(three)()(1)(2, 3) ).toEqual [1, 2, 3]
    # expect( selfCurrying(three)()()(1, 2, 3) ).toEqual [1, 2, 3]
    # expect( selfCurrying(three)()(1)(2)(3) ).toEqual [1, 2, 3]
    # expect( selfCurrying(three)()(1, 2)(3) ).toEqual [1, 2, 3]


================================================
FILE: _site/spec/basic.spec.coffee
================================================
{Sequence, Sequence: {sequence}} = require('../lib/allong.es.js').allong.es

double = (n) -> n + n
plusOne = (n) -> n + 1
  
describe "sequence", ->

  it "should be a thing", ->
    expect( sequence ).not.toBeNull()
    
  it "should return a function when given a function", ->
    expect( sequence(double) ).not.toBeNull()
  
  it "should sequence a single function", ->
    expect( sequence(double)(3) ).toEqual 6
  
  it "should sequence two functions", ->
    expect( sequence(double, plusOne)(3) ).toEqual 7
    
describe "Identity", ->
  
  it "should sequence a single function", ->
    expect( sequence(Sequence.Identity, double)(3) ).toEqual 6
  
  it "should sequence two functions", ->
    expect( sequence(Sequence.Identity, double, plusOne)(3) ).toEqual 7
    
describe "Maybe", ->
  
  it "should pass numbers through", ->
    expect( sequence(Sequence.Maybe, double, plusOne)(3) ).toEqual 7
  
  it "should pass null through", ->
    expect( sequence(Sequence.Maybe, double, plusOne)(null) ).toBeNull()
  
  it "should pass undefined through", ->
    expect( sequence(Sequence.Maybe, double, plusOne)(undefined) ).toBeUndefined()
    
  it "should short-circuit", ->
    expect( sequence(Sequence.Maybe, double, ((x) ->), plusOne)(undefined) ).toBeUndefined()
      
describe "Writer", ->
  
  parity = (n) ->
    [
      n
      if n % 2 is 0 then 'even' else 'odd'
    ]
    
  space = (n) ->
    [
      n
      ' '
    ]
    
  size = (n) ->
    [
      n
      if n < 10 then 'small' else 'normal'
    ]
  
  it "should accumulate writes", ->
    expect( sequence(Sequence.Writer, parity, space, size)(5) ).toEqual [5, 'odd small']
    
describe 'List', ->
  
  oneToN = (n) ->
    [1..n]
  
  nToOne = (n) ->
    [n..1]
    
  it "should handle two levels of lists", ->
    expect( sequence(Sequence.List, oneToN, nToOne)(3) ).toEqual [1, 2, 1, 3, 2, 1]
      

================================================
FILE: _site/spec/callbacks.spec.coffee
================================================
{Sequence, Sequence: {do}} = require('../lib/allong.es.js').allong.es

double = (v, c) -> c(v * 2)
plus1 = (v, c) -> c(v + 1)
identity = (v) -> v

describe "continuation", ->
  
  it "should work for the null do", ->
    
    expect( do(Sequence.Callback)(42)(identity) ).toBe 42
  
  it "should work for a double", ->
    
    expect( do(Sequence.Callback, double)(42)(identity) ).toBe 84
  
  it "should work for a double double", ->
    
    expect( do(Sequence.Callback, double, double)(2)(identity) ).toBe 8
  
  it "should work for a double plus1 double", ->
    
    expect( do(Sequence.Callback, double, plus1, double)(2)(identity) ).toBe 10

================================================
FILE: _site/spec/core.spec.coffee
================================================
# unary, binary, ternary, variadic, compose, sequence???
{defaults, mapWith, getWith, filterWith, compose, sequence, variadic, flip, curry} = require('../lib/allong.es.js').allong.es

echo = (a, b, c) -> "#{a} #{b} #{c}"
parenthesize = (a) -> "(#{a})"
square = (n) -> n * n
oddP = (n) -> !!(n % 2)

describe "defaults", ->
  
  it "should default values", ->
    expect( defaults(echo, 'c')('a', 'b') ).toEqual 'a b c'
    expect( defaults(echo, 'b', 'c')('a') ).toEqual 'a b c'
    expect( defaults(echo, 'a', 'b', 'c')() ).toEqual 'a b c'
  
  it "should ignore uneccesary defaults", ->
    expect( defaults(echo, 'a', 'b', 'c')('A') ).toEqual 'A b c'
    expect( defaults(echo, 'a', 'b', 'c')('A', 'B') ).toEqual 'A B c'
    expect( defaults(echo, 'a', 'b', 'c')('A', 'B', 'C') ).toEqual 'A B C'
    expect( defaults(echo, 'a', 'b', 'c')('A', 'B', 'C', 'D') ).toEqual 'A B C'
    
describe "mapWith", ->
  
  it "should map backwards", ->
    expect( mapWith(square, [1..5]) ).toEqual [1, 4, 9, 16, 25]
  
  it "should map backwards, curried", ->
    expect( mapWith(square)([1..5]) ).toEqual [1, 4, 9, 16, 25]
    
describe "filterWith", ->
  
  it "should filter backwards", ->
    expect( filterWith(oddP, [1..5]) ).toEqual [1, 3, 5]
  
  it "should filter backwards, curried", ->
    expect( filterWith(oddP)([1..5]) ).toEqual [1, 3, 5]

describe "compose", ->
  
  it "should compose two functions", ->
    expect( compose(parenthesize, parenthesize)('hello') ).toEqual '((hello))'
  
  it "should respect the arity of the first function", ->
    expect( compose(parenthesize, parenthesize).length ).toEqual 1
    expect( compose(echo, parenthesize).length ).toEqual 3
    
  it "should handle a common use case, pluckWith", ->
    myPluckWith = compose mapWith, getWith
    expect( myPluckWith('name')([{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']
    expect( myPluckWith('name', [{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']

describe "sequence", ->
  
  it "should sequence two functions", ->
    expect( sequence(parenthesize, parenthesize)('hello') ).toEqual '((hello))'
  
  it "should respect the arity of the first function", ->
    expect( sequence(parenthesize, parenthesize).length ).toEqual 1
    expect( sequence(parenthesize, echo).length ).toEqual 3
    
  it "should handle a common use case, pluckWith", ->
    myPluckWith = sequence getWith, mapWith
    expect( myPluckWith('name')([{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']
    expect( myPluckWith('name', [{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']
  
describe "flip", ->
  
  a = (a) -> [a]
  b = (a, b) -> [a, b]
  c = (a, b, c) -> [a, b, c]
  d = (a, b, c, d) -> [a, b, c, d]
  v = variadic( (x) -> x )
  
  it "should flip a unary function", ->
    expect( flip(a)(1) ).toEqual [1]
    
  it "should flip a binary function", ->
    expect( flip(b)(1, 2) ).toEqual [2, 1]
    
  it "should flip a ternary function", ->
    expect( flip(c)(1, 2, 3) ).toEqual [3, 2, 1]
    
  it "should flip a quaternary function", ->
    expect( flip(d)(1, 2, 3, 4) ).toEqual [4, 3, 2, 1]
    
  it "should flip a variadic function", ->
    expect( flip(v)(1, 2, 3, 4, 5) ).toEqual [5, 4, 3, 2, 1]
    
  it "should respect arities", ->
    expect( flip(v).length ).toEqual v.length
    expect( flip(a).length ).toEqual a.length
    expect( flip(b).length ).toEqual b.length
    expect( flip(c).length ).toEqual c.length
    
    # expect( flip(d).length ).toEqual d.length
    # NO; We do not guarantee arity for length > 3
    
  it 'should be self-currying', ->
    expect( flip(b)(1)(2) ).toEqual [2, 1]
    expect( flip(c)(1)(2)(3) ).toEqual [3, 2, 1]
    expect( flip(d)(1)(2)(3)(4) ).toEqual [4, 3, 2, 1]

================================================
FILE: _site/spec/decorating-classes.spec.coffee
================================================
{classDecorator, mixin, fluent} = require('../lib/allong.es.js').allong.es

class Todo
  constructor: (name) ->
    self = if this instanceof Todo
             this
           else
             new Todo()
    self.name = name or 'Untitled'
    self.done = false
  do: fluent -> this.done = true
  undo: fluent -> this.done = false
  
describe "features", ->
  it "should include mixin", ->
    expect(typeof mixin).toEqual 'function'
  it "should include classDecorator", ->
    expect(typeof classDecorator).toEqual 'function'

describe "classDecorator", ->

  AndColourCoded = classDecorator
    setColourRGB: (r, g, b) ->
      @colourCode = { r, g, b }
      this
    getColourRGB: -> @colourCode

  ColourTodo = AndColourCoded Todo

  todo = new ColourTodo('Use More Decorators')
        .setColourRGB(0, 255, 0)

  it "should set the name correctly", ->
    expect( todo.name ).toEqual "Use More Decorators"

  it "should set the colour code correctly", ->
    expect( todo.getColourRGB() ).toEqual
      r: 0
      g: 255
      b: 0

describe "mixin", ->

  LocationAware = mixin
    setLocation: (@location) -> this
    getLocation: -> @location
    
  describe "Using context", ->

    it "should add location to the existing class's prototype", ->
      LocationAware.call(Todo.prototype)
      expect( typeof Todo.prototype.setLocation ).toEqual 'function'
      expect( typeof Todo.prototype.getLocation ).toEqual 'function'
      
  describe "Using a parameter", ->
    
    a = { a: 'a' }
    b = { b: 'b' }
    c = { c: 'b' }
    
    it "should not add the location to the context", ->
      LocationAware.call(a, b)
      expect( a.setLocation ).toBeUndefined()
      expect( a.getLocation ).toBeUndefined()
      expect( typeof b.setLocation ).toEqual 'function'
      expect( typeof b.getLocation ).toEqual 'function'
      
    it "should add the location when called normally", ->
      LocationAware(c)
      expect( typeof c.setLocation ).toEqual 'function'
      expect( typeof c.getLocation ).toEqual 'function'
    

================================================
FILE: _site/spec/folding.spec.coffee
================================================
{mapWith, deepMapWith, filter} = require('../lib/allong.es.js').allong.es

square = (n) -> n * n

describe 'mapWith', ->
  
  it 'should square some numbers', ->
    
    expect( mapWith(square)([1..5]) ).toEqual [1, 4, 9, 16, 25]

describe 'deepMapWith', ->
  
  it 'should square some numbers', ->
    
    expect( deepMapWith(square)([1, [2..4], 5]) ).toEqual [1, [4, 9, 16], 25]
    
describe "filter", ->
  
  it "should find odd numbers", ->
    
    expect( filter([1, 2, 3, 4, 5, 6], '% 2 === 1') ).toEqual [1, 3, 5]
  
  it "should be self-currying", ->
    
    expect( filter([1, 2, 3, 4, 5, 6])('% 2 === 0') ).toEqual [2, 4, 6]

================================================
FILE: _site/spec/iterators.spec.coffee
================================================
{iterators: {slice, drop, take, accumulate, accumulateWithReturn, 
             fold, map, filter, FlatArrayIterator, RecursiveArrayIterator,
             unfold, unfoldWithReturn}} = require('../lib/allong.es.js').allong.es

describe "FlatArrayIterator", ->
  
  it "should iterate over a flat array", ->
    i = FlatArrayIterator([1, 2, 3, 4, 5])
    expect( i() ).toEqual(1)
    expect( i() ).toEqual(2)
    expect( i() ).toEqual(3)
    expect( i() ).toEqual(4)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()
    
  it "should not iterate down through an array", ->
    i = FlatArrayIterator([1, [2, 3, [4]], 5])
    expect( i() ).toEqual(1)
    expect( i() ).not.toEqual(2)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()
  
  it "should have no values given an empty array", ->
    i = FlatArrayIterator([])
    expect( i() ).toBeUndefined()
  
  it "should have a values given an empty tree", ->
    i = FlatArrayIterator([[], [[]]])
    expect( i() ).not.toBeUndefined()

describe "RecursiveArrayIterator", ->
  
  it "should have no values given an empty array", ->
    i = RecursiveArrayIterator([])
    expect( i() ).toBeUndefined()
  
  it "should have no values given an empty tree", ->
    i = RecursiveArrayIterator([[], [[]]])
    expect( i() ).toBeUndefined()
  
  it "should iterate over a flat array", ->
    i = RecursiveArrayIterator([1, 2, 3, 4, 5])
    expect( i() ).toEqual(1)
    expect( i() ).toEqual(2)
    expect( i() ).toEqual(3)
    expect( i() ).toEqual(4)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()
    
  it "should also iterate down through an array", ->
    i = RecursiveArrayIterator([1, [2, 3, [4]], 5])
    expect( i() ).toEqual(1)
    expect( i() ).toEqual(2)
    expect( i() ).toEqual(3)
    expect( i() ).toEqual(4)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()

sum = (x, y) -> x + y

describe "fold", ->
  
    describe "with a seed", ->
  
      it "should fold an iterator with many elements", ->
        expect( fold(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum, 0) ).toEqual(15)
  
      it "should fold an iterator with one element", ->
        expect( fold(RecursiveArrayIterator([[[4], []]]), sum, 42) ).toEqual(46)
  
      it "should fold an empty iterator", ->
        expect( fold(RecursiveArrayIterator([[], [[]]]), sum, 42) ).toEqual(42)
      
    describe "without a seed", ->
      
      it "should fold an array with two or more elements", ->
        expect( fold(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum) ).toEqual(15)
      
      it "should fold an array with one element", ->
        expect( fold(RecursiveArrayIterator([[[4], []]]), sum) ).toEqual(4)
      
      it "should fold an array with no elements", ->
        expect( fold(RecursiveArrayIterator([[[], []]]), sum) ).toBeUndefined()

describe "accumulate", ->
  
    describe "with a seed", ->
  
      it "should map an iterator with many elements", ->
        i = accumulate(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum, 0)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(3)
        expect( i() ).toEqual(6)
        expect( i() ).toEqual(10)
        expect( i() ).toEqual(15)
        expect( i() ).toBeUndefined()
  
      it "should map an iterator with one element", ->
        i = accumulate(RecursiveArrayIterator([[[4], []]]), sum, 42)
        expect( i() ).toEqual(46)
        expect( i() ).toBeUndefined()
  
      it "should map an empty iterator", ->
        i = accumulate(RecursiveArrayIterator([[[], []]]), sum, 42)
        expect( i() ).toBeUndefined()
      
    describe "without a seed", ->
  
      it "should map an iterator with many elements", ->
        i = accumulate(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(3)
        expect( i() ).toEqual(6)
        expect( i() ).toEqual(10)
        expect( i() ).toEqual(15)
        expect( i() ).toBeUndefined()
  
      it "should map an iterator with one element", ->
        i = accumulate(RecursiveArrayIterator([[[4], []]]), sum)
        expect( i() ).toEqual(4)
        expect( i() ).toBeUndefined()
  
      it "should map an empty iterator", ->
        i = accumulate(RecursiveArrayIterator([[[], []]]), sum)
        expect( i() ).toBeUndefined()

square = (x) -> x*x

describe "map", ->
  
      it "should map an iterator with many elements", ->
        i = map(RecursiveArrayIterator([1, [2, 3, [4]], 5]), square)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(4)
        expect( i() ).toEqual(9)
        expect( i() ).toEqual(16)
        expect( i() ).toEqual(25)
        expect( i() ).toBeUndefined()
  
      it "should map an iterator with one element", ->
        i = map(RecursiveArrayIterator([[[4], []]]), square)
        expect( i() ).toEqual(16)
        expect( i() ).toBeUndefined()
  
      it "should map an empty iterator", ->
        i = map(RecursiveArrayIterator([[[], []]]), square)
        expect( i() ).toBeUndefined()

odd = (x) -> x % 2 is 1

describe "filter", ->
  
      it "should filter an iterator with many elements", ->
        i = filter(RecursiveArrayIterator([1, [2, 3, [4]], 5]), odd)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(3)
        expect( i() ).toEqual(5)
        expect( i() ).toBeUndefined()
  
      it "should filter an iterator with one element", ->
        i = filter(RecursiveArrayIterator([[[4], []]]), odd)
        expect( i() ).toBeUndefined()
  
      it "should filter an empty iterator", ->
        i = filter(RecursiveArrayIterator([[[], []]]), odd)
        expect( i() ).toBeUndefined()
  
      it "should filter an iterator with no matches", ->
        i = filter(FlatArrayIterator([2, 4, 6, 8, 10]), odd)
        expect( i() ).toBeUndefined()

describe "slice", ->
  
  describe "with two parameter", ->
    
    it "should return an identity iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
    
    it "should return a trailing iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
      
    it "should return an empty iterator when out of range", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 5)
      expect( i() ).toBeUndefined()
  
  describe "with three parameters", ->
    
    it "should return an identity iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0, 5)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0, 99)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
    
    it "should return a leading iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0, 4)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toBeUndefined()
    
    it "should return a trailing iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 4)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 99)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
    
    it "should return an inner iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 3)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toBeUndefined()
      
    it "should return an empty iterator when given a zero length", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 0)
      expect( i() ).toBeUndefined()
      
    it "should return an empty iterator when out of range", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 5, 1)
      expect( i() ).toBeUndefined()
      
describe "drop", ->
  
  it "should drop the number of items dropped", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]), 2)
    expect( i() ).toEqual 3
    expect( i() ).toEqual 4
    expect( i() ).toEqual 5
    expect( i() ).toBeUndefined()
  
  it "should handle overdropping", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]), 99)
    expect( i() ).toBeUndefined()
    
  it "should handle underdropping", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]), 0)
    expect( i() ).toEqual 1
    expect( i() ).toEqual 2
    expect( i() ).toEqual 3
    expect( i() ).toEqual 4
    expect( i() ).toEqual 5
    expect( i() ).toBeUndefined()
    
  it "should default to one", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]))
    expect( i() ).toEqual 2
    expect( i() ).toEqual 3
    expect( i() ).toEqual 4
    expect( i() ).toEqual 5
    expect( i() ).toBeUndefined()
    
describe "accumulateWithReturn", ->
  
  it "should pass the state and result in a pair", ->
    i = accumulateWithReturn(FlatArrayIterator([1, 2, 3, 4, 5]), (state, element) ->
      [state + element, 'Total is ' + (state + element)]
    , 0);
    
    expect( i() ).toEqual 'Total is 1'
    expect( i() ).toEqual 'Total is 3'
    expect( i() ).toEqual 'Total is 6'
    expect( i() ).toEqual 'Total is 10'
    expect( i() ).toEqual 'Total is 15'
    
describe "unfold", ->
  
  it "should unfold and include the seed", ->
    i = unfold 0, (n) -> n + 1
    
    expect( i() ).toEqual 0
    expect( i() ).toEqual 1
    expect( i() ).toEqual 2
  
  it "should not unfold without a seed", ->
    i = unfold undefined, (n) -> n + 1
    
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined

describe "unfoldWithReturn", ->
  
  it "should unfold and throw off a value", ->
    i = unfoldWithReturn 1, (n) -> [n + 1, n*n]
    
    expect( i() ).toEqual 1
    expect( i() ).toEqual 4
    expect( i() ).toEqual 9
    expect( i() ).toEqual 16
  
  it "should halt if it returns undefined", ->
    i = unfoldWithReturn 1, (n) ->
      [n + 1, if n is 1 then undefined else n * n]
    
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
  
  it "should halt if the state becomes undefined", ->
    i = unfoldWithReturn 1, (n) ->
      [(if n is 3 then undefined else n + 1), (if n is undefined then 100 else n * n)]
    
    expect( i() ).toEqual 1
    expect( i() ).toEqual 4
    expect( i() ).toEqual 9
    expect( i() ).toEqual undefined

================================================
FILE: _site/spec/promises.spec.coffee
================================================
Promise = require 'promise'
{Sequence, Sequence: {do}} = require('../lib/allong.es.js').allong.es

describe "do", ->
    
  double = (value) ->
    new Promise (resolve, reject) ->
      resolve(value * 2)
      
  success = undefined
  failure = undefined
      
  beforeEach ->
      
    success = undefined
    failure = undefined
  
  describe "for a doubling promise", ->
  
    it "should work asynchronously", (done) ->
      
      dodPromise = do(Sequence.Promise, double)(3)
            
      dodPromise.then ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done())
            
    afterEach ->    
      expect( success ).toEqual 6
      expect( failure ).toBeUndefined()
  
  describe "for a double double", ->
  
    it "should work asynchronously", (done) ->
      
      dodPromise = do(Sequence.Promise, double, double)(2)
            
      dodPromise.then ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done())
            
    afterEach ->    
      expect( success ).toEqual 8
      expect( failure ).toBeUndefined()
  
  describe "for a double fail double", ->
  
    it "should fail forward", (done) ->
      
      failer = (value) ->
        new Promise (resolve, reject) ->
          reject 'sorry, old chap'
      
      dodPromise = do(Sequence.Promise, double, failer, double)(2)
            
      dodPromise.then( ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done()))
            
    afterEach ->    
      expect( success ).toBeUndefined()
      expect( failure ).toBe 'sorry, old chap'
      

================================================
FILE: _site/spec/trampoline.spec.coffee
================================================
{trampoline, tailCall} = require('../lib/allong.es.js').allong.es

describe "trampolining", ->

  depth = 32768

  it "should execute a clean even/odd to #{depth} levels", ->
  
    even = trampoline (n) ->
      if n is 0
        true
      else
        tailCall odd, n - 1
  
    odd = trampoline (n) ->
      if n is 0
        false
      else
        tailCall even, n - 1
  
    expect( even(0) ).toEqual true
    expect( even(1) ).toEqual false
    expect( even(2) ).toEqual true
    expect( even(3) ).toEqual false
    expect(-> even(depth) ).not.toThrow()
    
  it "should allow tail calling a co-recursive non-trampolined function too", ->
  
    even2 = trampoline (n) ->
      if n is 0
        true
      else
        tailCall odd2, n - 1
  
    odd2 = (n) ->
      if n is 0
        false
      else
        even2 n - 1
  
    expect(-> even2(1000) ).not.toThrow()
    expect( even2(100) ).toEqual true
    expect( even2(101) ).toEqual false

================================================
FILE: hello/world.txt
================================================
Hello!

================================================
FILE: lib/allong.es.js
================================================
/*! http://github.com/raganwald/allong.es (c) 2012-2013 Reg Braithwaite MIT Licensed */
(function (root) {
  
  // Setup
  // -----

  // Establish the root object, `window` in the browser, or `global` on the server.
  // *taken from [Underscore.js](http://underscorejs.org/)*

  var root = this;
  
  var __slice = Array.prototype.slice,
      __map = Array.prototype.map,
      __hasProp = Array.prototype.hasOwnProperty,
      __filter = Array.prototype.filter;
      
  
  // See: http://underscorejs.org/docs/underscore.html
  var nativeIsArray = Array.isArray,
      toString = Object.prototype.toString,
      isArray = nativeIsArray || function(obj) {
        return toString.call(obj) == '[object Array]';
      },
      isString = function (obj) { return toString.call(obj) == '[object String]'; },
      isFunction = function (obj) { return toString.call(obj) == '[object Function]'; };
      
  if (typeof (/./) !== 'function') {
    isFunction = function(obj) {
      return typeof obj === 'function';
    };
  }
  
  
  
  // here's our export object
  var allong = { es: {} };

  // ## Functionalizing
  //
  // The utility functions operate on other functions. They can also operate on string
  // abbreviations for functions by calling `functionalzie(...)` on their inputs.

  // SHIM
  if ('ab'.split(/a*/).length < 2) {
    if (typeof console !== "undefined" && console !== null) {
      console.log("Warning: IE6 split is not ECMAScript-compliant.  This breaks '->1'");
    }
  }

  // on the fence about whether to export this?
  function to_function (str) {
    var expr, leftSection, params, rightSection, sections, v, vars, _i, _len;
    params = [];
    expr = str;
    sections = expr.split(/\s*->\s*/m);
    if (sections.length > 1) {
      while (sections.length) {
        expr = sections.pop();
        params = sections.pop().split(/\s*,\s*|\s+/m);
        sections.length && sections.push('(function(' + params + '){return (' + expr + ')})');
      }
    } else if (expr.match(/\b_\b/)) {
      params = '_';
    } else {
      leftSection = expr.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m);
      rightSection = expr.match(/[+\-*\/%&|\^\.=<>!]\s*$/m);
      if (leftSection || rightSection) {
        if (leftSection) {
          params.push('$1');
          expr = '$1' + expr;
        }
        if (rightSection) {
          params.push('$2');
          expr = expr + '$2';
        }
      } else {
        vars = str.replace(/(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*\s*:|this|arguments|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, '').match(/([a-z_$][a-z_$\d]*)/gi) || [];
        for (_i = 0, _len = vars.length; _i < _len; _i++) {
          v = vars[_i];
          params.indexOf(v) >= 0 || params.push(v);
        }
      }
    }
    return new Function(params, 'return (' + expr + ')');
  };

  function functionalize (fn) {
    if (typeof fn === 'function') {
      return fn;
    } else if (typeof fn === 'string' && /^[_a-zA-Z]\w*$/.test(fn)) {
      return function() {
        var args, receiver, _ref;
        receiver = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
        return (_ref = receiver[fn]).call.apply(_ref, [receiver].concat(__slice.call(args)));
      };
    } else if (typeof fn === 'string') {
      return to_function(fn);
    } else if (typeof fn.lambda === 'function') {
      return fn.lambda();
    } else if (typeof fn.toFunction === 'function') {
      return fn.toFunction();
    }
  };

  function extend () {
    var consumer = arguments[0],
        providers = __slice.call(arguments, 1),
        key,
        i,
        provider,
        except;

    for (i = 0; i < providers.length; ++i) {
      provider = providers[i];
      except = provider['except'] || [];
      except.push('except');
      for (key in provider) {
        if (except.indexOf(key) < 0 && provider.hasOwnProperty(key)) {
          consumer[key] = provider[key];
        };
      };
    };
    return consumer;
  };
      
  function rotate (array, n) {
    var copy = array.slice(0),
        i, 
        pull, 
        push;
    
    if (n !== 0) {
      n || (n = 1);
      if (n > 0) {
        pull = 'shift';
        push = 'push';
      }
      else {
        n = -n;
        pull = 'pop';
        push = 'unshift'
      }
      for (i = 0; i < n; ++i) {
        copy[push](copy[pull]());
      }
    }
    
    return copy;
  };
  
  function reverse (array) {
    return array.reduce(function (acc, element) {
      acc.unshift(element);
      return acc;
    }, []);
  };
  
  function flatten (array) {
    var target = [];
    
    flattenOntoTarget(array);
    
    return target;
    
    function flattenOntoTarget (arr) {
      arr.forEach(function (element) {
        if (isArray(element)) {
          flattenOntoTarget(element);
        }
        else target.push(element);
      });
    }
  }
  
  extend(allong.es, {
    flatten: flatten
  })
  
  // # ARITY
  
  function invokeImmediately (fn) { return fn(); };

  // ### "Variadic"
  //
  //    fn = variadic(function (args) { return args })
  //
  //    fn()        //=> []
  //    fn(1)       //=> [1]
  //    fn(1, 2)    //=> [1, 2]
  //    fn(1, 2, 3) //=> [1, 2, 3]
  //
  //    fn = variadic(function (first, rest) { return [first, rest]})
  //
  //    fn()        //=> [undefined, []]
  //    fn(1)       //=> [1, []]
  //    fn(1, 2)    //=> [1, [2]]
  //    fn(1, 2, 3) //=> [1, [2, 3]]
  //
  //    fn = variadic(function (first, second, rest) { return [first, second, rest]})
  //
  //    fn()        //=> [undefined, undefined, []]
  //    fn(1)       //=> [1, undefined, []]
  //    fn(1, 2)    //=> [1, 2, []]
  //    fn(1, 2, 3) //=> [1, 2, [3]]
  var variadic = function () {
    var FUNCTIONS = {};
    function oldVariadic (fn) {
      var fnLength = fn.length;

      if (fnLength < 1) {
        return fn;
      }
      else if (fnLength === 1)  {
        return function () {
          return fn.call(this, __slice.call(arguments, 0))
        }
      }
      else {
        return function () {
          var numberOfArgs = arguments.length,
              namedArgs = __slice.call(arguments, 0, fnLength - 1),
              numberOfMissingNamedArgs = Math.max(fnLength - numberOfArgs - 1, 0),
              argPadding = new Array(numberOfMissingNamedArgs),
              variadicArgs = __slice.call(arguments, fn.length - 1);

          return fn.apply(this, namedArgs.concat(argPadding).concat([variadicArgs]))
        }
      }
    };
    return function (arity, fn) {
      if (fn == null) {
        fn = functionalize(arity);
        arity = 0;
      }
      else fn = functionalize(fn);
      var fnLength = fn.length;
      if (arity === 0) {
        return oldVariadic(fn);
      }
      else if (fnLength <= arity) {
        var fixedParams = fnLength - 1;
        var index = '' + arity + '-' + fixedParams;
        var code;
      
        if (FUNCTIONS[index] == null) {
          var parameters = new Array(arity);
          for (var i = 0; i < arity; ++i) {
            parameters[i] = "__" + i;
          }
          var pstr = parameters.join();
          if (fnLength > 1) {
            var cstr = parameters.slice(0, fnLength - 1).join();
            code = "return function ("+pstr+") { return fn.call("+cstr+", [].slice.call(arguments,"+(fnLength - 1)+")); };";
          }
          else code = "return function ("+pstr+") { return fn.call(this, [].slice.call(arguments, 0)); };";
          FUNCTIONS[index] = new Function(['fn'], code);
        }
        return FUNCTIONS[index](fn);
      }
      else throw 'not supported yet'
    };
  }();
  
  // sets a fixed arity for a function, without currying
  var unvariadic = (function () {
    var FUNCTIONS = {};
    return function unvariadic (arity, fn) {
      if (FUNCTIONS[arity] == null) {
        var parameters = new Array(arity);
        for (var i = 0; i < arity; ++i) {
          parameters[i] = "__" + i;
        }
        var pstr = parameters.join();
        var code = "return function ("+pstr+") { return fn.apply(this, arguments); };";
        FUNCTIONS[arity] = new Function(['fn'], code);
      }
      if (fn == null) {
        return function (fn) { return unvariadic(arity, fn); };
      }
      else return FUNCTIONS[arity](functionalize(fn));
    };
  })();

  // a kind of optional semantics: unary(f)(value) === f(value), unary(f)() === unary(f)
  function unary (fn) {
    return function unary (a) {
      if (a == null) {
        return unary;
      }
      else return fn(a);
    }
  };

  function binary (fn) {
    return function binary (a, b) {
      if (a == null) {
        return binary;
      }
      else if (b == null) {
        return unary(function (b) { return fn(a, b); });
      }
      else return fn(a, b);
    }
  };

  function ternary (fn) {
    return function ternary (a, b, c) {
      if (a == null) {
        return ternary;
      }
      else if (b == null) {
        return binary(function (b, c) { return fn(a, b, c); });
      }
      else if (c == null) {
        return unary(function (c) { return fn(a, b, c); });
      }
      else return fn(a, b, c);
    }
  };

  function quaternary (fn) {
    return function quaternary (a, b, c, d) {
      if (a == null) {
        return quaternary;
      }
      else if (b == null) {
        return ternary(function (b, c, d) { return fn(a, b, c, d); });
      }
      else if (c == null) {
        return binary(function (c, d) { return fn(a, b, c, d); });
      }
      else if (d == null) {
        return unary(function (d) { return fn(a, b, c, d); });
      }
      else return fn(a, b, c, d);
    }
  };
  
  var byArity = [
        invokeImmediately,
        unary,
        binary,
        ternary,
        quaternary
      ],
      byArityLength = byArity.length;
  
  function curryWithLeftAndRight (fn, leftArgs, rightArgs) {
    leftArgs || (leftArgs = []);
    rightArgs || (rightArgs = []);
    var fnLength = fn.length,
        remainingLength = fnLength - leftArgs.length - rightArgs.length;
    
    if (remainingLength < byArityLength) {
      return byArity[remainingLength](handleRemaining);
    }
    else return unvariadic(remainingLength, handleRemaining);
        
    function handleRemaining () {
      var params = __slice.call(arguments, 0, arguments.length),
          numParams = ((params.indexOf(void 0) >= 0)
            ? params.indexOf(void 0)
            : params.length),
          newLeft = leftArgs.concat(params.slice(0, numParams)),
          args = newLeft.concat(rightArgs),
          argsLength = args.length,
          remainingLength = fnLength - argsLength;
      
      if (remainingLength <= 0) {
        return fn.apply(this, args);
      }
      else return curryWithLeftAndRight(fn, newLeft, rightArgs);
    };
  };
  
  extend(allong.es, { curryWithLeftAndRight: curryWithLeftAndRight });
  
  // TODO: Deprecate
  function selfCurrying(fn) {
    fn = functionalize(fn);
    var fnLength = fn.length;
    
    if (fn.length > 0) {
      return variadic(fnLength, function (args) {
        if (args.length === 0) {
          return curry(fn)
        }
        else return fn.apply(this, args);
      });
    }
    else return fn;
  };
  
  extend(allong.es, {
    variadic: variadic,
    unvariadic: unvariadic,
    unary: unary,
    binary: binary,
    ternary: ternary,
    quaternary: quaternary
  });

  // ### Composition

  //    compose(a, b, c)
  //      //=> function (x) {
  //        return a(b(c(x)))
  //      }
  var compose = variadic( function compose (fns) {
    fns = fns.map(functionalize);
    
    var first, firstLength, second, rest, i;
    
    if (fns.length === 0) {
      return function () {};
    }
    else if (fns.length === 1) {
      return fns[0];
    }
    else if (fns.length === 2) {
      first = fns[0];
      firstLength = first.length;
      second = fns[1];
      
      if (firstLength === 1) {
        return function (a) { return first(second(a)); };
      }
      else if (firstLength === 2) {
        return function (a, b) { return first(second(a), b); };
      }
      else if (firstLength === 3) {
        return function (a, b, c) { return first(second(a), b, c); };
      }
      else return variadic( function (a, rest) {
        first.apply(this, [second(a)].concat(rest))
      });
    }
    else {
      first = fns[0];
      firstLength = first.length;
      
      return function (value) {
        for (i = fns.length - 1; i >= 0; --i) {
          value = fns[i](value);
        }
        return value;
      }
    }
  });

  extend(allong.es, {
    compose: compose
  });
  
  // # CALL_FLIPPED
  
  var callFlipped = (function () {
    
    function nullary (fn) {
      return variadic( function (args) {
        return fn.apply(this, reverse(args));
      });
    };
  
    // a kind of optional semantics: unary(f)(value) === f(value), unary(f)() === unary(f)
    function unary (fn) {
      return function unary (a) {
        if (a == null) {
          return unary;
        }
        else return fn(a);
      }
    };

    function binary (fn) {
      return function binary (a, b) {
        if (a == null) {
          return binary;
        }
        else if (b == null) {
          return unary(function (b) { return fn(b, a); });
        }
        else return fn(b, a);
      }
    };

    function ternary (fn) {
      return function ternary (a, b, c) {
        if (a == null) {
          return ternary;
        }
        else if (b == null) {
          return binary(function (c, b) { return fn(c, b, a); });
        }
        else if (c == null) {
          return unary(function (c) { return fn(c, b, a); });
        }
        else return fn(c, b, a);
      }
    };

    function quaternary (fn) {
      return function quaternary (a, b, c, d) {
        if (a == null) {
          return quaternary;
        }
        else if (b == null) {
          return ternary(function (d, c, b) { return fn(d, c, b, a); });
        }
        else if (c == null) {
          return binary(function (d, c) { return fn(d, c, b, a); });
        }
        else if (d == null) {
          return unary(function (d) { return fn(d, c, b, a); });
        }
        else return fn(d, c, b, a);
      }
    };
  
    var byArity = [
          nullary,
          unary,
          binary,
          ternary,
          quaternary
        ],
        byArityLength = byArity.length;

    function callWithLeftFlipped (fn, leftArgs) {
      leftArgs || ( leftArgs = []);
      var fnLength = fn.length,
          remainingLength = fnLength - leftArgs.length;
    
      if (remainingLength < byArityLength) {
        return byArity[remainingLength](handleRemaining);
      }
      else return unvariadic(remainingLength, handleRemaining);
        
      function handleRemaining () {
        var params = __slice.call(arguments, 0, arguments.length),
            numParams = (params.indexOf(void 0) >= 0)
              ? params.indexOf(void 0)
              : params.length
            args = leftArgs.concat(params.slice(0, numParams));
            argsLength = args.length,
            remainingLength = fnLength - argsLength;
      
        if (remainingLength <= 0) {
          return fn.apply(this, reverse(args));
        }
        else return callWithLeftFlipped(fn, args);
      };
    };
  
    return extend( variadic( function callFlipped (fn, args) {
      fn = functionalize(fn);
      var fnLength = fn.length,
          flipped = (fnLength < byArityLength)
            ? byArity[fnLength](fn)
            : callWithLeftFlipped(fn);
    
      if (args.length === 0) {
        return flipped;
      }
      else return flipped.apply(this, args);
    
    }), {
      unary: unary,
      binary: binary,
      ternary: ternary,
      quaternary: quaternary,
      callWithLeftFlipped: callWithLeftFlipped
    });
  
  })();
  
  // synonymish
  var flip = unary(callFlipped);

  extend(allong.es, {
    callFlipped: callFlipped,
    flip: flip
  });
  
  // # APPLY
  
  // the basics: apply and call
  
  // call that retuns a curried function
  
  var apply = binary( function (fn, args) {
    return curryWithLeftAndRight(fn, args, []);
  });
  
  var call = variadic( function (fn, args) {
    return curryWithLeftAndRight(fn, args, []);
  });
  
  var curry = unary(call);
  
  function urApply (fn, args) {
    return fn.apply(this, args);
  };
  
  var applyNow = call( urApply );
  
  var callNow = extend( variadic(applyNow), {
    unary: unary,
    binary: binary,
    ternary: ternary,
    quaternary: quaternary
  });
  
  // flipped forms
  
  var applyNowFlipped = flip(urApply);
  
  // apply and call left
  
  function urApplyLeft (fn, leftArgs) {
    var remainingLength = fn.length - leftArgs.length;
    
    if (fn.length > 0 && remainingLength > 0) {
      return variadic(remainingLength, function (args) {
        return fn.apply(this, leftArgs.concat(args));
      });
    }
    else return fn.apply(this, leftArgs);
  };
  
  var applyLeftNow = call(urApplyLeft);
  
  var callLeftNow = variadic(urApplyLeft);
  
  // flipped
  
  var applyLeftNowWith = flip(urApplyLeft);
  
  var applyRightNowWith = flip(urApplyRight);
  
  // rightmost
  
  function urApplyRight (fn, rightArgs) {
    var remainingLength = fn.length - rightArgs.length;
    
    if (fn.length > 0 && remainingLength > 0) {
      return variadic(remainingLength, function (args) {
        return fn.apply(this, args.concat(rightArgs));
      });
    }
    else return fn.apply(this, rightArgs);
  }
  
  var applyRightNow = call(urApplyRight);
  
  var callRightNow = variadic(urApplyRight);
  
  var callRight = variadic( function (fn, args) {
    return curryWithLeftAndRight(fn, [], args);
  });
  
  var callFirst = binary(call);
  var callFirstWith = flip(callFirst);
  
  var callLast = binary(callRight);
  var callLastWith = flip(callLast);

  // ### Partial applications that bind

  // A partially applied binding function
  //
  // roughly equivalent to applyRight
  //
  // var fn = function (...) { ... }
  //
  // bound(fn)(x)
  //   //=> fn.bind(x)
  //
  // bound(fn, foo)(x)
  //   //=> fn.bind(x, foo)
  //
  // bound(fn, foo, bar)(x)
  //   //=> fn.bind(x, foo, bar)
  var bound = variadic( function (messageName, args) {
    if (args === []) {
      return function (instance) {
        return instance[messageName].bind(instance)
      }
    }
    else {
      return function (instance) {
        return Function.prototype.bind.apply(
          instance[messageName], [instance].concat(args)
        )
      }
    }
  });
  
  var defaults = variadic( function (fn, values) {
    var fln = fn.length,
        vln = values.length;
    return variadic( function (args) {
      var aln = args.length,
          mln = Math.max(fln - aln, 0);
      args = args.concat(values.slice(vln-mln));
      return fn.apply(this, args);
    })
  });
  
  // collects arguments
  function args (arity) {
    if (arity === 1) {
      return function (a) {
        return [a];
      };
    }
    else if (arity === 2) {
      return function (a, b) {
        return [a, b];
      };
    }
    else if (arity === 3) {
      return function (a, b, c) {
        return [a, b, c];
      };
    }
    else return variadic( function (args) {
      return args;
    });
  };
  
  extend(allong.es, {
    applyNow: applyNow,
    apply: apply,
    callNow: callNow,
    call: call,
    callLeft: call,
    callRight: callRight,
    applyNowFlipped: applyNowFlipped,
    applyLeftNow: applyLeftNow,
    callLeftNow: callLeftNow,
    applyLeftNowWith: applyLeftNowWith,
    applyRightNow: applyRightNow,
    callRightNow: callRightNow,
    applyRightNowWith: applyRightNowWith,
    callFirst: callFirst,
    callLast: callLast,
    callFirstWith: callFirstWith,
    callLastWith: callLastWith,
    bound: bound,
    defaults: defaults,
    args: args,
    curry: curry
  });
  
  // # FOLDING
  
  var filter = binary( function filter (list, fn) {
    fn = functionalize(fn);
    
    return __filter.call(list, fn);
  });
  
  var filterWith = flip(filter);
  
  var map = binary( function map (list, fn) {
    fn = functionalize(fn);
    var fnLength = fn.length;
  
    if (fnLength !== 1) {
      fn = unary(fn);
    }
    return __map.call(list, fn);
  });
  
  var mapWith = flip(map);
  
  var mapArgumentsWith = binary( function (withFn, fn) {
    return variadic(fn.length, function (args) {
      return applyNow(fn, map(args, withFn));
    });
  });

  // turns any function into a recursive mapper
  //
  // deepMap(function (x) { return x * x })([1, [2, 3], 4])
  //   //=> [1, [4, 9], 16]
  // function deepMap (fn) {
  //   return function innerDeepMap (tree) {
  //     return __map.call(tree, function (element) {
  //       if (Array.isArray(element)) {
  //         return innerSoak(element);
  //       }
  //       else return fn(element);
  //     });
  //   };
  // };
  
  var deepMap = binary( function (tree, fn) {
    fn = functionalize(fn);
    
    return __map.call(tree, function (element) {
      if (Array.isArray(element)) {
        return deepMap(element, fn);
      }
      else return fn(element);
    });
  });
  
  var deepMapWith = flip(deepMap);
  
  extend(allong.es, {
    map: map,
    mapWith: mapWith,
    mapArgumentsWith: mapArgumentsWith,
    filter: filter,
    filterWith: filterWith,
    deepMap: deepMap,
    deepMapWith: deepMapWith
  });
  
  // # DECORATORS
  
  function mapIfMany (fn) {
    fn = functionalize(fn);
    
    return variadic( function (argList) {
      if (argList.length === 1 && !isArray(argList[0])) {
        return fn.call(this, argList[0]);
      }
      else return deepMap(argList, fn);
    });
  }

  var maybe = mapIfMany( function maybe (fn) {
    fn = functionalize(fn);
    return function () {
      var i;

      if (arguments.length === 0) {
        return
      }
      else {
        for (i = 0; i < arguments.length; ++i) {
          if (arguments[i] == null) return arguments[i];
        }
        return fn.apply(this, arguments)
      }
    }
  });

  function tap (value, fn) {
    fn = functionalize(fn);

    if (fn === void 0) {
      return curried
    }
    else return curried(fn);

    function curried (fn) {
      if (typeof(fn) === 'function') {
        fn(value)
      }
      return value
    }
  }

  function fluent (fn) {
    fn = functionalize(fn);
    return function () {
      fn.apply(this, arguments);
      return this
    }
  }

  // decorates a function to return its first argument
  var returnFirst = function (fn) {
    fn = functionalize(fn);
    return function () {
      fn.apply(this, arguments);
      return arguments[0];
    }
  }

  function tee (decoration) {
    decoration = functionalize(decoration);
    return function (fn) {
      fn = functionalize(fn);
      return compose(returnFirst(decoration), fn);
    };
  };

  function once (fn) {
    fn = functionalize(fn);
    var done = false,
        testAndSet;

    if (!!fn.name) {
      testAndSet = function () {
        this["__once__"] || (this["__once__"] = {})
        if (this["__once__"][fn.name]) return true;
        this["__once__"][fn.name] = true;
        return false
      }
    }
    else  {
      testAndSet = function (fn) {
        if (done) return true;
        done = true;
        return false
      }
    }

    return function () {
      return testAndSet.call(this) ? void 0 : fn.apply(this, arguments)
    }
  }

  function memoized (fn, keymaker) {
    fn = functionalize(fn);
    var lookupTable = {},
        key,
        value;

    keymaker || (keymaker = function (args) {
      return JSON.stringify(args)
    });

    return function () {
      var key = keymaker.call(this, arguments);

      return lookupTable[key] || (
        lookupTable[key] = fn.apply(this, arguments)
      )
    }
  };
  
  extend(allong.es, {
    maybe: maybe,
    tap: tap,
    fluent: fluent,
    returnFirst: returnFirst,
    tee: tee,
    once: once,
    memoized: memoized
  });
  
  // # MIXIN

  function mixin (decoration) {
    return function decorator () {
      if (arguments[0] !== void 0) {
        return decorator.call(arguments[0]);
      }
      else {
        extend(this, decoration);
        return this;
      }
    };
  };

  function classDecorator (decoration) {
    return function (clazz) {
      function Decorated  () {
        var self = this instanceof Decorated
                   ? this
                   : new Decorated();

        return clazz.apply(self, arguments);
      };
      Decorated.prototype = extend(new clazz(), decoration);
      return Decorated;
    };
  };
  
  extend(allong.es, {
    mixin: mixin,
    classDecorator: classDecorator
  });
  
  // # MORE

  var unbind = function unbind (fn) {
    fn = functionalize(fn);
    return fn.unbound ? unbind(fn.unbound()) : fn
  };

  function bind (fn, context, force) {
    fn = functionalize(fn);
    var unbound, bound;

    if (force) {
      fn = unbind(fn)
    }
    bound = function () {
      return fn.apply(context, arguments)
    };
    bound.unbound = function () {
      return fn;
    };

    return bound;
  }

  function invoke (fn) {
    fn = functionalize(fn);
    var args = __slice.call(arguments, 1);

    return function (instance) {
      return fn.apply(instance, args)
    }
  };

  function get (object, getName) {
    if (getName == null) {
      return function (getName) { return object[getName]; };
    }
    else return object[getName];
  };

  function getWith (getName, object) {
    if (object == null) {
      return function (object) { return object[getName]; };
    }
    else return object[getName];
  };

  var pluckWith = compose(mapWith, getWith),
      pluck = flip(pluckWith);

  // Send a message/invoke a method on the receiver.
  // TODO: Think about what it has in common with callLeft
  var send = variadic( function (methodName, args) {
    return variadic( function (receiver, remainingArgs) {
      var fn = receiver[methodName];
      return fn.apply(receiver, args.concat(remainingArgs))
    })
  });
  
  extend(allong.es, {
    bind: bind,
    unbind: unbind,
    invoke: invoke,
    get: get,
    getWith: getWith,
    send: send,
    pluckWith: pluckWith,
    pluck: pluck
  });
  
  // # TRAMPOLINE
      
  function Thunk (closure) {
    if (!(this instanceof Thunk))
      return new Thunk(closure);
    
    this.closure = closure;
  };
  
  Thunk.prototype.force = function () {
    return this.closure();
  };
      
  function trampoline (fn) {
    var trampolined = variadic( function (args) {
      var result = fn.apply(this, args);
      
      while (result instanceof Thunk) {
        result = result.force();
      }
      
      return result;
    });
    trampolined.__trampolined_fn = fn;
    return trampolined;
  };
  
  var tailCall = variadic( function (fn, args) {
    var context = this;
    if (fn.__trampolined_fn instanceof Function) {
      return new Thunk( function () { 
        return fn.__trampolined_fn.apply(context, args);
      });
    }
    else return new Thunk( function () { 
      return fn.apply(context, args);
    });
  });
  
  extend(allong.es, {
    trampoline: trampoline,
    tailCall: tailCall,
    Thunk: Thunk
  });
  
  // # ITERATORS
  
  (function (allong) {
      
    function fold (iter, binaryFn, seed) {
      var state, element;
      binaryFn = functionalize(binaryFn);
      if (seed !== void 0) {
        state = seed;
      }
      else {
        state = iter();
      }
      element = iter();
      while (element != null) {
        state = binaryFn.call(element, state, element);
        element = iter();
      }
      return state;
    };
  
    var HASNTBEENRUN = {};
  
    function unfold (seed, unaryFn) {
      var state = HASNTBEENRUN;
      unaryFn = functionalize(unaryFn);
      return function () {
        if (state === HASNTBEENRUN) {
          return (state = seed);
        }
        else if (state != null) {
          return (state = unaryFn.call(state, state));
        }
        else return state;
      };
    };
  
    // note that the unfoldWithReturn behaves differently than
    // unfold with respect to the first value returned
    function unfoldWithReturn (seed, unaryFn) {
      var state = seed,
          pair,
          value;
      unaryFn = functionalize(unaryFn);
      return function () {
        if (state != null) {
          pair = unaryFn.call(state, state);
          value = pair[1];
          state = value != null
                  ? pair[0]
                  : void 0
          return value;
        }
        else return void 0;
      };
    };

    function accumulate (iter, binaryFn, initial) {
      var state = initial;
      binaryFn = functionalize(binaryFn);
      return function () {
        element = iter();
        if (element == null) {
          return element;
        }
        else {
          if (state === void 0) {
            return (state = element);
          }
          else return (state = binaryFn.call(element, state, element));
        }
      }
    };
  
    function accumulateWithReturn (iter, binaryFn, initial) {
      var state = initial,
          stateAndReturnValue;
      binaryFn = functionalize(binaryFn);
      return function () {
        element = iter();
        if (element == null) {
          return element;
        }
        else {
          if (state === void 0) {
            return (state = element);
          }
          else {
            stateAndReturnValue = binaryFn.call(element, state, element);
            state = stateAndReturnValue[0];
            return stateAndReturnValue[1];
          }
        }
      }
    };
  
    function map (iter, unaryFn) {
      unaryFn = functionalize(unaryFn);
      return function() {
        var element;
        element = iter();
        if (element != null) {
          return unaryFn.call(element, element);
        } else {
          return void 0;
        }
      };
    };

    function select (iter, unaryPredicateFn) {
      unaryPredicateFn = functionalize(unaryPredicateFn);
      return function() {
        var element;
        element = iter();
        while (element != null) {
          if (unaryPredicateFn.call(element, element)) {
            return element;
          }
          element = iter();
        }
        return void 0;
      };
    };
  
    function reject (iter, unaryPredicateFn) {
      unaryPredicateFn = functionalize(unaryPredicateFn);
      return select(iter, function (something) {
        return !unaryPredicateFn(something);
      });
    };
  
    function find (iter, unaryPredicateFn) {
      unaryPredicateFn = functionalize(unaryPredicateFn);
      return select(iter, unaryPredicateFn)();
    }

    function slice (iter, numberToDrop, numberToTake) {
      var count = 0;
      while (numberToDrop-- > 0) {
        iter();
      }
      if (numberToTake != null) {
        return function() {
          if (++count <= numberToTake) {
            return iter();
          } else {
            return void 0;
          }
        };
      }
      else return iter;
    };
  
    var drop = defaults(binary(slice), 1);
  
    function take (iter, numberToTake) {
      return slice(iter, 0, numberToTake == null ? 1 : numberToTake);
    }

    function FlatArrayIterator (array) {
      var index = 0;
      return function() {
        return array[index++];
      };
    };
  
    function RecursiveArrayIterator (array) {
      var index, myself, state;
      index = 0;
      state = [];
      myself = function() {
        var element, tempState;
        element = array[index++];
        if (element instanceof Array) {
          state.push({
            array: array,
            index: index
          });
          array = element;
          index = 0;
          return myself();
        } else if (element === void 0) {
          if (state.length > 0) {
            tempState = state.pop(), array = tempState.array, index = tempState.index;
            return myself();
          } else {
            return void 0;
          }
        } else {
          return element;
        }
      };
      return myself;
    };
  
    function K (value) {
      return function () {
        return value;
      };
    };

    function upRange (from, to, by) {
      return function () {
        var was;
      
        if (from > to) {
          return void 0;
        }
        else {
          was = from;
          from = from + by;
          return was;
        }
      }
    };

    function downRange (from, to, by) {
      return function () {
        var was;
      
        if (from < to) {
          return void 0;
        }
        else {
          was = from;
          from = from - by;
          return was;
        }
      }
    };
  
    function range (from, to, by) {
      if (from == null) {
        return upRange(1, Infinity, 1);
      }
      else if (to == null) {
        return upRange(from, Infinity, 1);
      }
      else if (by == null) {
        if (from <= to) {
          return upRange(from, to, 1);
        }
        else return downRange(from, to, 1)
      }
      else if (by > 0) {
        return upRange(from, to, by);
      }
      else if (by < 0) {
        return downRange(from, to, Math.abs(by))
      }
      else return k(from);
    };
  
    var numbers = unary(range);

    extend(allong.es, { iterators: {
      accumulate: accumulate,
      accumulateWithReturn: accumulateWithReturn,
      fold: fold,
      unfold: unfold,
      unfoldWithReturn: unfoldWithReturn,
      map: map,
      select: select,
      reject: reject,
      filter: select,
      find: find,
      slice: slice,
      drop: drop,
      take: take,
      FlatArrayIterator: FlatArrayIterator,
      RecursiveArrayIterator: RecursiveArrayIterator,
      constant: K,
      K: K,
      numbers: numbers,
      range: range
    }});
  
  })(allong);
  
  // Monadic Sequencing
  // ------------------
  
  //    sequence(a, b, c)
  //      //=> function (x) {
  //        return c(b(a(x)))
  //      }
  // var sequence = callFlipped(compose);

  var Promise = require('promise');
  
  var BaseMonad = function (methods) {
    var name,
        body;
    if (methods) {
      for (name in methods) {
        if (methods.hasOwnProperty(name)) {
          this[name] = methods[name];
        }
      }
    }
  };

  BaseMonad.prototype = {
    of: function (value) { return value; },
    map: function (fn) { return fn; },
    chain: function (mValue, fn) { return this.map(fn)(mValue); }
  };
  
  var Identity = new BaseMonad();

  var Maybe = new BaseMonad({
    map: maybe
  });

  var Writer = new BaseMonad({
    of: function(value) {
      return [value, ''];
    },
    map: function(fn) {
      return function(_arg) {
        var newlyWritten, result, value, writtenSoFar, _ref;
        value = _arg[0], writtenSoFar = _arg[1];
        _ref = fn(value), result = _ref[0], newlyWritten = _ref[1];
        return [result, writtenSoFar + newlyWritten];
      };
    }
  });
  
  var ArrayWriter = new BaseMonad({
    of: function (argument) {
          return [argument, []];
        },
    chain: function (valueAndLogList, fn) {
              var value = valueAndLogList[0],
                  logList = valueAndLogList[1],
                  resultAndLogList = fn(value),
                  result = resultAndLogList[0],
                  resultLogList = flatten(logList.concat(resultAndLogList[1]));
          
              return [result, resultLogList];
            }
  });

  var List = new BaseMonad({
    of: function(value) {
      return [value];
    },
    join: function(mValue) {
      return mValue.reduce(this.concat, this.zero());
    },
    map: function(fn) {
      return function(mValue) {
        return mValue.map(fn);
      };
    },
    zero: function() {
      return [];
    },
    concat: function(ma, mb) {
      return ma.concat(mb);
    },
    chain: function(mValue, fn) {
      return this.join(this.map(fn)(mValue));
    }
  });

  var Then = new BaseMonad({
    of: function(value) {
      return new Promise(function(resolve, reject) {
        return resolve(value);
      });
    },
    map: function(fnReturningAPromise) {
      return function(promiseIn) {
        return new Promise(function(resolvePromiseOut, rejectPromiseOut) {
          return promiseIn
            .then(fnReturningAPromise, rejectPromiseOut)
            .then(resolvePromiseOut, rejectPromiseOut)
        });
      };
    }
  });

  var Callback = {
    chain: function (mValue, fn) {
      if (mValue.length === 1) {
        return function (value, callback) {
          return mValue(value, variadic( function (results) {
            return fn.apply(null, results.concat([callback]));
          }));
        };
      }
      else {
        // needs variadicr
        return variadic( function (args) {
          var callback = args[args.length - 1],
              values   = __slice.call(args, 0, args.length - 1);
          return mValue.apply(this, values.concat([ variadic( function (results) {
            return fn.apply(null, results.concat([callback]));
          })]));
        });
      }
    }
  };
  
  var pipeline = flip(compose);

  var sequence = variadic( function (sequenceArgs) {
    var fns, supervisor, arity, _of, _map, _chain;
    if (sequenceArgs.length === 0) {
      return function (value) { return value; } // I combinator
    }
    else {
      sequenceArgs = flatten(sequenceArgs);
      if (isFunction(sequenceArgs[0])) {
        return pipeline.apply(this, sequenceArgs);
      }
      else {
        if (isString(sequenceArgs[0])) {
          supervisor = sequence[sequenceArgs[0]];
        }
        else supervisor = sequenceArgs[0];
        
        _map   = (supervisor.map && supervisor.map.bind(supervisor));
        _chain = (supervisor.chain && supervisor.chain.bind(supervisor));
        _of    = (supervisor.of && supervisor.of.bind(supervisor));
        
        // default for _chain
        if (_chain == null) {
          _chain = (_map == null)
                   ? function (mValue, fn) { return fn(mValue); } // "T"
                   : function (mValue, fn) { return _map(fn)(mValue); }
        }
        
        fns = 2 <= sequenceArgs.length ? __slice.call(sequenceArgs, 1) : [];
        arity = fns.length > 0
          ? fns[0].length
          : 0;
          
        if (isFunction(_of)) {
          // option 1: There is an _of: uses this function to provide a seed
          return variadic(arity, function (args) {
            return fns.reduce(_chain, _of.apply(this, args));
          });
        }
        else {
          // option 2: uses the first function as the seed
          chained = fns.slice(1).reduce(_chain, fns[0]);
          return unvariadic(arity, chained);
        }
      }
    }
  });
  
  extend(sequence, {
    Identity: Identity,
    Maybe: Maybe,
    List: List,
    Writer: Writer,
    Then: Then,
    Callback: Callback
  });

  extend(allong.es, {
    pipeline: pipeline,
    sequence: sequence
  });
  
  // andand, oror
  // ------------
  
  var andand = mapIfMany( function andand (fn) {
    return variadic(fn.length, function (args) {
      if (args.length > 0) {
        var argTruthiness = args.reduce(function (acc, value) { return acc && value; });
        return argTruthiness && fn.apply(this, args);
      }
    })
  })
  
  var oror = mapIfMany( function oror (fn) {
    return variadic(fn.length, function (args) {
      if (args.length > 0) {
        var argTruthiness = args.reduce(function (acc, value) { return acc || value; });
        return argTruthiness || fn.apply(this, args);
      }
    })
  });

  extend(allong.es, {
    andand: andand,
    oror: oror
  });
  
  // Sequence Transformers
  // ---------------------
  
  var fn2Then = mapIfMany( function fn2Then (fn) {
    return function (value) {
      return new Promise(function(resolve, reject) {
        return resolve(fn(value));
      });
    }
  });
  
  var callback2Then = mapIfMany( function callback2Then (fn) {
    if (fn.length === 1) {
      return function () {
        return new Promise(function(resolve, reject) {
          return fn(function (value) {
            return resolve(value);
          })
        });
      };
    }
    else return variadic(fn.length - 1, function(values) {
      return new Promise(function(resolve, reject) {
        return fn.apply(this, values.concat([function (value) { return resolve(value); }]));
      });
    });
  });
  
  var fn2Callback = mapIfMany( function fn2Callback (fn) {
    return function (value, callback) {
      return callback(fn(value));
    }
  });
  
  var then2Callback = mapIfMany( function fn2Callback (fn) {
    return function (value, callback) {
      var thennable = fn(value);
      thennable.then(callback, function (error) { throw error; });
    }
  });

  extend(sequence, {
    fn2Then: fn2Then,
    fn2Callback: fn2Callback,
    callback2Then: callback2Then,
    then2Callback: then2Callback
  });

  // Exports and sundries
  // --------------------

  if (typeof exports !== 'undefined') {
    if (typeof module !== 'undefined' && module.exports) {
      exports = module.exports = allong;
    }
    exports.allong = allong;
  } else {
    root.allong = allong;
  }
  
}).call(this);


================================================
FILE: package.json
================================================
{
  "author": "Reg Braithwaite <raganwald@gmail.com> (http://braythwayt.com)",
  "name": "allong.es",
  "description": "Combinators and Function Decorators",
  "version": "0.14.0",
  "homepage": "http://allong.es",
  "repository": {
    "type": "git",
    "url": "git://github.com/raganwald/allong.es.git"
  },
  "main": "lib/allong.es.js",
  "scripts": {
    "test": "jasmine-node --coffee --verbose spec"
  },
  "engines": {
    "node": ""
  },
  "dependencies": {
    "promise": ""
  },
  "devDependencies": {
    "jasmine-node": "",
    "coffee-script": "",
    "grunt": "~0.4.1",
    "grunt-contrib-uglify": "~0.2.0"
  }
}


================================================
FILE: spec/andand-oror.spec.coffee
================================================
{andand, oror} = require('../lib/allong.es').allong.es

T = () -> true
F = () -> false
N = () -> null
U = () -> undefimed

describe "andand", ->
  
  it " should pass null through", ->
    expect( andand(T)(null) ).toEqual(null && true)
  
  it " should pass undefined through", ->
    expect( andand(T)(undefined) ).toEqual(undefined && true)
  
  it " should pass false through", ->
    expect( andand(T)(false) ).toEqual(false && true)
    
  it 'should evaluate on truthy', ->
    expect( andand(T)('truthy') ).toEqual('truthy' && true)
    
  it 'should evaluate on []', ->
    expect( andand(T)([]) ).toEqual([] && true)
    
  it 'should evaluate on \'\'', ->
    expect( andand(T)('') ).toEqual('' && true)
    
  it 'should evaluate on 0', ->
    expect( andand(T)(0) ).toEqual(0 && true)

describe "oror", ->
  
  it " should pass null through", ->
    expect( oror(T)(null) ).toEqual(null || true)
  
  it " should pass undefined through", ->
    expect( oror(T)(undefined) ).toEqual(undefined || true)
  
  it " should pass false through", ->
    expect( oror(T)(false) ).toEqual(false || true)
    
  it 'should evaluate on truthy', ->
    expect( oror(T)('truthy') ).toEqual('truthy' || true)
    
  it 'should evaluate on []', ->
    expect( oror(T)([]) ).toEqual([] || true)
    
  it 'should evaluate on \'\'', ->
    expect( oror(T)('') ).toEqual('' || true)
    
  it 'should evaluate on 0', ->
    expect( oror(T)(0) ).toEqual(0 || true)

================================================
FILE: spec/apply.spec.coffee
================================================
{ callRight, applyNow, callNow, applyNowFlipped, 
  call, applyLeftNow, callLeftNow, args, applyLeftNowWith,
  applyRightNow, callRightNow, applyRightNowWith,
  callFirst, callFirstWith, callLast, callLastWith, apply
} = require('../lib/allong.es.js').allong.es

echo = (a, b, c) -> "#{a} #{b} #{c}"

five = (a, b, c, d, e) -> [a, b, c, d, e]
three = (a, b, c) -> [a, b, c]
twelve = (a, b, c, d, e, f, g, h, i, j, k, l) ->
vari = (args...) -> args
one = (x) -> x

describe "apply", ->
  
  it "should apply an array of arguments to a function", ->
    expect( apply(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it "should be curried", ->
    expect( apply(three)([1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it "should  be self-currying, it should apply what it gets", ->
    expect( apply(three, [1, 2])(3) ).toEqual three(1, 2, 3)

describe "applyNow", ->
  
  it "should apply an array of arguments to a function", ->
    expect( applyNow(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it "should not be self-currying, it should apply what it gets", ->
    expect( applyNow(three, [1, 2]) ).toEqual three(1, 2)
    expect( applyNow(three, [1]) ).toEqual three(1)
    
  it "should be curried", ->
    expect( applyNow(three)([1, 2, 3]) ).toEqual three(1, 2, 3)
    
  describe "when flipped", ->
    
    it "should apply an array of arguments to a function", ->
      expect( applyNowFlipped([1, 2, 3], three) ).toEqual three(1, 2, 3)

    it "should be curried", ->
      expect( applyNowFlipped([1, 2, 3])(three) ).toEqual three(1, 2, 3)

describe "callNow", ->
  
  it "should apply arguments to a function", ->
    expect( callNow(three, 1, 2, 3) ).toEqual three(1, 2, 3)
    
  it "should not be self-currying, it should apply what it gets", ->
    expect( callNow(three, 1, 2) ).toEqual three(1, 2)
    expect( callNow(three, 1) ).toEqual three(1)
    
  it "should not be curried", ->
    expect( callNow(three) ).toEqual three()
    
  # variadic functions do not have a 'this' predefined at this point.
    
describe "applyLeftNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( applyLeftNow(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( applyLeftNow(three, [1, 2]) ).not.toEqual three(1, 2)
    expect( applyLeftNow(three, [1, 2])(3) ).toEqual three(1, 2, 3)
    
  it 'should not be fully curried', ->
    expect( applyLeftNow(three, [1])(2) ).toEqual three(1, 2)
    
  describe "when flipped", ->
  
    it 'should apply all the arguments if possible', ->
      expect( applyLeftNowWith([1, 2, 3], three) ).toEqual three(1, 2, 3)
    
    it 'should not be full application', ->
      expect( applyLeftNowWith([1, 2], three) ).not.toEqual three(1, 2)
      expect( applyLeftNowWith([1, 2], three)(3) ).toEqual three(1, 2, 3)
    
    it 'should not be fully curried', ->
      expect( applyLeftNowWith([1], three)(2) ).toEqual three(1, 2)
    
describe "callLeftNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( callLeftNow(three, 1, 2, 3) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( callLeftNow(three, 1, 2) ).not.toEqual three(1, 2)
    expect( callLeftNow(three, 1, 2)(3) ).toEqual three(1, 2, 3)
    
  it 'should not be fully curried', ->
    expect( callLeftNow(three, 1)(2) ).toEqual three(1, 2)

describe "applyRightNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( applyRightNow(three, [1, 2, 3]) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( applyRightNow(three, [1, 2]) ).not.toEqual three(1, 2)
    expect( applyRightNow(three, [1, 2])(3) ).toEqual three(3, 1, 2)
    
  it 'should not be fully curried', ->
    expect( applyRightNow(three, [1])(2) ).toEqual three(2, 1)
    
  describe "when flipped", ->
  
    it 'should apply all the arguments if possible', ->
      expect( applyRightNowWith([1, 2, 3], three) ).toEqual three(1, 2, 3)
    
    it 'should not be full application', ->
      expect( applyRightNowWith([1, 2], three) ).not.toEqual three(1, 2)
      expect( applyRightNowWith([1, 2], three)(3) ).toEqual three(3, 1, 2)
    
    it 'should not be fully curried', ->
      expect( applyRightNowWith([1], three)(2) ).toEqual three(2, 1)
    
describe "callRightNow", ->
  
  it 'should apply all the arguments if possible', ->
    expect( callRightNow(three, 1, 2, 3) ).toEqual three(1, 2, 3)
    
  it 'should not be full application', ->
    expect( callRightNow(three, 1, 2) ).not.toEqual three(1, 2)
    expect( callRightNow(three, 1, 2)(3) ).toEqual three(3, 1, 2)
    
  it 'should not be fully curried', ->
    expect( callRightNow(three, 1)(2) ).toEqual three(2, 1)
    
################################

describe "call", ->
  
  it "should call an array of arguments to a function", ->
    expect( call(echo, 1, 2, 3) ).toEqual "1 2 3"
  
  it "should have a curried nature", ->
    expect( call(five)(1, 2, 3, 4, 5) ).toEqual [1..5]
    expect( call(five)(1, 2, 3)(4, 5) ).toEqual [1..5]
    expect( call(five, 1, 2, 3)(4, 5) ).toEqual [1..5]
    expect( call(five, 1, 2, 3)(4)(5) ).toEqual [1..5]
    
  it "should get the arity right for small amounts", ->
    expect( call(five, 1, 2).length ).toEqual 3

describe "callRight", ->
  
  it "should call an array of arguments to a function", ->
    expect( callRight(echo, 1, 2, 3) ).toEqual "1 2 3"
  
  it "should have a curried nature", ->
    expect( callRight(five)(1, 2, 3, 4, 5) ).toEqual [1..5]
    expect( callRight(five)(1, 2, 3)(4, 5) ).toEqual [1..5]
    
  it "should apply given arguments to the right", ->
    expect( callRight(five, 1)(2, 3, 4, 5) ).toEqual [2, 3, 4, 5, 1]
    expect( callRight(five, 1, 2)(3, 4, 5) ).toEqual [3, 4, 5, 1, 2]
    expect( callRight(five, 1, 2, 3)(4, 5) ).toEqual [4, 5, 1, 2, 3]
    expect( callRight(five, 1, 2, 3, 4)(5) ).toEqual [5, 1, 2, 3, 4]
    expect( callRight(five, 1, 2, 3)(4, 5) ).toEqual [4, 5, 1, 2, 3]
    expect( callRight(five, 1, 2, 3)(4)(5) ).toEqual [4, 5, 1, 2, 3]
    
  it "should get the arity right for small amounts", ->
    expect( callRight(five, 1, 2).length ).toEqual 3
    
describe 'callFirst', ->
  
  it 'should call with the first argument', ->
    expect( callFirst(three, 1)(2, 3) ).toEqual [1..3]
    
  it 'should get the arity right', ->
    expect( callFirst.length ).toEqual 2
    expect( callFirst(three, 1).length ).toEqual 2
    
  it 'should be curried', ->
    expect( callFirst(three)(1)(2, 3) ).toEqual [1..3]
    
describe 'callFirstWith', ->
  
  it 'should call with the first argument', ->
    expect( callFirstWith(1, three)(2, 3) ).toEqual [1..3]
    
  it 'should get the arity right', ->
    expect( callFirstWith.length ).toEqual 2
    expect( callFirstWith(1, three).length ).toEqual 2
    
  it 'should be curried', ->
    expect( callFirstWith(1)(three)(2, 3) ).toEqual [1..3]
    
describe "args", ->
  
  it "should collect arguments into an array", ->
    expect( args(3)(1, 2, 3) ).toEqual [1, 2, 3]

================================================
FILE: spec/arity.spec.coffee
================================================
{unvariadic, variadic, curry} = require('../lib/allong.es.js').allong.es

numberOfArgs = -> arguments.length
threeArguments = (a, b, c) -> arguments.length

describe "unvariadic", ->
  
  it "should clip arguments", ->
    expect( unvariadic(3, numberOfArgs)(1, 2, 3, 4, 5) ).toEqual threeArguments(1, 2, 3, 4, 5)
    
  it "shouldn't pad arguments", ->
    expect( unvariadic(3, numberOfArgs)(1) ).toEqual threeArguments(1)
    
describe "variadic", ->
  
  describe "with an arity", ->
    
    one = (args) -> [args]
    
    two = (arg, args) -> [arg, args]
    
    onev = (args...) -> [args]
    
    twov = (arg, args...) -> [arg, args]
    
    it 'should 1', ->
      expect( variadic(1, one)(1, 2, 3) ).toEqual onev(1, 2, 3)
    
    it 'should 2', ->
      expect( variadic(1, one).length ).toEqual 1
    
    it 'should 3', ->
      expect( variadic(2, one).length ).toEqual 2

describe "curry", ->
  
  three = (a, b, c) -> [a, b, c]
  
  it "should be a function that returns a function", ->
    expect( curry instanceof Function).toEqual true
    expect( curry(three) instanceof Function).toEqual true
  
  it "should allow a full invocation", ->
    expect( curry(three)(1, 2, 3) ).toEqual [1, 2, 3]
  
  it "should allow partial invocation", ->
    expect( curry(three)(1)(2, 3) ).toEqual [1, 2, 3]
    expect( curry(three)()(1, 2, 3) ).toEqual [1, 2, 3]
    expect( curry(three)(1)(2)(3) ).toEqual [1, 2, 3]
    expect( curry(three)(1, 2)(3) ).toEqual [1, 2, 3]
    
    # describe "self-currying", ->
    #   
    #   it "shouldn't affect normal useage", ->
    #     expect(selfCurrying(three)(4, 5, 6)).toEqual three(4, 5, 6)
    #     expect(selfCurrying(three)(4, 5)).toEqual three(4, 5)
    #     expect(selfCurrying(three)(4)).toEqual three(4)
    #   
    #   it "should curry with no arguments", ->
    # expect( selfCurrying(three)()(1)(2, 3) ).toEqual [1, 2, 3]
    # expect( selfCurrying(three)()()(1, 2, 3) ).toEqual [1, 2, 3]
    # expect( selfCurrying(three)()(1)(2)(3) ).toEqual [1, 2, 3]
    # expect( selfCurrying(three)()(1, 2)(3) ).toEqual [1, 2, 3]


================================================
FILE: spec/core.spec.coffee
================================================
# unary, binary, ternary, variadic, compose, sequence???
{defaults, mapWith, getWith, filterWith, compose, sequence, variadic, flip, curry} = require('../lib/allong.es.js').allong.es

echo = (a, b, c) -> "#{a} #{b} #{c}"
parenthesize = (a) -> "(#{a})"
square = (n) -> n * n
oddP = (n) -> !!(n % 2)

describe "defaults", ->
  
  it "should default values", ->
    expect( defaults(echo, 'c')('a', 'b') ).toEqual 'a b c'
    expect( defaults(echo, 'b', 'c')('a') ).toEqual 'a b c'
    expect( defaults(echo, 'a', 'b', 'c')() ).toEqual 'a b c'
  
  it "should ignore uneccesary defaults", ->
    expect( defaults(echo, 'a', 'b', 'c')('A') ).toEqual 'A b c'
    expect( defaults(echo, 'a', 'b', 'c')('A', 'B') ).toEqual 'A B c'
    expect( defaults(echo, 'a', 'b', 'c')('A', 'B', 'C') ).toEqual 'A B C'
    expect( defaults(echo, 'a', 'b', 'c')('A', 'B', 'C', 'D') ).toEqual 'A B C'
    
describe "mapWith", ->
  
  it "should map backwards", ->
    expect( mapWith(square, [1..5]) ).toEqual [1, 4, 9, 16, 25]
  
  it "should map backwards, curried", ->
    expect( mapWith(square)([1..5]) ).toEqual [1, 4, 9, 16, 25]
    
describe "filterWith", ->
  
  it "should filter backwards", ->
    expect( filterWith(oddP, [1..5]) ).toEqual [1, 3, 5]
  
  it "should filter backwards, curried", ->
    expect( filterWith(oddP)([1..5]) ).toEqual [1, 3, 5]

describe "compose", ->
  
  it "should compose two functions", ->
    expect( compose(parenthesize, parenthesize)('hello') ).toEqual '((hello))'
  
  it "should respect the arity of the first function", ->
    expect( compose(parenthesize, parenthesize).length ).toEqual 1
    expect( compose(echo, parenthesize).length ).toEqual 3
    
  it "should handle a common use case, pluckWith", ->
    myPluckWith = compose mapWith, getWith
    expect( myPluckWith('name')([{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']
    expect( myPluckWith('name', [{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']

describe "sequence", ->
  
  it "should sequence two functions", ->
    expect( sequence(parenthesize, parenthesize)('hello') ).toEqual '((hello))'
  
  it "should respect the arity of the first function", ->
    expect( sequence(parenthesize, parenthesize).length ).toEqual 1
    expect( sequence(parenthesize, echo).length ).toEqual 3
    
  it "should handle a common use case, pluckWith", ->
    myPluckWith = sequence getWith, mapWith
    expect( myPluckWith('name')([{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']
    expect( myPluckWith('name', [{name: 'foo'}, {name: 'bar'}]) ).toEqual ['foo', 'bar']
  
describe "flip", ->
  
  a = (a) -> [a]
  b = (a, b) -> [a, b]
  c = (a, b, c) -> [a, b, c]
  d = (a, b, c, d) -> [a, b, c, d]
  v = variadic( (x) -> x )
  
  it "should flip a unary function", ->
    expect( flip(a)(1) ).toEqual [1]
    
  it "should flip a binary function", ->
    expect( flip(b)(1, 2) ).toEqual [2, 1]
    
  it "should flip a ternary function", ->
    expect( flip(c)(1, 2, 3) ).toEqual [3, 2, 1]
    
  it "should flip a quaternary function", ->
    expect( flip(d)(1, 2, 3, 4) ).toEqual [4, 3, 2, 1]
    
  it "should flip a variadic function", ->
    expect( flip(v)(1, 2, 3, 4, 5) ).toEqual [5, 4, 3, 2, 1]
    
  it "should respect arities", ->
    expect( flip(v).length ).toEqual v.length
    expect( flip(a).length ).toEqual a.length
    expect( flip(b).length ).toEqual b.length
    expect( flip(c).length ).toEqual c.length
    
    # expect( flip(d).length ).toEqual d.length
    # NO; We do not guarantee arity for length > 3
    
  it 'should be self-currying', ->
    expect( flip(b)(1)(2) ).toEqual [2, 1]
    expect( flip(c)(1)(2)(3) ).toEqual [3, 2, 1]
    expect( flip(d)(1)(2)(3)(4) ).toEqual [4, 3, 2, 1]

================================================
FILE: spec/decorating-classes.spec.coffee
================================================
{classDecorator, mixin, fluent} = require('../lib/allong.es.js').allong.es

class Todo
  constructor: (name) ->
    self = if this instanceof Todo
             this
           else
             new Todo()
    self.name = name or 'Untitled'
    self.done = false
  do: fluent -> this.done = true
  undo: fluent -> this.done = false
  
describe "features", ->
  it "should include mixin", ->
    expect(typeof mixin).toEqual 'function'
  it "should include classDecorator", ->
    expect(typeof classDecorator).toEqual 'function'

describe "classDecorator", ->

  AndColourCoded = classDecorator
    setColourRGB: (r, g, b) ->
      @colourCode = { r, g, b }
      this
    getColourRGB: -> @colourCode

  ColourTodo = AndColourCoded Todo

  todo = new ColourTodo('Use More Decorators')
        .setColourRGB(0, 255, 0)

  it "should set the name correctly", ->
    expect( todo.name ).toEqual "Use More Decorators"

  it "should set the colour code correctly", ->
    expect( todo.getColourRGB() ).toEqual
      r: 0
      g: 255
      b: 0

describe "mixin", ->

  LocationAware = mixin
    setLocation: (@location) -> this
    getLocation: -> @location
    
  describe "Using context", ->

    it "should add location to the existing class's prototype", ->
      LocationAware.call(Todo.prototype)
      expect( typeof Todo.prototype.setLocation ).toEqual 'function'
      expect( typeof Todo.prototype.getLocation ).toEqual 'function'
      
  describe "Using a parameter", ->
    
    a = { a: 'a' }
    b = { b: 'b' }
    c = { c: 'b' }
    
    it "should not add the location to the context", ->
      LocationAware.call(a, b)
      expect( a.setLocation ).toBeUndefined()
      expect( a.getLocation ).toBeUndefined()
      expect( typeof b.setLocation ).toEqual 'function'
      expect( typeof b.getLocation ).toEqual 'function'
      
    it "should add the location when called normally", ->
      LocationAware(c)
      expect( typeof c.setLocation ).toEqual 'function'
      expect( typeof c.getLocation ).toEqual 'function'
    

================================================
FILE: spec/folding.spec.coffee
================================================
{mapWith, deepMapWith, filter} = require('../lib/allong.es.js').allong.es

square = (n) -> n * n

describe 'mapWith', ->
  
  it 'should square some numbers', ->
    
    expect( mapWith(square)([1..5]) ).toEqual [1, 4, 9, 16, 25]

describe 'deepMapWith', ->
  
  it 'should square some numbers', ->
    
    expect( deepMapWith(square)([1, [2..4], 5]) ).toEqual [1, [4, 9, 16], 25]
    
describe "filter", ->
  
  it "should find odd numbers", ->
    
    expect( filter([1, 2, 3, 4, 5, 6], '% 2 === 1') ).toEqual [1, 3, 5]
  
  it "should be self-currying", ->
    
    expect( filter([1, 2, 3, 4, 5, 6])('% 2 === 0') ).toEqual [2, 4, 6]

================================================
FILE: spec/iterators.spec.coffee
================================================
{iterators: {slice, drop, take, accumulate, accumulateWithReturn, 
             fold, map, filter, FlatArrayIterator, RecursiveArrayIterator,
             unfold, unfoldWithReturn}} = require('../lib/allong.es.js').allong.es

describe "FlatArrayIterator", ->
  
  it "should iterate over a flat array", ->
    i = FlatArrayIterator([1, 2, 3, 4, 5])
    expect( i() ).toEqual(1)
    expect( i() ).toEqual(2)
    expect( i() ).toEqual(3)
    expect( i() ).toEqual(4)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()
    
  it "should not iterate down through an array", ->
    i = FlatArrayIterator([1, [2, 3, [4]], 5])
    expect( i() ).toEqual(1)
    expect( i() ).not.toEqual(2)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()
  
  it "should have no values given an empty array", ->
    i = FlatArrayIterator([])
    expect( i() ).toBeUndefined()
  
  it "should have a values given an empty tree", ->
    i = FlatArrayIterator([[], [[]]])
    expect( i() ).not.toBeUndefined()

describe "RecursiveArrayIterator", ->
  
  it "should have no values given an empty array", ->
    i = RecursiveArrayIterator([])
    expect( i() ).toBeUndefined()
  
  it "should have no values given an empty tree", ->
    i = RecursiveArrayIterator([[], [[]]])
    expect( i() ).toBeUndefined()
  
  it "should iterate over a flat array", ->
    i = RecursiveArrayIterator([1, 2, 3, 4, 5])
    expect( i() ).toEqual(1)
    expect( i() ).toEqual(2)
    expect( i() ).toEqual(3)
    expect( i() ).toEqual(4)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()
    
  it "should also iterate down through an array", ->
    i = RecursiveArrayIterator([1, [2, 3, [4]], 5])
    expect( i() ).toEqual(1)
    expect( i() ).toEqual(2)
    expect( i() ).toEqual(3)
    expect( i() ).toEqual(4)
    expect( i() ).toEqual(5)
    expect( i() ).toBeUndefined()

sum = (x, y) -> x + y

describe "fold", ->
  
    describe "with a seed", ->
  
      it "should fold an iterator with many elements", ->
        expect( fold(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum, 0) ).toEqual(15)
  
      it "should fold an iterator with one element", ->
        expect( fold(RecursiveArrayIterator([[[4], []]]), sum, 42) ).toEqual(46)
  
      it "should fold an empty iterator", ->
        expect( fold(RecursiveArrayIterator([[], [[]]]), sum, 42) ).toEqual(42)
      
    describe "without a seed", ->
      
      it "should fold an array with two or more elements", ->
        expect( fold(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum) ).toEqual(15)
      
      it "should fold an array with one element", ->
        expect( fold(RecursiveArrayIterator([[[4], []]]), sum) ).toEqual(4)
      
      it "should fold an array with no elements", ->
        expect( fold(RecursiveArrayIterator([[[], []]]), sum) ).toBeUndefined()

describe "accumulate", ->
  
    describe "with a seed", ->
  
      it "should map an iterator with many elements", ->
        i = accumulate(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum, 0)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(3)
        expect( i() ).toEqual(6)
        expect( i() ).toEqual(10)
        expect( i() ).toEqual(15)
        expect( i() ).toBeUndefined()
  
      it "should map an iterator with one element", ->
        i = accumulate(RecursiveArrayIterator([[[4], []]]), sum, 42)
        expect( i() ).toEqual(46)
        expect( i() ).toBeUndefined()
  
      it "should map an empty iterator", ->
        i = accumulate(RecursiveArrayIterator([[[], []]]), sum, 42)
        expect( i() ).toBeUndefined()
      
    describe "without a seed", ->
  
      it "should map an iterator with many elements", ->
        i = accumulate(RecursiveArrayIterator([1, [2, 3, [4]], 5]), sum)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(3)
        expect( i() ).toEqual(6)
        expect( i() ).toEqual(10)
        expect( i() ).toEqual(15)
        expect( i() ).toBeUndefined()
  
      it "should map an iterator with one element", ->
        i = accumulate(RecursiveArrayIterator([[[4], []]]), sum)
        expect( i() ).toEqual(4)
        expect( i() ).toBeUndefined()
  
      it "should map an empty iterator", ->
        i = accumulate(RecursiveArrayIterator([[[], []]]), sum)
        expect( i() ).toBeUndefined()

square = (x) -> x*x

describe "map", ->
  
      it "should map an iterator with many elements", ->
        i = map(RecursiveArrayIterator([1, [2, 3, [4]], 5]), square)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(4)
        expect( i() ).toEqual(9)
        expect( i() ).toEqual(16)
        expect( i() ).toEqual(25)
        expect( i() ).toBeUndefined()
  
      it "should map an iterator with one element", ->
        i = map(RecursiveArrayIterator([[[4], []]]), square)
        expect( i() ).toEqual(16)
        expect( i() ).toBeUndefined()
  
      it "should map an empty iterator", ->
        i = map(RecursiveArrayIterator([[[], []]]), square)
        expect( i() ).toBeUndefined()

odd = (x) -> x % 2 is 1

describe "filter", ->
  
      it "should filter an iterator with many elements", ->
        i = filter(RecursiveArrayIterator([1, [2, 3, [4]], 5]), odd)
        expect( i() ).toEqual(1)
        expect( i() ).toEqual(3)
        expect( i() ).toEqual(5)
        expect( i() ).toBeUndefined()
  
      it "should filter an iterator with one element", ->
        i = filter(RecursiveArrayIterator([[[4], []]]), odd)
        expect( i() ).toBeUndefined()
  
      it "should filter an empty iterator", ->
        i = filter(RecursiveArrayIterator([[[], []]]), odd)
        expect( i() ).toBeUndefined()
  
      it "should filter an iterator with no matches", ->
        i = filter(FlatArrayIterator([2, 4, 6, 8, 10]), odd)
        expect( i() ).toBeUndefined()

describe "slice", ->
  
  describe "with two parameter", ->
    
    it "should return an identity iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
    
    it "should return a trailing iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
      
    it "should return an empty iterator when out of range", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 5)
      expect( i() ).toBeUndefined()
  
  describe "with three parameters", ->
    
    it "should return an identity iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0, 5)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0, 99)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
    
    it "should return a leading iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 0, 4)
      expect( i() ).toEqual 1
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toBeUndefined()
    
    it "should return a trailing iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 4)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 99)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toEqual 5
      expect( i() ).toBeUndefined()
    
    it "should return an inner iterator", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 3)
      expect( i() ).toEqual 2
      expect( i() ).toEqual 3
      expect( i() ).toEqual 4
      expect( i() ).toBeUndefined()
      
    it "should return an empty iterator when given a zero length", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 1, 0)
      expect( i() ).toBeUndefined()
      
    it "should return an empty iterator when out of range", ->
      i = slice(FlatArrayIterator([1, 2, 3, 4, 5]), 5, 1)
      expect( i() ).toBeUndefined()
      
describe "drop", ->
  
  it "should drop the number of items dropped", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]), 2)
    expect( i() ).toEqual 3
    expect( i() ).toEqual 4
    expect( i() ).toEqual 5
    expect( i() ).toBeUndefined()
  
  it "should handle overdropping", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]), 99)
    expect( i() ).toBeUndefined()
    
  it "should handle underdropping", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]), 0)
    expect( i() ).toEqual 1
    expect( i() ).toEqual 2
    expect( i() ).toEqual 3
    expect( i() ).toEqual 4
    expect( i() ).toEqual 5
    expect( i() ).toBeUndefined()
    
  it "should default to one", ->
    i = drop(FlatArrayIterator([1, 2, 3, 4, 5]))
    expect( i() ).toEqual 2
    expect( i() ).toEqual 3
    expect( i() ).toEqual 4
    expect( i() ).toEqual 5
    expect( i() ).toBeUndefined()
    
describe "accumulateWithReturn", ->
  
  it "should pass the state and result in a pair", ->
    i = accumulateWithReturn(FlatArrayIterator([1, 2, 3, 4, 5]), (state, element) ->
      [state + element, 'Total is ' + (state + element)]
    , 0);
    
    expect( i() ).toEqual 'Total is 1'
    expect( i() ).toEqual 'Total is 3'
    expect( i() ).toEqual 'Total is 6'
    expect( i() ).toEqual 'Total is 10'
    expect( i() ).toEqual 'Total is 15'
    
describe "unfold", ->
  
  it "should unfold and include the seed", ->
    i = unfold 0, (n) -> n + 1
    
    expect( i() ).toEqual 0
    expect( i() ).toEqual 1
    expect( i() ).toEqual 2
  
  it "should not unfold without a seed", ->
    i = unfold undefined, (n) -> n + 1
    
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined

describe "unfoldWithReturn", ->
  
  it "should unfold and throw off a value", ->
    i = unfoldWithReturn 1, (n) -> [n + 1, n*n]
    
    expect( i() ).toEqual 1
    expect( i() ).toEqual 4
    expect( i() ).toEqual 9
    expect( i() ).toEqual 16
  
  it "should halt if it returns undefined", ->
    i = unfoldWithReturn 1, (n) ->
      [n + 1, if n is 1 then undefined else n * n]
    
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
    expect( i() ).toEqual undefined
  
  it "should halt if the state becomes undefined", ->
    i = unfoldWithReturn 1, (n) ->
      [(if n is 3 then undefined else n + 1), (if n is undefined then 100 else n * n)]
    
    expect( i() ).toEqual 1
    expect( i() ).toEqual 4
    expect( i() ).toEqual 9
    expect( i() ).toEqual undefined

================================================
FILE: spec/map-arguments-with.spec.coffee
================================================
{mapArgumentsWith, variadic, maybe} = require('../lib/allong.es').allong.es

myArgs = variadic (a) -> a 
double = (n) -> n * 2
plus1 = (n) -> n + 1

describe "mapArgumentsWith", ->
  
  it "should map some simple integers", ->
    expect( mapArgumentsWith(double, myArgs)(1, 2, 3) ).toEqual [2, 4, 6]
    
  it "should curry", ->
    expect( mapArgumentsWith(double)(myArgs)(1, 2, 3) ).toEqual [2, 4, 6]
    
  it "should construct nice decorators", ->
    doubleYourArguments = mapArgumentsWith(double)
    expect( (doubleYourArguments myArgs)(1, 2, 3) ).toEqual [2, 4, 6]
    
describe "self-mapping", ->
  
  describe "maybe", ->
    
    it "should return a function for a function", ->
      expect( maybe(double)(2) ).toEqual 4
      expect( maybe(double)(undefined) ).toBeUndefined()
      
    it "should return an array for multiple items", ->
      expect( maybe(double, plus1)[0](4) ).toEqual 8
      expect( maybe(double, plus1)[0](undefined) ).toBeUndefined()
      expect( maybe(double, plus1)[1](4) ).toEqual 5
      expect( maybe(double, plus1)[1](undefined) ).toBeUndefined()
  
  

================================================
FILE: spec/sequence-objects.spec.coffee
================================================
{sequence, flatten} = require('../lib/allong.es').allong.es

WithLogging =
  chain: (valueAndLogList, fn) ->
    value = valueAndLogList[0]
    logList = valueAndLogList[1]
    resultAndLogList = fn(value)
    result = resultAndLogList[0]
    resultLogList = flatten(logList.concat(resultAndLogList[1]))
    [result, resultLogList]
  of: (argument) -> [argument, []]

double = (number) ->
  result = number * 2
  [result, ['' + number + ' * 2 = ' + result]]

plus1 = (number) ->
  result = number + 1
  [result, ['' + number + ' + 1 = ' + result]]  

describe "sequence with object definitions", ->
  
  it "should work for some simple math", ->
    expect( sequence(WithLogging, double, plus1 )(2) ).toEqual [5, ['2 * 2 = 4', '4 + 1 = 5']]

================================================
FILE: spec/sequence.advanced.spec.coffee
================================================
{sequence, sequence: {Identity, Maybe, Writer, List, Then, Callback}} = require('../lib/allong.es.js').allong.es

describe "Sequence", ->
  
  describe "Naive", ->

    double = (n) -> n + n
    plusOne = (n) -> n + 1
  
    it "should be a thing", ->
      expect( sequence ).not.toBeNull()
    
    it "should return a function when given a function", ->
      expect( sequence(double) ).not.toBeNull()
  
    it "should do a single function", ->
      expect( sequence(double)(3) ).toEqual 6
  
    it "should do two functions", ->
      expect( sequence(double, plusOne)(3) ).toEqual 7
    
  describe "Identity", ->

    double = (n) -> n + n
    plusOne = (n) -> n + 1
    
    it "should be a thing", ->
      expect( Identity ).not.toBeNull()    
  
    it "should do a single function", ->
      expect( sequence(Identity, double)(3) ).toEqual 6
  
    it "should do two functions", ->
      expect( sequence(Identity, double, plusOne)(3) ).toEqual 7
    
  describe "Maybe", ->

    double = (n) -> n + n
    plusOne = (n) -> n + 1
  
    it "should pass numbers through", ->
      expect( sequence(Maybe, double, plusOne)(3) ).toEqual 7
  
    it "should pass null through", ->
      expect( sequence(Maybe, double, plusOne)(null) ).toBeNull()
  
    it "should pass undefined through", ->
      expect( sequence(Maybe, double, plusOne)(undefined) ).toBeUndefined()
    
    it "should short-circuit", ->
      expect( sequence(Maybe, double, ((x) ->), plusOne)(undefined) ).toBeUndefined()
      
  describe "Writer", ->
  
    parity = (n) ->
      [
        n
        if n % 2 is 0 then 'even' else 'odd'
      ]
    
    space = (n) ->
      [
        n
        ' '
      ]
    
    size = (n) ->
      [
        n
        if n < 10 then 'small' else 'normal'
      ]
  
    it "should accumulate writes", ->
      expect( sequence(Writer, parity, space, size)(5) ).toEqual [5, 'odd small']
    
  describe 'List', ->
  
    oneToN = (n) ->
      [1..n]
  
    nToOne = (n) ->
      [n..1]
    
    it "should handle two levels of lists", ->
      expect( sequence(List, oneToN, nToOne)(3) ).toEqual [1, 2, 1, 3, 2, 1]

================================================
FILE: spec/sequence.callback.spec.coffee
================================================
{sequence, sequence: {Identity, Maybe, Writer, List, Then, Callback}} = require('../lib/allong.es.js').allong.es

describe "Sequence", ->
      
  describe "Callback", ->
    
    identity = (v) -> v
      
    describe "with one parameter", ->

      double = (v, c) -> c(v * 2)
      plus1 = (v, c) -> c(v + 1)
  
      it "should work for a double", ->
    
        expect( sequence(Callback, double)(42, identity) ).toBe 84
  
      it "should work for a double double", ->
    
        expect( sequence(Callback, double, double)(2, identity) ).toBe 8
  
      it "should work for a double plus1 double", ->
    
        expect( sequence(Callback, double, plus1, double)(2, identity) ).toBe 10
        
    describe "with multiple parameters", ->
      
      argsToArray = (args..., callback) ->
        callback(args)
      
      argsToArgs = (args..., callback) ->
        callback(args...)
      
      it "should work for a singleton", ->
        expect( sequence(Callback, argsToArgs, argsToArray)(1, identity) ).toEqual [1]
      
      it "should work for a doubleton", ->
        expect( sequence(Callback, argsToArgs, argsToArray)(1, 2, identity) ).toEqual [1, 2]

================================================
FILE: spec/sequence.decorataors.spec.coffee
================================================
{sequence, maybe, andand, oror} = require('../lib/allong.es.js').allong.es

describe "Sequence Decorations", ->
  
  identity = (fn) -> fn
  identitywrapper = (fn) -> (value) -> fn(value)
  double = (n) -> n + n
  plusOne = (n) -> n + 1
  nothing = (x) ->
    
  describe "maybe", ->
    
    it "for a number", ->
      expect(
        sequence(
          maybe(
            double, 
            plusOne))(3)
      ).toEqual sequence(maybe(double), maybe(plusOne))(3)
      
    it "for a null", ->
      expect(
        sequence(
          maybe(
            double, 
            plusOne))(null)
      ).toEqual sequence(maybe(double), maybe(plusOne))(null)
      
    it "for undefined", ->
      expect(
        sequence(
          maybe(
            double, 
            plusOne))(undefined)
      ).toEqual sequence(maybe(double), maybe(plusOne))(undefined)
    
    it "should short-circuit", ->
      expect(
        sequence(
          maybe(
            double, 
            nothing,
            plusOne))(10)
      ).toEqual sequence(maybe(double), maybe(nothing), maybe(plusOne))(10)

================================================
FILE: spec/sequence.then.spec.coffee
================================================
Promise = require 'promise'

{sequence} = require('../lib/allong.es.js').allong.es

# also demonstrates string shorthand

describe "Then", ->
    
  double = (value) ->
    new Promise (resolve, reject) ->
      resolve(value * 2)
      
  success = undefined
  failure = undefined
      
  beforeEach ->
      
    success = undefined
    failure = undefined
  
  describe "for a doubling promise", ->
  
    it "should work asynchronously", (done) ->
      
      sequencedPromise = sequence('Then', double)(3)
            
      sequencedPromise.then ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done())
            
    afterEach ->    
      expect( success ).toEqual 6
      expect( failure ).toBeUndefined()
  
  describe "for a double double", ->
  
    it "should work asynchronously", (done) ->
      
      sequencedPromise = sequence('Then', double, double)(2)
            
      sequencedPromise.then ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done())
            
    afterEach ->    
      expect( success ).toEqual 8
      expect( failure ).toBeUndefined()
  
  describe "for a double fail double", ->
  
    it "should fail forward", (done) ->
      
      failer = (value) ->
        new Promise (resolve, reject) ->
          reject 'sorry, old chap'
      
      sequencedPromise = sequence('Then', double, failer, double)(2)
            
      sequencedPromise.then( ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done()))
            
    afterEach ->    
      expect( success ).toBeUndefined()
      expect( failure ).toBe 'sorry, old chap'
      

================================================
FILE: spec/sequence.transformers.spec.coffee
================================================
Promise = require 'promise'

{sequence, sequence: {Then, Callback, fn2Then, fn2Callback, callback2Then, then2Callback}} = require('../lib/allong.es.js').allong.es

describe "fn2Then", ->
  
  plus1 = (value) -> value + 1
    
  double = (value) ->
    new Promise (resolve, reject) ->
      resolve(value * 2)
      
  success = undefined
  failure = undefined
      
  beforeEach ->
      
    success = undefined
    failure = undefined
  
  describe "for a single function", ->
  
    it "should work ", (done) ->
      
      sequencedPromise = sequence(Then,
        double,
        fn2Then(plus1),
        double
      )(1)
            
      sequencedPromise.then ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done())
            
    afterEach ->    
      expect( success ).toEqual 6
      expect( failure ).toBeUndefined()

describe "callback2Then", ->
  
  plus1 = (value, callback) ->
    callback(value + 1)
    
  double = (value) ->
    new Promise (resolve, reject) ->
      resolve(value * 2)
      
  success = undefined
  failure = undefined
      
  beforeEach ->
      
    success = undefined
    failure = undefined
  
  describe "for a single function", ->
  
    it "should work ", (done) ->
      
      sequencedPromise = sequence(Then,
        double,
        callback2Then(plus1),
        double
      )(1)
            
      sequencedPromise.then ((value) ->
        success = value
        done()),
          ((reason) ->
            failure = reason
            done())
            
    afterEach ->    
      expect( success ).toEqual 6
      expect( failure ).toBeUndefined()

describe "fn2Callback", ->
  
  plus1 = (value) -> value + 1
    
  double = (value, callback) ->
    callback(value * 2)
    
  identity = (n) -> n
  
  it "should work for a double plus1 double", ->

    expect(
      sequence(Callback, 
        double, 
        fn2Callback(plus1), 
        double
      )(2, identity) ).toBe 10

describe "then2Callback", ->
  
  plus1 = (value) ->
    new Promise (resolve, reject) ->
      resolve(value + 1)
    
  double = (value, callback) ->
    callback(value * 2)
    
  identity = (n) -> n
      
  success = undefined
  failure = undefined
      
  beforeEach ->
      
    success = undefined
    failure = undefined
  
  describe "for a single function", ->
  
    it "should work ", (done) ->

      sequence(Callback, 
        double, 
        then2Callback(plus1), 
        double
      )(2, (value) -> 
        success = value
        done())
            
    afterEach ->    
      expect( success ).toEqual 10
      expect( failure ).toBeUndefined()

================================================
FILE: spec/trampoline.spec.coffee
================================================
{trampoline, tailCall} = require('../lib/allong.es.js').allong.es

describe "trampolining", ->

  depth = 32768

  it "should execute a clean even/odd to #{depth} levels", ->
  
    even = trampoline (n) ->
      if n is 0
        true
      else
        tailCall odd, n - 1
  
    odd = trampoline (n) ->
      if n is 0
        false
      else
        tailCall even, n - 1
  
    expect( even(0) ).toEqual true
    expect( even(1) ).toEqual false
    expect( even(2) ).toEqual true
    expect( even(3) ).toEqual false
    expect(-> even(depth) ).not.toThrow()
    
  it "should allow tail calling a co-recursive non-trampolined function too", ->
  
    even2 = trampoline (n) ->
      if n is 0
        true
      else
        tailCall odd2, n - 1
  
    odd2 = (n) ->
      if n is 0
        false
      else
        even2 n - 1
  
    expect(-> even2(1000) ).not.toThrow()
    expect( even2(100) ).toEqual true
    expect( even2(101) ).toEqual false
Download .txt
gitextract_ii3k_5xa/

├── .gitignore
├── Gruntfile.coffee
├── README.md
├── _site/
│   ├── Gruntfile.coffee
│   ├── README.md
│   ├── functions.txt
│   ├── lib/
│   │   └── allong.es.js
│   ├── package.json
│   ├── release_notes.md
│   └── spec/
│       ├── apply.spec.coffee
│       ├── arity.spec.coffee
│       ├── basic.spec.coffee
│       ├── callbacks.spec.coffee
│       ├── core.spec.coffee
│       ├── decorating-classes.spec.coffee
│       ├── folding.spec.coffee
│       ├── iterators.spec.coffee
│       ├── promises.spec.coffee
│       └── trampoline.spec.coffee
├── hello/
│   └── world.txt
├── lib/
│   └── allong.es.js
├── package.json
└── spec/
    ├── andand-oror.spec.coffee
    ├── apply.spec.coffee
    ├── arity.spec.coffee
    ├── core.spec.coffee
    ├── decorating-classes.spec.coffee
    ├── folding.spec.coffee
    ├── iterators.spec.coffee
    ├── map-arguments-with.spec.coffee
    ├── sequence-objects.spec.coffee
    ├── sequence.advanced.spec.coffee
    ├── sequence.callback.spec.coffee
    ├── sequence.decorataors.spec.coffee
    ├── sequence.then.spec.coffee
    ├── sequence.transformers.spec.coffee
    └── trampoline.spec.coffee
Download .txt
SYMBOL INDEX (111 symbols across 2 files)

FILE: _site/lib/allong.es.js
  function to_function (line 33) | function to_function (str) {
  function functionalize (line 69) | function functionalize (fn) {
  function extend (line 87) | function extend () {
  function rotate (line 108) | function rotate (array, n) {
  function reverse (line 133) | function reverse (array) {
  function invokeImmediately (line 142) | function invokeImmediately (fn) { return fn(); }
  function oldVariadic (line 168) | function oldVariadic (fn) {
  function unary (line 246) | function unary (fn) {
  function binary (line 255) | function binary (fn) {
  function ternary (line 267) | function ternary (fn) {
  function quaternary (line 282) | function quaternary (fn) {
  function curryWithLeftAndRight (line 309) | function curryWithLeftAndRight (fn, leftArgs, rightArgs) {
  function selfCurrying (line 340) | function selfCurrying(fn) {
  function nullary (line 415) | function nullary (fn) {
  function unary (line 422) | function unary (fn) {
  function binary (line 431) | function binary (fn) {
  function ternary (line 443) | function ternary (fn) {
  function quaternary (line 458) | function quaternary (fn) {
  function callWithLeftFlipped (line 485) | function callWithLeftFlipped (fn, leftArgs) {
  function urApply (line 557) | function urApply (fn, args) {
  function urApplyLeft (line 576) | function urApplyLeft (fn, leftArgs) {
  function urApplyRight (line 599) | function urApplyRight (fn, rightArgs) {
  function args (line 667) | function args (arity) {
  function deepMap (line 738) | function deepMap (fn) {
  function maybe (line 773) | function maybe (fn) {
  function tap (line 790) | function tap (value, fn) {
  function fluent (line 806) | function fluent (fn) {
  function tee (line 823) | function tee (decoration) {
  function once (line 831) | function once (fn) {
  function memoized (line 857) | function memoized (fn, keymaker) {
  function mixin (line 888) | function mixin (decoration) {
  function classDecorator (line 900) | function classDecorator (decoration) {
  function bind (line 926) | function bind (fn, context, force) {
  function invoke (line 943) | function invoke (fn) {
  function get (line 952) | function get (object, getName) {
  function getWith (line 959) | function getWith (getName, object) {
  function Thunk (line 991) | function Thunk (closure) {
  function trampoline (line 1002) | function trampoline (fn) {
  function fold (line 1038) | function fold (iter, binaryFn, seed) {
  function unfold (line 1057) | function unfold (seed, unaryFn) {
  function unfoldWithReturn (line 1073) | function unfoldWithReturn (seed, unaryFn) {
  function accumulate (line 1091) | function accumulate (iter, binaryFn, initial) {
  function accumulateWithReturn (line 1108) | function accumulateWithReturn (iter, binaryFn, initial) {
  function map (line 1130) | function map (iter, unaryFn) {
  function select (line 1143) | function select (iter, unaryPredicateFn) {
  function reject (line 1158) | function reject (iter, unaryPredicateFn) {
  function find (line 1165) | function find (iter, unaryPredicateFn) {
  function slice (line 1170) | function slice (iter, numberToDrop, numberToTake) {
  function take (line 1189) | function take (iter, numberToTake) {
  function FlatArrayIterator (line 1193) | function FlatArrayIterator (array) {
  function RecursiveArrayIterator (line 1200) | function RecursiveArrayIterator (array) {
  function K (line 1229) | function K (value) {
  function upRange (line 1235) | function upRange (from, to, by) {
  function downRange (line 1250) | function downRange (from, to, by) {
  function range (line 1265) | function range (from, to, by) {
  function Supervisor (line 1326) | function Supervisor(methods) {

FILE: lib/allong.es.js
  function to_function (line 51) | function to_function (str) {
  function functionalize (line 87) | function functionalize (fn) {
  function extend (line 105) | function extend () {
  function rotate (line 126) | function rotate (array, n) {
  function reverse (line 151) | function reverse (array) {
  function flatten (line 158) | function flatten (array) {
  function invokeImmediately (line 181) | function invokeImmediately (fn) { return fn(); }
  function oldVariadic (line 207) | function oldVariadic (fn) {
  function unary (line 285) | function unary (fn) {
  function binary (line 294) | function binary (fn) {
  function ternary (line 306) | function ternary (fn) {
  function quaternary (line 321) | function quaternary (fn) {
  function curryWithLeftAndRight (line 348) | function curryWithLeftAndRight (fn, leftArgs, rightArgs) {
  function selfCurrying (line 379) | function selfCurrying(fn) {
  function nullary (line 459) | function nullary (fn) {
  function unary (line 466) | function unary (fn) {
  function binary (line 475) | function binary (fn) {
  function ternary (line 487) | function ternary (fn) {
  function quaternary (line 502) | function quaternary (fn) {
  function callWithLeftFlipped (line 529) | function callWithLeftFlipped (fn, leftArgs) {
  function urApply (line 601) | function urApply (fn, args) {
  function urApplyLeft (line 620) | function urApplyLeft (fn, leftArgs) {
  function urApplyRight (line 643) | function urApplyRight (fn, rightArgs) {
  function args (line 711) | function args (arity) {
  function mapIfMany (line 824) | function mapIfMany (fn) {
  function tap (line 852) | function tap (value, fn) {
  function fluent (line 868) | function fluent (fn) {
  function tee (line 885) | function tee (decoration) {
  function once (line 893) | function once (fn) {
  function memoized (line 919) | function memoized (fn, keymaker) {
  function mixin (line 950) | function mixin (decoration) {
  function classDecorator (line 962) | function classDecorator (decoration) {
  function bind (line 988) | function bind (fn, context, force) {
  function invoke (line 1005) | function invoke (fn) {
  function get (line 1014) | function get (object, getName) {
  function getWith (line 1021) | function getWith (getName, object) {
  function Thunk (line 1053) | function Thunk (closure) {
  function trampoline (line 1064) | function trampoline (fn) {
  function fold (line 1100) | function fold (iter, binaryFn, seed) {
  function unfold (line 1119) | function unfold (seed, unaryFn) {
  function unfoldWithReturn (line 1135) | function unfoldWithReturn (seed, unaryFn) {
  function accumulate (line 1153) | function accumulate (iter, binaryFn, initial) {
  function accumulateWithReturn (line 1170) | function accumulateWithReturn (iter, binaryFn, initial) {
  function map (line 1192) | function map (iter, unaryFn) {
  function select (line 1205) | function select (iter, unaryPredicateFn) {
  function reject (line 1220) | function reject (iter, unaryPredicateFn) {
  function find (line 1227) | function find (iter, unaryPredicateFn) {
  function slice (line 1232) | function slice (iter, numberToDrop, numberToTake) {
  function take (line 1251) | function take (iter, numberToTake) {
  function FlatArrayIterator (line 1255) | function FlatArrayIterator (array) {
  function RecursiveArrayIterator (line 1262) | function RecursiveArrayIterator (array) {
  function K (line 1291) | function K (value) {
  function upRange (line 1297) | function upRange (from, to, by) {
  function downRange (line 1312) | function downRange (from, to, by) {
  function range (line 1327) | function range (from, to, by) {
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (199K chars).
[
  {
    "path": ".gitignore",
    "chars": 10,
    "preview": ".DS_Store\n"
  },
  {
    "path": "Gruntfile.coffee",
    "chars": 483,
    "preview": "module.exports = (grunt) ->\n\n  grunt.initConfig \n    pkg: grunt.file.readJSON('package.json'),\n    uglify:\n      options"
  },
  {
    "path": "README.md",
    "chars": 16873,
    "preview": "# `allong.es`\n\nThe `allong.es` library is a collection of functions designed to facilitate writing JavaScript and/or Cof"
  },
  {
    "path": "_site/Gruntfile.coffee",
    "chars": 483,
    "preview": "module.exports = (grunt) ->\n\n  grunt.initConfig \n    pkg: grunt.file.readJSON('package.json'),\n    uglify:\n      options"
  },
  {
    "path": "_site/README.md",
    "chars": 16875,
    "preview": "# `allong.es`\n\nThe `allong.es` library is a collection of functions designed to facilitate writing JavaScript and/or Cof"
  },
  {
    "path": "_site/functions.txt",
    "chars": 2792,
    "preview": "{ variadic: [Function],\n  unvariadic: [Function: unvariadic],\n  curryWithLeftAndRight: [Function: curryWithLeftAndRight]"
  },
  {
    "path": "_site/lib/allong.es.js",
    "chars": 36351,
    "preview": "/*! http://github.com/raganwald/allong.es (c) 2012-2013 Reg Braithwaite MIT Licensed */\n(function (root) {\n  \n  // Setup"
  },
  {
    "path": "_site/package.json",
    "chars": 628,
    "preview": "{\n  \"author\": \"Reg Braithwaite <raganwald@gmail.com> (http://braythwayt.com)\",\n  \"name\": \"allong.es\",\n  \"description\": \""
  },
  {
    "path": "_site/release_notes.md",
    "chars": 284,
    "preview": "# Release Notes\n\n1. Currying is implicit\n2. \"With\" and \"This\" naming conventions, e.g. `pluckWith` is the should of `plu"
  },
  {
    "path": "_site/spec/apply.spec.coffee",
    "chars": 7039,
    "preview": "{ callRight, applyNow, callNow, applyNowFlipped, \n  call, applyLeftNow, callLeftNow, args, applyLeftNowWith,\n  applyRigh"
  },
  {
    "path": "_site/spec/arity.spec.coffee",
    "chars": 2088,
    "preview": "{unvariadic, variadic, curry} = require('../lib/allong.es.js').allong.es\n\nnumberOfArgs = -> arguments.length\nthreeArgume"
  },
  {
    "path": "_site/spec/basic.spec.coffee",
    "chars": 1883,
    "preview": "{Sequence, Sequence: {sequence}} = require('../lib/allong.es.js').allong.es\n\ndouble = (n) -> n + n\nplusOne = (n) -> n + "
  },
  {
    "path": "_site/spec/callbacks.spec.coffee",
    "chars": 649,
    "preview": "{Sequence, Sequence: {do}} = require('../lib/allong.es.js').allong.es\n\ndouble = (v, c) -> c(v * 2)\nplus1 = (v, c) -> c(v"
  },
  {
    "path": "_site/spec/core.spec.coffee",
    "chars": 3725,
    "preview": "# unary, binary, ternary, variadic, compose, sequence???\n{defaults, mapWith, getWith, filterWith, compose, sequence, var"
  },
  {
    "path": "_site/spec/decorating-classes.spec.coffee",
    "chars": 2041,
    "preview": "{classDecorator, mixin, fluent} = require('../lib/allong.es.js').allong.es\n\nclass Todo\n  constructor: (name) ->\n    self"
  },
  {
    "path": "_site/spec/folding.spec.coffee",
    "chars": 639,
    "preview": "{mapWith, deepMapWith, filter} = require('../lib/allong.es.js').allong.es\n\nsquare = (n) -> n * n\n\ndescribe 'mapWith', ->"
  },
  {
    "path": "_site/spec/iterators.spec.coffee",
    "chars": 11087,
    "preview": "{iterators: {slice, drop, take, accumulate, accumulateWithReturn, \n             fold, map, filter, FlatArrayIterator, Re"
  },
  {
    "path": "_site/spec/promises.spec.coffee",
    "chars": 1738,
    "preview": "Promise = require 'promise'\n{Sequence, Sequence: {do}} = require('../lib/allong.es.js').allong.es\n\ndescribe \"do\", ->\n   "
  },
  {
    "path": "_site/spec/trampoline.spec.coffee",
    "chars": 954,
    "preview": "{trampoline, tailCall} = require('../lib/allong.es.js').allong.es\n\ndescribe \"trampolining\", ->\n\n  depth = 32768\n\n  it \"s"
  },
  {
    "path": "hello/world.txt",
    "chars": 6,
    "preview": "Hello!"
  },
  {
    "path": "lib/allong.es.js",
    "chars": 41358,
    "preview": "/*! http://github.com/raganwald/allong.es (c) 2012-2013 Reg Braithwaite MIT Licensed */\n(function (root) {\n  \n  // Setup"
  },
  {
    "path": "package.json",
    "chars": 628,
    "preview": "{\n  \"author\": \"Reg Braithwaite <raganwald@gmail.com> (http://braythwayt.com)\",\n  \"name\": \"allong.es\",\n  \"description\": \""
  },
  {
    "path": "spec/andand-oror.spec.coffee",
    "chars": 1457,
    "preview": "{andand, oror} = require('../lib/allong.es').allong.es\n\nT = () -> true\nF = () -> false\nN = () -> null\nU = () -> undefime"
  },
  {
    "path": "spec/apply.spec.coffee",
    "chars": 7039,
    "preview": "{ callRight, applyNow, callNow, applyNowFlipped, \n  call, applyLeftNow, callLeftNow, args, applyLeftNowWith,\n  applyRigh"
  },
  {
    "path": "spec/arity.spec.coffee",
    "chars": 2088,
    "preview": "{unvariadic, variadic, curry} = require('../lib/allong.es.js').allong.es\n\nnumberOfArgs = -> arguments.length\nthreeArgume"
  },
  {
    "path": "spec/core.spec.coffee",
    "chars": 3725,
    "preview": "# unary, binary, ternary, variadic, compose, sequence???\n{defaults, mapWith, getWith, filterWith, compose, sequence, var"
  },
  {
    "path": "spec/decorating-classes.spec.coffee",
    "chars": 2041,
    "preview": "{classDecorator, mixin, fluent} = require('../lib/allong.es.js').allong.es\n\nclass Todo\n  constructor: (name) ->\n    self"
  },
  {
    "path": "spec/folding.spec.coffee",
    "chars": 639,
    "preview": "{mapWith, deepMapWith, filter} = require('../lib/allong.es.js').allong.es\n\nsquare = (n) -> n * n\n\ndescribe 'mapWith', ->"
  },
  {
    "path": "spec/iterators.spec.coffee",
    "chars": 11087,
    "preview": "{iterators: {slice, drop, take, accumulate, accumulateWithReturn, \n             fold, map, filter, FlatArrayIterator, Re"
  },
  {
    "path": "spec/map-arguments-with.spec.coffee",
    "chars": 1098,
    "preview": "{mapArgumentsWith, variadic, maybe} = require('../lib/allong.es').allong.es\n\nmyArgs = variadic (a) -> a \ndouble = (n) ->"
  },
  {
    "path": "spec/sequence-objects.spec.coffee",
    "chars": 740,
    "preview": "{sequence, flatten} = require('../lib/allong.es').allong.es\n\nWithLogging =\n  chain: (valueAndLogList, fn) ->\n    value ="
  },
  {
    "path": "spec/sequence.advanced.spec.coffee",
    "chars": 2133,
    "preview": "{sequence, sequence: {Identity, Maybe, Writer, List, Then, Callback}} = require('../lib/allong.es.js').allong.es\n\ndescri"
  },
  {
    "path": "spec/sequence.callback.spec.coffee",
    "chars": 1178,
    "preview": "{sequence, sequence: {Identity, Maybe, Writer, List, Then, Callback}} = require('../lib/allong.es.js').allong.es\n\ndescri"
  },
  {
    "path": "spec/sequence.decorataors.spec.coffee",
    "chars": 1095,
    "preview": "{sequence, maybe, andand, oror} = require('../lib/allong.es.js').allong.es\n\ndescribe \"Sequence Decorations\", ->\n  \n  ide"
  },
  {
    "path": "spec/sequence.then.spec.coffee",
    "chars": 1787,
    "preview": "Promise = require 'promise'\n\n{sequence} = require('../lib/allong.es.js').allong.es\n\n# also demonstrates string shorthand"
  },
  {
    "path": "spec/sequence.transformers.spec.coffee",
    "chars": 2686,
    "preview": "Promise = require 'promise'\n\n{sequence, sequence: {Then, Callback, fn2Then, fn2Callback, callback2Then, then2Callback}} "
  },
  {
    "path": "spec/trampoline.spec.coffee",
    "chars": 954,
    "preview": "{trampoline, tailCall} = require('../lib/allong.es.js').allong.es\n\ndescribe \"trampolining\", ->\n\n  depth = 32768\n\n  it \"s"
  }
]

About this extraction

This page contains the full source code of the raganwald/allong.es GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (183.9 KB), approximately 55.6k tokens, and a symbol index with 111 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!