Строго говоря, Аппликативный достаточно для traverse
- вам не нужна монада.
TaskEither<Error, string[]>
до TaskEither<Error, Either<Error, string>[]>
?
const program1 = (input: string) =>
P.pipe(
toItems(input),
TE.map(A.map(toTitle))
);
TaskEither<Error, Either<Error, string[]>>
до TaskEither<Error, Option<string[]>>
?
const program2 = (input: string) =>
P.pipe(
toItems(input),
TE.map(items => A.array.traverse(O.option)(items, clean))
);
Конкретная выбранная конструкция зависит от вашей среды и цели. ▶ Option
: акцент на отсутствие / присутствие; ▶ Either
: допускает более конкретный тип ошибки в Left
.
Давайте рассмотрим некоторые программы и представим, что все используют веб-API с TaskEither
.
Программа 3:
(input: string) => TE.TaskEither<Error, string[]>
▶ либо потерпит неудачу полностью с Error
, либо преуспеет с string[]
извлеченными данными
Программа 4:
(input: string) => TE.TaskEither<Error, E.Either<Error, string[]>>
▶ fetch
приведет к Error
или преуспевает. В случае успеха обработайте веб-данные дальше - в результате Error
или string[]
Программа 5:
(input: string) => TE.TaskEither<Error, E.Either<Error, string>[]>
▶ такая же, как и в Программе 4, но пост-обработка веб-данных приводит к несколько Either
результатов - каждый может потерпеть неудачу или преуспеть индивидуально
Вот реализация программы 4 в качестве некоего промежуточного положения:
const program4 = (
input: string
): TE.TaskEither<Error, E.Either<Error, string[]>> =>
P.pipe(
toItems(input), // TE.TaskEither<Error, string[]>
TE.map(items => // TE.TaskEither<E, E.Either<Error, string[]>>
A.array.traverse(E.either)( // E.Either<Error, string[]>
items,
F.flow( // E.Either<Error, string>
toTitle,
E.chain(s => E.fromOption(() => Error())(clean(s)))
)
)
)
);
Codesandbox