I need to define an entry-point file for the library so that NPM can package it properly.
I want the library to be usable by importing specific modules to aid in self-documenting code. I need to import everything first
{import-functor, 3:3} {import-apply, 4:4} {import-applicative, 5:3} {import-profunctor, 9:3} {import-chain, 6:3} {import-monoid, 8:3} {import-semigroup, 7:3}
The export list is just every individual module and a hand-selected list of methods that are easiest to use.
export { Functor, Apply, Applicative, Profunctor, Chain, Monoid, Semigroup, map, apAll, constant, mapInOut, mapIn, objectify, chain, expandAll, combine, identity, concatAll };
Used in section 2
I need to extract all the methods for the named exports
It is sometimes useful to have the combinators exported by this library be memoized to avoid recomputing values that haven't changed. To that end a memoized version of each function is setup here. It currently relies on the memoize function from the ramda library. Note that Monoid and Applicative do not need memoizing since their implementations perform no appreciable work. I also need to import the modules under a different name in order to re-export them properly.
import R from './ramda'; import FunctorI from './Functor'; import ApplyI from './Apply'; import ChainI from './Chain'; import ProfunctorI from './Profunctor'; import SemigroupI from './Semigroup'; {import-monoid, 8:3} {import-applicative, 5:3} const { identity } = Monoid; const { constant } = Applicative; function memoize(f) { let lastFirst = null; let lastSecond = null; let lastResult = null; return (first: *, second: *) => { if (lastFirst !== first && lastSecond !== second || lastResult === null) { lastFirst = first; lastSecond = second; lastResult = f(first, second); } return lastResult; }; } const map = (...args: *) => memoize(FunctorI.map(...args)); const ap = (...args: *) => memoize(ApplyI.ap(...args)); const apAll = (...args: *) => memoize(ApplyI.apAll(...args)); const promap = (...args: *) => memoize(ProfunctorI.promap(...args)); const mapInOut = promap; const mapIn = (...args: *) => memoize(ProfunctorI.mapIn(...args)); const mapOut = map; const objectify = (...args: *) => memoize(ProfunctorI.objectify(...args)); const bind = (...args: *) => memoize(ChainI.bind(...args)); const chain = (...args: *) => memoize(ChainI.chain(...args)); const expand = (...args: *) => memoize(ChainI.expand(...args)); const expandAll = (...args: *) => memoize(ChainI.expandAll(...args)); const combine = (...args: *) => memoize(ChainI.combine(...args)); const concat = (...args: *) => memoize(SemigroupI.concat(...args)); const concatAll = (...args: *) => memoize(SemigroupI.concatAll(...args)); const Functor = { map }; const Apply = { ap, apAll }; const Profunctor = { promap, mapInOut, mapIn, mapOut, objectify }; const Chain = { bind, chain, expand, expandAll, combine }; const Semigroup = { concat, concatAll }; {export-list, 1}
I use a version of function composition throughout the examples. It is defined as follows.
The constant function is mentioned occasionally. It is defined with a type signature as follows.
type Constant<A, B> = (a) => (b) => a const constant : Constant<*,*> = x => y => x
The identity function is a useful function and appears in some of the laws for the interfaces.
The filter function is used in some examples