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.
Top comments (0)