简体   繁体   中英

How to combine the left sides of Either objects in fp-ts?

I have two validation functions for different values that return Either . I'd like to throw an exception if one of them has a left value and do nothing if both are right . I've never used fp-ts before and can't figure out how to properly combine left results. My current solution works but does not feel like i'm using it properly.

import { Either, left, right, isLeft, getOrElse } from 'fp-ts/lib/Either';

function validateMonth( m: Month ): Either<Error, Month> {
   return m.isInRange() ? right(m) : left(new Error('Month must be in range!'));
}

function validateYear( y: Year ): Either<Error, Year> {
   return year.isBefore(2038) ? right(y) : left(new Error('Year must be before 2038!'));
}

function throwingValidator(m: Month, y: Year): void {
 // todo: Refactor to get rid of intermediate variables,
 // combining results of validateMonth and validateYear into a type
 // of Either<Error, Unit>
 const monthResult = validateMonth( month );
 const yearResult = validateYear( year );
 const throwOnError = (e: Error) => { throw e; };
 if ( isLeft( monthResult ) ) { getOrElse(throwOnError)(monthResult); }
 if ( isLeft( yearResult ) ) { getOrElse(throwOnError)(yearResult); }
}

I've read the introduction athttps://dev.to/gcanti/getting-started-with-fp-ts-either-vs-validation-5eja but that code is exactly the opposite of what I want: I don't care about the input value after the validation and want to return only the first error that occurs.

You're probably looking for something like

const toPromise = fold(e => Promise.reject(e), r => Promise.resolve(r));

Promise.all([
    toPromise(validateMonth(month)),
    toPromise(validateYear(year)),
]).then(([validMonth, validYear]) => {
    return …
});

or the more functional way

toPromise(ap(ap(of(validMonth => validYear => {
    return …
}), validateMonth(month)), validateYear(year)))

You can also do the Promise.all with array.sequence and the toPromise afterwards.

Ignoring throwingValidator and throwing in general (which kind of defeats the purpose of using fp-ts in the first place) and focusing only on this specific request:

Refactor to get rid of intermediate variables, combining results of validateMonth and validateYear into a type of Either

You are probably looking for:

const monthAndYearResult: Either<
  Error,
  { month: Month, year: Year }
> = sequenceS(either)({
  month: validateMonth(month),
  year: validateYear(year)
})

"sequence"ing in general requires an instance of Traversable (the struct { year, month } in this case) and an instance of Applicative ( either in this case), and the semantics are the ones of aggregating different independent computations together.

If you explicitly want to ignore the result, usually a _ -suffix alternative is provided to accomplish this, but it isn't as of now in fp-ts v2.

To obtain a Either<Error, void> you can thus resort to:

const result = pipe(
  sequenceS(E.either)({ month: validateMonth(month), year: validateYear(year) }),
  E.map(constVoid)
)

Note that sequenceS is just one of the possible options, you could use sequenceT or array.sequence to obtain similar results, eg:

pipe(
  sequenceT(either)([validateMonth(month), validateYear(year)]),
  E.map(constVoid)
)

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