Skip to content

Latest commit

 

History

History
606 lines (436 loc) · 14 KB

1.4.md

File metadata and controls

606 lines (436 loc) · 14 KB

Teep 1.4.x

Usage

Node.js

npm install teep
var teep = require('teep');

var a = teep.option(); // none
var b = a.map(function (x) { return x + 1; }); // none

var c = teep.option(41); // some(41)
var d = c.map(function (x) { return x + 1; }); // some(42)

HTML

<script src="https://github.com/earldouglas/teep/raw/1.4.0/teep.js"></script>
<script type="text/javascript">
  window.teep = window.edc.teep;
</script>

JavaScript console

Fire up your browser's JavaScript console from this page, and explore the teep object directly.

Features

Array

array.contains(xs, x)

contains takes an array and an element, and returns whether the element exists at least once in the array.

Example:

var yep  = teep.array.contains([1,2,3], 2); // true
var nope = teep.array.contains([1,2,3], 4); // false

array.foldr(xs, z, f)

foldr performs a right-fold over an array, using z as the starting point.

Example:

function sum(xs) {
  return teep.array.foldr(xs, 0, function (z, x) {
    return z + x;
  });
}

sum([1,2,3,4]); // 10

array.flatten(xss)

flatten takes an array of arrays, and returns the concatenation of each.

Example:

teep.array.flatten([[1,2],[3,4],[5,6]]); // [1,2,3,4,5,6]

array.map(xs, f)

map applies a function over each element in an array, and returns a new array of the results.

Example:

function plusOne(x) {
  return x + 1;
}
teep.array.map([1,2,3,4], plusOne); // [2,3,4,5]

array.flatMap(xs, f)

flatMap applies a function (that itself returns an array) over each element in an array, and returns an array of the concatenation of the results.

Example:

function repeatPlusOne(x) {
  return [x, x + 1];
}
teep.array.flatMap([1,2,3], repeatPlusOne); // [1,2,2,3,3,4]

array.filter(xs, f)

filter returns an array of elements from xs for which f returns true.

Example:

teep.array.filter([1,2,3,4,5,6], function (x) {
  return x % 2 === 1 || x === 4;
}); // [1,2,4,5]

Function

fn.compose(f, g)

compose takes two unary functions f and g, and combines them into a single unary function f ∘ g that applies g to an input, passes the output to f, applies f to it, and returns the result.

The application of (f ∘ g)(x) is equivalent to f(g(x)).

Example:

function inc(x) {
  return x + 1;
}

function square(x) {
  return x * x;
}

var nine = teep.fn.compose(square, inc)(2); // square(inc(2)) == (2 + 1) ^ 2
var five = teep.fn.compose(inc, square)(2); // inc(square(2)) == (2 ^ 2) + 1

fn.curry(f, args)

curry takes an n-ary function f and an optional array of arguments, and returns a curried version of f, comprised of n nested unary functions where the arity of f is n.

Example:

function add(x, y) {
  return x + y;
}

var add2 = teep.fn.curry(add)(2);
var five = add2(3); // 2 + 3 == 5

Example:

function mathemagic(x, y, z) {
  return x * (y + z);
}

var fortyTwo = teep.fn.curry(mathemagic)(2)(20)(1); // 2 * (20 + 1)

fn.memoize(f, cache)

memoize takes a function and an optional cache implementation, and memoizes the function by backing it with the cache, if supplied, or a simple object-based cache otherwise.

Example:

function expensiveFn(n) {
  for (var i = 0; i < 10000; i++) {
    for (var j = 0; j < 10000; j++) {
      i = i;
    }
  }
  return n;
}

var cheapFn = teep.fn.memoize(expensiveFn);

var slowResult = cheapFn(42); // expensive computation the first time
var fastResult = cheapFn(42); // cheap cache lookup the second time

fn.lazy(f, cache)

lazy takes a function and an optional cache implementation, and creates a function that, given input arguments, returns a lazy evaluator that will apply the function to the arguments only when needed, and only once if needed many times.

Example:

function expensiveFn(n) {
  for (var i = 0; i < 10000; i++) {
    for (var j = 0; j < 10000; j++) {
      i = i;
    }
  }
  return n;
}

var lazyVal = teep.fn.lazy(expensiveFn)(42); // lazily apply 42 -- no computation yet

var slowResult = lazyVal.get(); // expensive computation the first time
var fastResult = lazyVal.get(); // cheap cache lookup the second time

fn.throttle(limit, period, interval, f)

