DEV Community

Iven Marquardt
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
Enter fullscreen mode Exit fullscreen mode

You can find a running example on repl.

Top comments (0)