Search code examples
typescriptfunctional-programmingfp-ts

Transform Either<A, B>[] into Either<A[], B[]>


As the title says, I want to transform Either<A, B>[] into Either<A[], B[]>

The rule is, if there is a single left-value (an error), I want to get a left-value with all the errors; otherwise, I want the right answers.

This seems like it should be the easiest thing, but here is all I have:

const compress = <A, B>(arr: E.Either<A, B>[]): E.Either<A[], B[]> => 
  A.reduce(
    E.right([]),
    (acc: E.Either<A[], B[]>, v: E.Either<A, B>) => 
    E.match(
      (a: A) => E.match((aar: A[]) => E.left([...aar, a]), 
                        (bar: B[]) => E.left([a]))(acc),
      (b: B) => E.match((aar: A[]) => E.left(aar), 
                        (bar: B[]) => E.right([...bar, b]))(acc)
    )(v)
   )(arr);

Seems way too complicated.


Solution

  • There's the separate function which you can use to collect up the lefts and rights in an array of Eithers. That could arguably be used as is, but you could get the exact type you're looking for by doing:

    import { separate } from 'fp-ts/Array';
    import * as E from 'fp-ts/Either';
    import { pipe } from 'fp-ts/lib/function';
    
    declare const es: Array<E.Either<number, string>>;
    
    const output: E.Either<string[], number[]> = pipe(
      es,
      separate,
      // Assuming you want to have the either be left if any result was left.
      (s) => s.left.length > 0 ? E.left(s.left) : E.right(s.right),
    );
    

    There's also sequenceArray which can be a bit of a shortcut when you're aggregating errors from multiple sources, but this becomes a left as soon as a single left is encountered so you wouldn't get back all of the left values (which is a potential downside if you need to know all of the lefts at once).

    const output2: E.Either<string, readonly number[]> = pipe(
      es,
      E.sequenceArray,
    );