throttle limits the rate of application of a function f, regardless of how frequently it is called. Call order is preserved.

Arguments:

  • limit is the maximum number of applications to allow in a period
  • period is the duration (in milliseconds) to allow up to limit applications
  • interval is the minimum time (in milliseconds) allowed between applications
  • f is a single-argument function to be throttled

Example:

function f(x) {
  ...
}
// Don't exceed two f(x) calls per second
var limit = 2;
var period = 1000;

// Space out f(x) calls with 250ms in between
var interval = 250;

var throttledF = teep.fn.throttle(limit, period, interval, f);
// Run f(x) a bunch of times:
//  * Don't exceed two per second
//  * Don't exceed one every 250ms
throttledF(1);
throttledF(2);
throttledF(3);
throttledF(4);
throttledF(5);

Option

option(value)

option constructs a representation of an optional value, represented as either "some value" or "no value", depending on whether a non-null argument was supplied.

An option instance exposes the following fields:

  • empty
  • map(f) - returns a new option by applying f over this option's value, and wrapping the result in an option
  • flatMap(f) - returns a new option by applying f over this option's value, and returning the result
  • ap(a) - assumes this option wraps a function, and returns a new option by mapping this option's function over the option a and returning the result
  • toString()

Example:

function parse(x) {
  var y = parseInt(x);
  if (y) {
    return teep.option(y);
  } else {
    return teep.option();
  }
}

parse('forty-two').toString(); // none()
parse('42').toString(); // some(42)

function timesTwo(x) {
  return x * 2;
}
parse('21').map(timesTwo).toString(); // some(42)

parse('21').flatMap(function (x) {
  return parse('2').map(function (y) {
    return x * y;
  });
}).toString(); // some(42)

Promise

promise.collect(promises, callback)

Given an array of promises and a callback, passes the result of each promise (in order) as an argument to the callback, and returns a single promise that yields the result of the callback.

Any of the promises can be lazy, implemented as a nullary function that returns a promise, and will be retrieved as late as possible.

Example:

var p = teep.promise.collect([
  Promise.resolve(2),
  Promise.resolve(20),
  Promise.resolve(1)
], function (x, y, z) {
  return x * (y + z);
});

p.then(function(x) {
  console.log(x); // 42
});

p is congruent to Promise.resolve(2 * (20 + 1)), or Promise.resolve(42)

Validation

validation.valid(value)

valid constructs a validation representing a valid value.

A validation created by valid exposes the following fields:

  • valid - returns true
  • value - returns the (valid) value
  • map(f) - returns a new (valid) validation by mapping f over this validation's value and wrapping the in a (valid) validation
  • flatMap(f) - returns a new validation result by mapping f over this validation's value and returning the result
  • ap(a) - assumes this validation wraps a function, and returns a new validation by mapping this validation's function over the validation a and returning the result
  • toString()

validation.invalid(errors)

invalid constructs a validation representing an invalid value, and containing an array of errors.

A validation created by invalid exposes the following fields:

  • valid - returns false
  • errors - returns the array of errors
  • map(f) - returns a this validation
  • flatMap(f) - returns this validation
  • ap(a) - if a is valid, return this validation, otherwise returns a new (invalid) validation containing the concatenation of this validation's errors with a's errors
  • toString()

Example:

function parse(x) {
  var y = parseInt(x);
  if (y) {
    return teep.validation.valid(y);
  } else {
    return teep.validation.invalid([x + ' is not a number']);
  }
}

parse('forty-two').toString(); // invalid(forty-two is not a number)
parse('42').toString(); // valid(42)

function multiply(x) {
  return function (y) {
    return x * y;
  };
}
parse('21').map(multiply(2)).toString(); // valid(42)

parse('21').flatMap(function (x) {
  return parse('2').map(multiply(x));
}).toString(); // valid(42)

teep.validation.valid(multiply).
  ap(parse('21')).
  ap(parse('2')).toString(); // valid(42)

teep.validation.valid(multiply).
  ap(parse('twenty-one')).
  ap(parse('two')).toString(); // invalid(twenty-one is not a number,
                               //         two is not a number)

List

list(head, tail)

list constructs a linked list from a value, head, representing the first element in the list, and another list, tail, representing the rest of the constructed list. If head is null, tail is returned, and if tail is null, the empty list is returned.

