Turn non-algebraic, imperative arrays into a monad transformer
Iven Marquardt
Posted on November 17, 2020
Turns out that although JS arrays are not algebraic, you can implement a transformer-like type ArrayT m a = m (Array (m a))
. It behaves like a transformer & adheres to the monad laws for many m
but there are no guarantees that this holds for all m
:
// ARRAYT
const arrFoldT = chain => f => init => mmx =>
chain(mmx) (mx => {
const go = (acc, i) =>
i === mx.length
? acc
: chain(mx[i]) (x =>
go(f(acc) (x), i + 1))
return go(init, 0);
});
const arrAppendT = ({chain, of}) => mmx => mmy =>
arrFoldT(chain)
(acc => x =>
chain(acc) (acc_ =>
of(arrSnoc(of(x)) (acc_)))) (mmx) (mmy);
const arrChainT = ({chain, of}) => mmx => fmm =>
arrFoldT(chain)
(acc => x =>
arrAppendT({chain, of}) (acc) (fmm(x)))
(of([]))
(mmx);
const arrOfT = of => x => of([of(x)]);
// OPTION
const Option = union("Option");
const None = Option("None", {});
const Some = some => Option(Some, {some});
const optChain = mx => fm =>
match(mx, {
None: _ => None,
Some: ({some: x}) => fm(x)
});
const optOf = x => Some(x);
// OPTARR
const optArrChain = arrChainT({chain: optChain, of: optOf});
const optArrOf = arrOfT(optOf);
// MAIN
const mmw = Some([Some(1), Some(2), Some(3)]),
mmx = Some([Some(1), None, Some(3)]),
mmy = Some([Some(1), Some(0), Some(3)]),
mmz = None;
const main = optArrChain(mmw) (x => optArrOf(x * x)),
main2 = optArrChain(mmx) (x => optArrOf(x * x)),
main3 = optArrChain(mmx) (x => x === 0 ? None : optArrOf(x * x)),
main4 = optArrChain(mmz) (x => optArrOf(x * x));
main; // Some([Some(1), Some(4), Some(9)])
main2; // None
main3; // None
main4; // None
You can find a running example on repl.
💖 💪 🙅 🚩
Iven Marquardt
Posted on November 17, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.