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

Managing array of monads in fp-ts and Functional Programming

我是函数式编程的新手,我在 运行 遍历数组方面遇到了很多困难。

当我阅读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.

有人可以使用 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
)

严格来说,Applicative 足以满足 traverse - 您不需要 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))
  );

具体选择的结构取决于您的环境和目的。 ▶ Option:强调absence/presence; ▶ Either:允许在 Left.

中使用更具体的错误类型

让我们看一些程序并想象一下,所有程序都使用 API 和 TaskEither

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

▶ 将 完全失败 Errorstring[] 成功获取数据

计划 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 相同,但 post- 处理 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