【问题标题】:Typescript: How to kleisli compose (monadic compose) Promise monad using fp-tsTypescript: How to kleisli compose (monadic compose) Promise monad using fp-ts
【发布时间】:2020-10-24 14:08:35
【问题描述】:

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

我对 Haskell 很熟悉,所以我会这样问:>=>(fish operator) 的等价物是什么?

【问题讨论】:

  • this 是您要找的吗?如果是这样,我可以写一个答案;如果没有,请详细说明您想要什么。
  • @jcalz monad.chain(f(a), g); 这部分是我要找的。谢谢你。我还认为我可以使用 Task 而不是为 Promise 实现整个 Monad。
  • @Ingun전인건 我认为 FP-TS 很难在无点样式 (GitHub Issue) 的上下文中推断类型,因此您必须临时手动编写箭头。此外,如果您想要具有并行操作语义的单个类型,您可能只需要实现自己的异步类型。

标签: typescript functional-programming fp-ts


【解决方案1】:

Promise 由 fp-ts 中的 TaskTaskEither monad 表示,它们是异步计算。 TaskEither 额外模拟失败,与Task<Either<...>> 相同。

Kleisli 箭头可以通过chain 单子操作和flow(管道操作符)组成。结果类似于在 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 Promise 不遵循monadic API,例如它们是eagerly computed 2。在函数式编程中,副作用被尽可能地延迟,因此我们需要使用TaskTaskEither 形式的兼容包装器。


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

【讨论】:

猜你喜欢
  • 2022-12-19
  • 2022-12-02
  • 1970-01-01
  • 2022-12-02
  • 2022-12-28
  • 2022-12-02
  • 2022-12-19
  • 2022-12-26
  • 2022-12-02
相关资源
最近更新 更多