问题
I'm very new to Functional Programming and I'm struggling a lot with running traverse on arrays.
When I read this book it seems that I should be able to simply traverse between Monads but I can't wrap my head around this concept with fp-ts.
Can someone explain the following using array.traverse/sequence
or any other ways please?
- How can I go from
TaskEither<Error, string[]>
toTaskEither<Error, Either<Error, string>[]>
; or is there a better way to go from a single error to nested errors while keeping the typings simple? - How can I go from
TaskEither<Error, Either<Error, string[]>>
to something likeTaskEither<Error, Option<string[]>>
or something similar; or should we map the result of that function to turn back toEither
?
Consider the following simplified code to have a better idea what we are doing with these arrays:
// helper functions
declare function toItems(input: string): TaskEither<Error, string[]);
declare function toTitle(item: string): Either<Error, string>;
declare function clean(item: string): Option<string>;
// This is what I tried so far
const program = (input: string) => pipe(
toItems(input), // we have TaskEither<Error, string[]>
TE.map(items => array.traverse(either)(items, toTitle)), // now we have TaskEither<Error, Either<Error, string[]>>
TE.map(items => array.traverse(option)(items, clean)), // nothing works with clean() from here
)
回答1:
Strictly spoken, Applicative is sufficient for traverse
- you don't need a monad.
TaskEither<Error, string[]>
toTaskEither<Error, Either<Error, string>[]>
?
const program1 = (input: string) =>
P.pipe(
toItems(input),
TE.map(A.map(toTitle))
);
TaskEither<Error, Either<Error, string[]>>
toTaskEither<Error, Option<string[]>>
?
const program2 = (input: string) =>
P.pipe(
toItems(input),
TE.map(items => A.array.traverse(O.option)(items, clean))
);
The concrete chosen structure depends your environment and purpose. ▶ Option
: emphasis on absence/presence ; ▶ Either
: permits a more concrete error type in Left
.
Let's look at some programs and imagine, all use a web API with TaskEither
.
(input: string) => TE.TaskEither<Error, string[]>
▶ will either fail completely with Error
or succeed with string[]
fetched data
(input: string) => TE.TaskEither<Error, E.Either<Error, string[]>>
▶ fetch
results in Error
or succeeds. If succeeded, process web data further - resulting in Error
or string[]
(input: string) => TE.TaskEither<Error, E.Either<Error, string>[]>
▶ same as Program 4, but post-processing of web data results in multiple Either
results - each can fail or succeed individually
Here is implementation of program 4 as some kind of middle ground:
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
来源:https://stackoverflow.com/questions/61133166/managing-array-of-monads-in-fp-ts-and-functional-programming