在 fp-ts 中链接一些异步任务,保留每个任务的结果

Chain some async tasks in fp-ts retaining every task's result

在 fp-ts 中,我试图将一些可能失败的异步任务与 TaskEither 链接在一起,但我需要稍后在链中使用中间任务的结果。

在这个例子中:

const getFoo = (a: string): Promise<Foo> => {};
const getBar = (foo: Foo): Promise<Bar> => {};
const mkFooBar = (foo: Foo, bar: Bar): Promise<FooBar> => {};

const async main1: Promise<FooBar> => {
  const a = "a";
  const foo = await getFoo(a);
  const bar = await getBar(foo);
  const fooBar = await mkFooBar(foo, bar);

  return Promise.resolve(fooBar);
};

const main2: Promise<FooBar> => {
  const a = "a";

  return pipe(
    TE.tryCatch(() => getFoo(a), e => e),
    TE.chain(foo => TE.tryCatch(() => getBar(foo), e => e)),
    TE.chain(bar => TE.tryCatch(() => mkFooBar(??, bar), e => e))
  );
};

main1 函数是针对此问题的 async/await 式解决方案。我想要做的是在 fp-ts chain 风格中模拟类似的东西。 main2 是我的尝试。

因为async/await版本将所有的中间结果都引入了本地作用域(即foobar),所以调用mkFooBar很容易,这取决于两者这些结果。

但在fp-ts版本中,中间结果被困在每个任务的范围内。

我认为使这个版本工作的唯一方法是使异步函数本身(即 getFoogetBar return 它们的参数,或者 TaskEither 包装器 return 参数,以便它们可以传递到链中的下一个函数。

这是执行此操作的正确方法吗?或者是否有更简单的版本更类似于 async/await 版本?

根据您需要在以下计算中访问中间结果的次数,我建议使用 Do(Haskell 的近似表示法),或者通过手动 mapping.

携带中间结果

鉴于:

import { pipe } from "fp-ts/function";
import * as TE from "fp-ts/TaskEither";

declare function getFoo(a: string): TE.TaskEither<unknown, Foo>;
declare function getBar(foo: Foo): TE.TaskEither<unknown, Bar>;
declare function mkFooBar(foo: Foo, bar: Bar): TE.TaskEither<unknown, FooBar>;

示例Do

function main2(): TE.TaskEither<unknown, FooBar> {
  return pipe(
    TE.Do,
    TE.bind("foo", () => getFoo("a")),
    TE.bind("bar", ({ foo }) => getBar(foo)),
    TE.chain(({ foo, bar }) => mkFooBar(foo, bar))
  );
}

手动映射示例:

function main3(): TE.TaskEither<unknown, FooBar> {
  return pipe(
    getFoo("a"),
    TE.chain(foo =>
      pipe(
        getBar(foo),
        TE.map(bar => ({ foo, bar }))
      )
    ),
    TE.chain(({ foo, bar }) => mkFooBar(foo, bar))
  );
}