A list instance exposes the following fields:

  • head - the head of this list, if it is not empty
  • tail - the tail of this list, if it is not empty
  • length - returns the length of this list
  • map(f) - returns a new list created by applying f over this list
  • flatMap(f) - returns a new list created by applying f over this list and concatenating the results
  • concat(l) - returns the concatenation of this list with l
  • toString()

Example:

var l = teep.list(1, teep.list(2, teep.list(3, teep.list())));

l.toString(); // cons(1, cons(2, cons(3, nil)));

l.map(function (x) {
  return x * 2;
}).toString(); // cons(2, cons(4, cons(6, nil)));

l.flatMap(function (x) {
  return teep.list(x, teep.list(x + 0.1, teep.list()));
}).toString(); // cons(1, cons(1.1, cons(2, cons(2.1, cons(3, cons(3.1, nil))))))

var l2 = teep.list(4, teep.list(5, teep.list(6, teep.list())));
l.concat(l2).toString(); // cons(1, cons(2, cons(3, cons(4, cons(5, cons(6, nil))))))

Reader

reader(f)

reader constructs a "dependency injection" context around a function f that needs an external context (i.e. the application of an argument) to run.

A reader instance exposes the following fields:

  • apply(a) - applies the bound function with the value a
  • map(g) - returns a new reader by composing f with g
  • flatMap(g) - returns a new reader composing f with g, then applying the resulting reader

Example:

var db = {
  x: 21,
  y: 2,
};

function getX(db) {
  return db.x;
}

function timesTwo(x) {
  return x * 2;
}

teep.reader(getX).map(timesTwo).apply(db); // 42

function getY(db) {
  return db.y;
}

teep.reader(getX).flatMap(function (x) {
  return teep.reader(getY).map(function (y) {
    return x * y;
  });
}).apply(db); // 42

read

read returns a reader of identity, to easily "look up" the context.

Example:

var getX = teep.read.map(function (e) { return e.x; });
var getY = teep.read.map(function (e) { return e.y; });

var r = getX.flatMap(function (x) {
  return getY.map(function (y) {
    return x * y;
  });
});

var e = { x: 6, y: 7, };

r.apply(e); // 42

Future

future(f)

future creates a data structure around a function f that expects a callback, enabling chaining, combining, and composing of additional callbacks.

A future instance exposes the following fields:

  • apply(k) - applies the bound function with the callback k
  • map(g) - returns a new future by composing f with g
  • flatMap(g) - returns a new future composing f with g, then applying the resulting future
  • sequence(f2) - returns a new future whose callback expects the results of the function f and the future f2.

Example:

function async(x) {
  return function (f) {
    setTimeout(f(x), 1000);
  };
}

function log(x) {
  console.log(x);
}

teep.future(async(21)).map(function (x) {
  return x * 2;
}).apply(log); // 42

teep.future(async(21)).flatMap(function (x) {
  return teep.future(async(x * 2));
}).apply(log); // 42

ReaderT

readerT(f)

readerT, like reader, constructs a context around a function f that, given an external context, returns another monad

A readerT instance exposes the following fields:

  • apply(a) - applies the bound function with the value a
  • map(g) - returns a new readerT by composing f with g
  • flatMap(g) - returns a new readerT composing f with g, then binding the resulting monad

Example:

var db = {
  x: 21,
  y: 2,
};

var getX = function (db) {
  return teep.future(function (k) {
    return k(db.x);
  });
};

function log(x) {
  console.log(x);
}

teep.readerT(getX).map(function (x) {
  return x * 2;
}).apply(db).apply(log); // 42

function multiplyByY(x) {
  return teep.readerT(function (db) {
    return teep.future(function (k) {
      return k(x * db.y);
    });
  });
};

teep.readerT(getX).flatMap(multiplyByY).apply(db).apply(log); // 42

State

state(f)

state takes a function of type (S) => { state: S, value: A } and wraps it in a data structure exposing the following fields:

  • apply(s) - runs the state action with the initial state s
  • map(f) - returns a new state action that applies f, which is a pure function to value
  • flatMap(f) - returns a new state action that applies f, which itself returns a state action, to value

Example:

var getX = teep.state(function (db) {
  return { state: db, value: db.x };
});

var setX = function (x) {
  return teep.state(function (db) {
    db.x = x;
    return { state: db, value: null };
  });
};

getX.apply({ x: 42 }).value; // 42

getX.map(times(7)).apply({ x: 6 }).value; // 42

getX.map(times(7)).flatMap(setX).flatMap(function (x) {
  return getX;
}).apply({ x: 6 }).value; // 42