在fp-ts和函数式编程中管理monad数组

时间:2020-04-10 02:31:49

标签: arrays typescript functional-programming monads fp-ts

我对函数式编程非常陌生,并且在数组上运行遍历很费力。

当我阅读this book时,似乎应该可以在Monad之间进行简单遍历,但是我无法用fp-ts来解决这个问题。

有人可以使用array.traverse/sequence或其他任何方式解释以下内容吗?

  1. 如何从TaskEither<Error, string[]>转到TaskEither<Error, Either<Error, string>[]>;还是有更好的方法将单个错误变成嵌套错误,同时保持简单输入?
  2. 如何从TaskEither<Error, Either<Error, string[]>>转到类似TaskEither<Error, Option<string[]>>之类的东西;还是应该映射该函数的结果以返回到Either

考虑以下简化代码,以更好地了解我们对这些数组的处理方式:

// 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 个答案:

答案 0 :(得分:4)

严格地说,Applicative足以满足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中使用更具体的错误类型。


让我们看一些程序并进行想象,所有程序都使用带有TaskEither的Web API。

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

▶将通过Error完全失败 或通过string[]获取的数据成功 计划4:(input: string) => TE.TaskEither<Error, E.Either<Error, string[]>>

fetch产生Error或成功。如果成功,则进一步处理Web数据-产生Error string[]

计划5:(input: string) => TE.TaskEither<Error, E.Either<Error, string>[]>

▶与计划4相同,但对Web数据进行后处理会产生多个 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