1

我对函数式编程比较陌生,对fp-ts也很陌生,所以我很难理解提供的 util 函数。我目前正在尝试弄清楚如何将TaskEithers 作为数组中的后备处理。

我有一个函数来获取一些数据id,它返回ErrorSuccess

declare function getData(id: number): TaskEither<Error, Success>

我想要的是一个函数,它将遍历一些ids 数组(例如[1, 2, 3, 4]),为每个数组请求数据。TaskEither它应该在成功并返回的第一个停止Right<Success>。如果所有的TaskEithers 都失败了,它应该将它们的错误收集到一个Left<Error[]>.

import { map } from 'fp-ts/lib/Array';

const program: TaskEither<Error[], Success>
  = pipe(
    [1, 2, 3, 4],
    map(getData),
    /*
     * Now I have a TaskEither<Error, Success>[]
     * What comes next?
     */
  );

我尝试了类似的方法,但有一些明显的问题(如下所述):

import { map, sequence } from 'fp-ts/lib/Array';
import { map as mapTaskEither } from 'fp-ts/lib/TaskEither'

const program: TaskEither<Error, Success>
  = pipe(
    [1, 2, 3, 4],
    map(getData),
    sequence(taskEither), // Now this gets to a TaskEither<Error, Success[]>
    mapTaskEither(successes => successes[0])
  );

这种方法的问题

  1. getData在所有 ID 上运行,第一次成功时不会短路
  2. 如果有任何getDatas 错误,它就会出错。所以如果有错误,即使成功getData(4)了总体也会出错programgetData(1)
  3. 它不会将错误收集到一个数组中Error[]
4

1 回答 1

1

感谢这个有见地的评论,我想出了一个解决方案。

为这个 util 函数命名有助于提高可读性:

import { TaskEither, taskEither, swap } from 'fp-ts/lib/TaskEither';
import { sequence, map } from 'fp-ts/lib/Array';
import { pipe } from 'fp-ts/lib/function';

const firstRightOrLefts: <A, B>(taskEithers: TaskEither<A, B>[]) => TaskEither<A[], B>
    = taskEithers => pipe(
        taskEithers,
        map(swap),
        sequence(taskEither),
        swap,
    );

然后它可以像这样使用:

import { map } from 'fp-ts/lib/Array';

const program: TaskEither<Error, Success>
  = pipe(
    [1, 2, 3, 4],
    map(getData),
    getFirstRightOrLefts, // <-- Woohoo!
  );
于 2020-07-08T00:07:36.687 回答