fp-ts
Version:
Functional programming in TypeScript
464 lines (463 loc) • 11.6 kB
JavaScript
import * as A from './Array';
import { fromEquals } from './Eq';
import { identity, pipe, bind_, bindTo_, flow } from './function';
/**
* @category constructors
* @since 2.0.0
*/
export function make(value, forest) {
if (forest === void 0) { forest = A.empty; }
return {
value: value,
forest: forest
};
}
/**
* @category instances
* @since 2.0.0
*/
export function getShow(S) {
var show = function (t) {
return t.forest === A.empty || t.forest.length === 0
? "make(" + S.show(t.value) + ")"
: "make(" + S.show(t.value) + ", [" + t.forest.map(show).join(', ') + "])";
};
return {
show: show
};
}
/**
* @category instances
* @since 2.0.0
*/
export function getEq(E) {
var SA;
var R = fromEquals(function (x, y) { return E.equals(x.value, y.value) && SA.equals(x.forest, y.forest); });
SA = A.getEq(R);
return R;
}
var draw = function (indentation, forest) {
var r = '';
var len = forest.length;
var tree;
for (var i = 0; i < len; i++) {
tree = forest[i];
var isLast = i === len - 1;
r += indentation + (isLast ? '└' : '├') + '─ ' + tree.value;
r += draw(indentation + (len > 1 && !isLast ? '│ ' : ' '), tree.forest);
}
return r;
};
/**
* Neat 2-dimensional drawing of a forest
*
* @since 2.0.0
*/
export function drawForest(forest) {
return draw('\n', forest);
}
/**
* Neat 2-dimensional drawing of a tree
*
* @example
* import { make, drawTree, tree } from 'fp-ts/Tree'
*
* const fa = make('a', [
* tree.of('b'),
* tree.of('c'),
* make('d', [tree.of('e'), tree.of('f')])
* ])
*
* assert.strictEqual(drawTree(fa), `a
* ├─ b
* ├─ c
* └─ d
* ├─ e
* └─ f`)
*
*
* @since 2.0.0
*/
export function drawTree(tree) {
return tree.value + drawForest(tree.forest);
}
/**
* Build a tree from a seed value
*
* @category constructors
* @since 2.0.0
*/
export function unfoldTree(b, f) {
var _a = f(b), a = _a[0], bs = _a[1];
return { value: a, forest: unfoldForest(bs, f) };
}
/**
* Build a tree from a seed value
*
* @category constructors
* @since 2.0.0
*/
export function unfoldForest(bs, f) {
return bs.map(function (b) { return unfoldTree(b, f); });
}
export function unfoldTreeM(M) {
var unfoldForestMM = unfoldForestM(M);
return function (b, f) { return M.chain(f(b), function (_a) {
var a = _a[0], bs = _a[1];
return M.chain(unfoldForestMM(bs, f), function (ts) { return M.of({ value: a, forest: ts }); });
}); };
}
export function unfoldForestM(M) {
var traverseM = A.traverse(M);
return function (bs, f) {
return pipe(bs, traverseM(function (b) { return unfoldTreeM(M)(b, f); }));
};
}
// TODO: curry in v3
/**
* @since 2.0.0
*/
export function elem(E) {
var go = function (a, fa) {
if (E.equals(a, fa.value)) {
return true;
}
return fa.forest.some(function (tree) { return go(a, tree); });
};
return go;
}
/**
* Fold a tree into a "summary" value in depth-first order.
*
* For each node in the tree, apply `f` to the `value` and the result of applying `f` to each `forest`.
*
* This is also known as the catamorphism on trees.
*
* @example
* import { fold, make } from 'fp-ts/Tree'
*
* const t = make(1, [make(2), make(3)])
*
* const sum = (as: Array<number>) => as.reduce((a, acc) => a + acc, 0)
*
* // Sum the values in a tree:
* assert.deepStrictEqual(fold((a: number, bs: Array<number>) => a + sum(bs))(t), 6)
*
* // Find the maximum value in the tree:
* assert.deepStrictEqual(fold((a: number, bs: Array<number>) => bs.reduce((b, acc) => Math.max(b, acc), a))(t), 3)
*
* // Count the number of leaves in the tree:
* assert.deepStrictEqual(fold((_: number, bs: Array<number>) => (bs.length === 0 ? 1 : sum(bs)))(t), 2)
*
* @category destructors
* @since 2.6.0
*/
export function fold(f) {
var go = function (tree) { return f(tree.value, tree.forest.map(go)); };
return go;
}
// -------------------------------------------------------------------------------------
// non-pipeables
// -------------------------------------------------------------------------------------
/* istanbul ignore next */
var map_ = function (fa, f) { return pipe(fa, map(f)); };
var ap_ = function (fab, fa) {
return pipe(fab, chain(function (f) { return pipe(fa, map(f)); }));
};
/* istanbul ignore next */
var chain_ = function (ma, f) { return pipe(ma, chain(f)); };
/* istanbul ignore next */
var reduce_ = function (fa, b, f) { return pipe(fa, reduce(b, f)); };
/* istanbul ignore next */
var foldMap_ = function (M) {
var foldMapM = foldMap(M);
return function (fa, f) { return pipe(fa, foldMapM(f)); };
};
/* istanbul ignore next */
var reduceRight_ = function (fa, b, f) { return pipe(fa, reduceRight(b, f)); };
/* istanbul ignore next */
var extend_ = function (wa, f) { return pipe(wa, extend(f)); };
/* istanbul ignore next */
var traverse_ = function (F) {
var traverseF = traverse(F);
return function (ta, f) { return pipe(ta, traverseF(f)); };
};
// -------------------------------------------------------------------------------------
// pipeables
// -------------------------------------------------------------------------------------
/**
* Apply a function to an argument under a type constructor.
*
* @category Apply
* @since 2.0.0
*/
export var ap = function (fa) { return function (fab) { return ap_(fab, fa); }; };
/**
* Combine two effectful actions, keeping only the result of the first.
*
* Derivable from `Apply`.
*
* @category combinators
* @since 2.0.0
*/
export var apFirst = function (fb) {
return flow(map(function (a) { return function () { return a; }; }), ap(fb));
};
/**
* Combine two effectful actions, keeping only the result of the second.
*
* Derivable from `Apply`.
*
* @category combinators
* @since 2.0.0
*/
export var apSecond = function (fb) {
return flow(map(function () { return function (b) { return b; }; }), ap(fb));
};
/**
* Composes computations in sequence, using the return value of one computation to determine the next computation.
*
* @category Monad
* @since 2.0.0
*/
export var chain = function (f) { return function (ma) {
var _a = f(ma.value), value = _a.value, forest = _a.forest;
var concat = A.getMonoid().concat;
return {
value: value,
forest: concat(forest, ma.forest.map(chain(f)))
};
}; };
/**
* Composes computations in sequence, using the return value of one computation to determine the next computation and
* keeping only the result of the first.
*
* Derivable from `Monad`.
*
* @category combinators
* @since 2.0.0
*/
export var chainFirst = function (f) {
return chain(function (a) {
return pipe(f(a), map(function () { return a; }));
});
};
/**
* @category Extend
* @since 2.0.0
*/
export var extend = function (f) { return function (wa) { return ({
value: f(wa),
forest: wa.forest.map(extend(f))
}); }; };
/**
* Derivable from `Extend`.
*
* @category combinators
* @since 2.0.0
*/
export var duplicate =
/*#__PURE__*/
extend(identity);
/**
* Derivable from `Monad`.
*
* @category combinators
* @since 2.0.0
*/
export var flatten =
/*#__PURE__*/
chain(identity);
/**
* `map` can be used to turn functions `(a: A) => B` into functions `(fa: F<A>) => F<B>` whose argument and return types
* use the type constructor `F` to represent some computational context.
*
* @category Functor
* @since 2.0.0
*/
export var map = function (f) { return function (fa) { return ({
value: f(fa.value),
forest: fa.forest.map(map(f))
}); }; };
/**
* @category Foldable
* @since 2.0.0
*/
export var reduce = function (b, f) { return function (fa) {
var r = f(b, fa.value);
var len = fa.forest.length;
for (var i = 0; i < len; i++) {
r = pipe(fa.forest[i], reduce(r, f));
}
return r;
}; };
/**
* @category Foldable
* @since 2.0.0
*/
export var foldMap = function (M) { return function (f) {
return reduce(M.empty, function (acc, a) { return M.concat(acc, f(a)); });
}; };
/**
* @category Foldable
* @since 2.0.0
*/
export var reduceRight = function (b, f) { return function (fa) {
var r = b;
var len = fa.forest.length;
for (var i = len - 1; i >= 0; i--) {
r = pipe(fa.forest[i], reduceRight(r, f));
}
return f(fa.value, r);
}; };
/**
* @category Extract
* @since 2.6.2
*/
export var extract = function (wa) { return wa.value; };
/**
* @since 2.6.3
*/
export var traverse = function (F) {
var traverseF = A.traverse(F);
var out = function (f) { return function (ta) {
return F.ap(F.map(f(ta.value), function (value) { return function (forest) { return ({
value: value,
forest: forest
}); }; }), pipe(ta.forest, traverseF(out(f))));
}; };
return out;
};
/**
* @since 2.6.3
*/
export var sequence = function (F) { return traverse(F)(identity); };
/**
* Wrap a value into the type constructor.
*
* @category Applicative
* @since 2.7.0
*/
export var of = function (a) { return ({
value: a,
forest: A.empty
}); };
// -------------------------------------------------------------------------------------
// instances
// -------------------------------------------------------------------------------------
/**
* @category instances
* @since 2.0.0
*/
export var URI = 'Tree';
/**
* @category instances
* @since 2.7.0
*/
export var Functor = {
URI: URI,
map: map_
};
/**
* @category instances
* @since 2.7.0
*/
export var Applicative = {
URI: URI,
map: map_,
ap: ap_,
of: of
};
/**
* @category instances
* @since 2.7.0
*/
export var Monad = {
URI: URI,
map: map_,
ap: ap_,
of: of,
chain: chain_
};
/**
* @category instances
* @since 2.7.0
*/
export var Foldable = {
URI: URI,
reduce: reduce_,
foldMap: foldMap_,
reduceRight: reduceRight_
};
/**
* @category instances
* @since 2.7.0
*/
export var Traversable = {
URI: URI,
map: map_,
reduce: reduce_,
foldMap: foldMap_,
reduceRight: reduceRight_,
traverse: traverse_,
sequence: sequence
};
/**
* @category instances
* @since 2.7.0
*/
export var Comonad = {
URI: URI,
map: map_,
extend: extend_,
extract: extract
};
// TODO: remove in v3
/**
* @category instances
* @since 2.0.0
*/
export var tree = {
URI: URI,
map: map_,
of: of,
ap: ap_,
chain: chain_,
reduce: reduce_,
foldMap: foldMap_,
reduceRight: reduceRight_,
traverse: traverse_,
sequence: sequence,
extract: extract,
extend: extend_
};
// -------------------------------------------------------------------------------------
// do notation
// -------------------------------------------------------------------------------------
/**
* @since 2.9.0
*/
export var Do =
/*#__PURE__*/
of({});
/**
* @since 2.8.0
*/
export var bindTo = function (name) { return map(bindTo_(name)); };
/**
* @since 2.8.0
*/
export var bind = function (name, f) {
return chain(function (a) {
return pipe(f(a), map(function (b) { return bind_(a, name, b); }));
});
};
// -------------------------------------------------------------------------------------
// pipeable sequence S
// -------------------------------------------------------------------------------------
/**
* @since 2.8.0
*/
export var apS = function (name, fb) {
return flow(map(function (a) { return function (b) { return bind_(a, name, b); }; }), ap(fb));
};