欢迎光临散文网 会员登陆 & 注册

在 js 中使用 generator 模拟 Haskell 的 do 语法

2023-01-30 20:14 作者:友纪V-入OP  | 我要投稿

又又又又开始画画了,看看这次仰卧起坐式努力能持续多久wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww

该特性仅供玩耍,有诸多限制,且在 Typescript 中不可用

Haskell 的 do 语法糖用于将 Monad 的组合计算扁平化,下面是一个对 List 的组合计算,以及在 js 中的等价代码:

在编写这种麻烦的嵌套代码的时候,总幻想着有没有方法将其扁平化以方便编写和阅读(确实有,见https://gcanti.github.io/fp-ts/guides/do-notation.html);最近发现 js 的 generator 能够去模拟 do 语法,这里做一下记录。

generator 既能让它往外输出值,也能给它喂值,这里利用后者:

考虑下面 Haskell 代码:

其可以描述为,对任意[1, 2, 3]中的值 a,对任意[1, 2, 3]中的值 b,获取所有 a+b 和 a*b 的值,将所有结果组成列表,最后结果为[1 + 2, 1 * 2, 1 + 3, 1 * 3, 1 + 4, 1 * 4, 2 + 2, 2 * 2, 2 + 3, 2 * 3, 2 + 4, 2 * 4, 3 + 2, 3 * 2, 3 + 3, 3 * 3, 3 + 4, 3 * 4]

如何做到这样呢?显然我们只需要获取[1, 2, 3][2, 3, 4]的笛卡尔积即可,然后对结果集合中的每个值,都去创建 generator 并把值喂给他。一个例子见下面:

但显然 yield 不一定只有两个,我们需要支持任意个 yield 的情况,因此根据需求,这里编写一个获取任意数量列表的笛卡尔积的函数:

然后就可以做抽象了:

玩耍一下:

再来考虑另一个典型的 Monad——Maybe,简单抽象一波并实现 map 和 flatMap:

考虑下面的 Haskell 代码:

对应的 generator 和 flatMap 的形式是:

那么,如何去使用这个 generator 呢?考虑 Maybe 的特性——这里有两个 Maybe 值,其中任何一个为 Nothing 时,结果就为 Nothing;若两个都为 Just,则我们把它们的值取出来,应用给这个 generator,具体流程如下(注意该代码与上面数组这样操作的异同):

于是和数组一样的问题——如果有多个 yield 怎么办呢?我们需要某种方法去接受一个Array<Maybe<A>>,返回一个Maybe<Array<A>>,它的实现和数组的实现基本相同:

然后便可以实现 maybe 的 do,它的实现和数组的版本完全相同,除了 sequence 的实例不同:

看来这个形式可能可以适用于所有 Monad。玩耍一下:


在 js 中使用 generator 模拟 Haskell 的 do 语法的评论 (共 条)

分享到微博请遵守国家法律