简体   繁体   中英

Traverse/sequence and Array<Either<E, A>> into Either<Array<E>, Array<A>> in fp-ts

I have a list of entities where even one failed validation would yield an error. However, I'd still like to iterate the whole list and collect all the errors for further logging.

Traverse/sequence with a default Either's Applicative would yield Either<E, A[]> (only the first encountered error) instead of needed Either<E[], A[]> .

Is there a default tool in the library to achieve this, or could it be achieved by writing a custom Applicative?

There's no fp-ts function that does this directly, but you could use getApplicativeValidation from the Either module:

import * as E from 'fp-ts/lib/Either'
import * as RA from 'fp-ts/lib/ReadonlyArray'
import {pipe} from 'fp-ts/lib/function'

const collectErrors = <E, A>(
  xs: readonly E.Either<E, A>[]
): E.Either<readonly E[], readonly A[]> =>
  pipe(
    xs,
    RA.traverse(E.getApplicativeValidation(RA.getSemigroup<E>()))(
      E.mapLeft(RA.of)
    )
  )

// Left(['a', 'b'])
collectErrors([E.left('a'), E.right(1), E.left('b'))])
// Right([1, 2])
collectErrors([E.right(1), E.right(2)])
// Right([])
collectErrors([])

How this works is that getApplicativeValidation takes a Semigroup instance and returns an Applicative instance that can be used with traverse . The applicative instance will combine errors using the semigroup instance.

RA.traverse(
  // The applicative instance for the either that will collect the errors 
  // into an array
  E.getApplicativeValidation(
    // The semigroup instance for readonly E[], which concatenates the arrays
    RA.getSemigroup<E>()
  )
)(
  // Turn each Either<E, A> into Either<readonly E[], A> by putting each error
  // inside an array so they can be concatenated
  E.mapLeft(RA.of)
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM