Iven Marquardt

Posted on

# Turn non-algebraic, imperative arrays into a monad transformer

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.