打字稿:如何使用fp-ts进行kleisli撰写(monadic撰写)Promise monad

时间:2020-10-24 14:08:35

标签: typescript functional-programming fp-ts

如何使用 fp-ts 将两个kleisli箭头(函数)f:A -> Promise Bg: B -> Promise C组合到h:A -> Promise C中?

我对Haskell很熟悉,所以我会这样问:>=>(鱼运算符)等于什么?

1 个答案:

答案 0 :(得分:3)

承诺由fp-ts中的TaskTaskEither单子表示,它们是异步计算。 TaskEither还会对失败进行建模,并且与Task<Either<...>>相同。

Kleisli Arrows可以通过单子chainflow(管道运算符)的操作来组成。结果类似于>=>运算符在Haskell中的应用。

让我们以TaskEither为例:
const f = (a: A): Promise<B> => Promise.resolve(42);
const g = (b: B): Promise<C> => Promise.resolve(true);
使用tryCatchK 1 将返回Promise的函数转换为返回TaskEither的函数:
import * as TE from "fp-ts/lib/TaskEither";
const fK = TE.tryCatchK(f, identity); // (a: A) => TE.TaskEither<unknown, B>
const gK = TE.tryCatchK(g, identity); // (b: B) => TE.TaskEither<unknown, C>
两者都组成:
const piped = flow(fK, TE.chain(gK)); // (a: A) => TE.TaskEither<unknown, C>

这是Codesandbox的复制粘贴块:

// you could also write:
// import { taskEither as TE } from "fp-ts";
import * as TE from "fp-ts/lib/TaskEither";
// you could also write:
// import {pipeable as P} from "fp-ts"; P.pipe(...)
import { flow, identity, pipe } from "fp-ts/lib/function";
import * as T from "fp-ts/lib/Task";

type A = "A";
type B = "B";
type C = "C";
const f = (a: A): Promise<B> => Promise.resolve("B");
const g = (b: B): Promise<C> => Promise.resolve("C");

// Alternative to `identity`: use `toError` in fp-ts/lib/Either
const fK = TE.tryCatchK(f, identity);
const gK = TE.tryCatchK(g, identity);

const piped = flow(fK, TE.chain(gK));

const effect = pipe(
  "A",
  piped,
  TE.fold(
    (err) =>
      T.fromIO(() => {
        console.log(err);
      }),
    (c) =>
      T.fromIO(() => {
        console.log(c);
      })
  )
);

effect();

为什么没有承诺?

JavaScript承诺不遵守monadic API,例如eagerly computed 2 。在函数式编程中,副作用会尽可能延迟,因此我们需要使用TaskTaskEither形式的兼容包装器。


1 identity仅在失败情况下转发错误。您也可以使用toError
如果您对历史原因感兴趣, 2 Incorporate monads and category theory #94值得一读。