10

在 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-tschain样式中模拟这样的东西。main2这是我的尝试。

因为该async/await版本将所有中间结果引入本地范围(即foobar),所以很容易调用mkFooBar,这取决于这两个结果。

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

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

这是正确的方法吗?还是有更简单的版本更接近该async/await版本?

4

1 回答 1

16

根据您需要在以下计算中访问中间结果的次数,我建议使用Do(Haskell 的 do 表示法的近似值),或者通过手动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))
  );
}
于 2019-10-26T15:14:04.117 回答