假设我们有一个函数数组,每个函数取一个数字并返回一个TaskEither<string, number>
。
let tasks: (n: number) => TE.TaskEither<string, number>[] = [];
目标是提供一个初始值,然后按顺序执行每个函数和另一个函数的结果。
reducer是一个选项:
const taskEitherReducer = <E, S>(acc: TE.TaskEither<E, S>, cur: (x: S) => TE.TaskEither<E, S>) =>
pipe(TE.map(cur)(acc), TE.flatten);
然后可以定义一个接受初始值和任务数组的函数:
const runTasks =
(initialValue: number) =>
A.reduce(
TE.of(initialValue),
(
acc: TE.TaskEither<string, number>,
cur: (ord: number) => TE.TaskEither<string, number>
) => taskEitherReducer(acc, cur));
最后:
runTasks(1)(tasks);
问题:
- 所有这些过程看起来像一个组合,
TaskEither.chain()
允许组合函数,但强制知道我们想要组合哪些函数。在这种情况下,我们不知道有多少个这样的函数,只知道它的类型。
那么,fp-ts
中有没有什么内置的方法来实现这种动态构图呢?
不确定这将回答您的问题,但是有一个reduceM
组合子,它概括了返回一元值的函数的通常减少。你需要为函数返回的类型提供一个Monad
实例,为你要折叠的类型提供一个Foldable
实例。
import * as TE from "fp-ts/TaskEither";
import * as F from "fp-ts/Foldable";
import * as A from "fp-ts/Array";
import { pipe } from "fp-ts/lib/function";
const tasks: ((n: number) => TE.TaskEither<string, number>)[] = [];
const reduceTaskEither = F.reduceM(TE.Monad, A.Foldable);
const runTasks = (initialValue: number) =>
pipe(
tasks,
reduceTaskEither(initialValue, (n, f) => f(n))
);