0

In fp-ts, how do I go from ReaderEither<R, E, A>[] to ReaderEither<R, E[], A[]>? Essentially I want to convert an array of ReaderEither instances to a single ReaderEither instance.

I've tried searching for the answer, but haven't had much luck. I'm new to functional programming and fp-ts, so I'm still trying to wrap my head around all of the concepts and put them into practice. I appreciate your help.

Ryan Helmoski
  • 359
  • 2
  • 7
  • I believe Traversable might be the key. I've found some examples of using it with Option and Either, but I'm having trouble getting it to work with ReaderEither. https://gcanti.github.io/fp-ts/modules/Traversable.ts.html – Ryan Helmoski Sep 24 '20 at 01:23
  • 1
    You can get a `ReaderEither` from a `ReaderEither[]` with [`sequenceT`](https://gcanti.github.io/fp-ts/modules/Apply.ts.html#sequencet), but I'm not sure how to accumulate the errors. – Lauren Yim Sep 24 '20 at 01:29
  • Are you sure you want a `ReaderEither`? This is the same as a `Reader>`; do you want a `Reader[]>` instead? – Lauren Yim Sep 24 '20 at 02:04
  • Yes, I'm hoping to accumulate the results in one single `Either`, rather than having multiple `Either`'s – Ryan Helmoski Sep 24 '20 at 02:09
  • How do you want this to work? What if some of the `ReaderEither`s return a `Left` and some return a `Right`? Do you want the final `Either` to be dependent on the first `ReaderEither` (e.g. if the first `ReaderEither` returns a `Left`, return a `Left` by accumulating the other `Left`s and discard the `Right`s)? And what if the `ReaderEither[]` is empty? – Lauren Yim Sep 24 '20 at 02:17
  • Yes, that sounds like exactly what I'm trying to achieve; albeit, with one clarification: I'm hoping to return a `Left` if any of the `ReaderEither`'s return a `Left`, not just if the first one returns a `Left`. So if the first `ReaderEither` returns a `Right`, but the second one returns a `Left`, I would still want to return a `Left`. I would only want to return a `Right` if all of the `ReaderEither`'s return a `Right`. Apologies if that is what you meant; I just want to be sure we're on the same page. Thank you for your time. – Ryan Helmoski Sep 24 '20 at 02:25

1 Answers1

1

Try this:

import * as A from 'fp-ts/Array'
import {left, right} from 'fp-ts/Either'
import * as R from 'fp-ts/Reader'
import {flow} from 'fp-ts/function'
import type {ReaderEither} from 'fp-ts/ReaderEither'

const sequenceRE: <R, E, A>(
  fs: ReaderEither<R, E, A>[]
) => ReaderEither<R, E[], A[]> = flow(
  // ReaderEither<R, E, A>[] -> Reader<R, Either<E, A>[]>
  A.sequence(R.reader),
  // Maps the reader: Reader<R, Either<E, A>[]> -> ReaderEither<R, E[], A[]>
  R.map(flow(
    // Either<E, A>[] -> Separated<E[], A[]>
    A.separate,
    // Separated<E[], A[]> -> Either<E[], A[]>
    s => s.left.length ? left(s.left) : right(s.right)
  ))
)

// Right [4, 6]
sequenceRE([r => right(r * 2), r => right(r * 3)])(2)
// Left ['foo', 'bar']
sequenceRE([r => right(r * 2), r => left('foo'), r => left('bar')])(2)
// Right []
sequenceRE([])(2)

The code does this:

  • sequences (from Traversable) the ReaderEither<R, E, A>[] into Reader<R, Either<E, A>[]>

  • separates (from Compactable) the Either<E, A>[] from the reader into {left: E[], right: A[]}

    interface Separated<A, B> {
      readonly left: A
      readonly right: B
    }
    
    // For Array:
    declare const separate: <A, B>(fa: Either<A, B>[]) => Separated<A[], B[]>
    
  • If there are any Lefts, returns a Left with the Left values, and otherwise returns a Right with the Right values.

Lauren Yim
  • 12,700
  • 2
  • 32
  • 59