Managing array of monads in fp-ts and Functional Programming

血红的双手。 提交于 2020-07-07 12:04:29

问题


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?

  1. How can I go from TaskEither<Error, string[]> to TaskEither<Error, Either<Error, string>[]>; or is there a better way to go from a single error to nested errors while keeping the typings simple?
  2. How can I go from TaskEither<Error, Either<Error, string[]>> to something like TaskEither<Error, Option<string[]>> or something similar; or should we map the result of that function to turn back to Either?

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[]> to TaskEither<Error, Either<Error, string>[]>?

const program1 = (input: string) =>
  P.pipe(
    toItems(input),
    TE.map(A.map(toTitle))
  );

TaskEither<Error, Either<Error, string[]>> to TaskEither<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.

Program 3: (input: string) => TE.TaskEither<Error, string[]>

▶ will either fail completely with Error or succeed with string[] fetched data

Program 4: (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[]

Program 5: (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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!