gcanti / fp-ts-rxjs

fp-ts bindings for RxJS
https://gcanti.github.io/fp-ts-rxjs/
MIT License
187 stars 29 forks source link

sequenceT improvement for ObservableEithers #71

Closed fgaudo closed 6 months ago

fgaudo commented 1 year ago

🚀 Feature request

Improve sequenceT for ObservableEither's to immediately return Left if one of the arguments returns a Left.

Current Behavior

When executing sequenceT over a sequence of ObservableEithers the resulting ObservableEither will wait until all have responded, regardless of any Left values that have already been returned.

// this will wait 10 seconds regardless
sequenceT(OE.Apply)(
    OE.rightObservable<number,number>(Rx.timer(10000)),
    OE.leftObservable(Rx.timer(2000)),
    OE.rightObservable(Rx.timer(10000)),
    OE.rightObservable(Rx.timer(10000))
)

This happens because combineLatest (which is the implementation of Observable's ap) does not consider the Either result and only fails fast when any of the observables actually throws an error.

Desired Behavior

Let sequenceT skip all other Observables when one of them returns a Left.

Suggested Solution

I think one solution could be a little change in ObservableEithers' ap implementation like this:

export const ap = <E, A>(
  fa: ObservableEither<E, A>
): (<B>(fab: ObservableEither<E, (a: A) => B>) => ObservableEither<E, B>) =>
  flow(
    fold(rxThrowError, right), // throws an error if left
    R.map(gab => (ga: E.Either<E, A>) => E.ap(ga)(gab)),
    R.ap(pipe(fa, fold(rxThrowError, right))), // throws an error if left
    catchError(left) // catch any errors and convert them back to left values
  )

We basically throw an erroring observable if there's a left value, and convert it back to Left values only at the end.

I guess the same would apply to apFirst and apSecond and maybe others. What do you think?

Your environment

Software Version(s)
fp-ts 2.16.0
fp-ts-rxjs 0.6.15
rxjs 7.8.1
TypeScript 5.1.6