在 js 中使用 generator 模擬 Haskell 的 do 語法

又又又又開始畫畫了,看看這次仰臥起坐式努力能持續(xù)多久wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww

該特性僅供玩耍,有諸多限制,且在 Typescript 中不可用
Haskell 的 do 語法糖用于將 Monad 的組合計算扁平化,下面是一個對 List 的組合計算,以及在 js 中的等價代碼:
在編寫這種麻煩的嵌套代碼的時候,總幻想著有沒有方法將其扁平化以方便編寫和閱讀(確實有,見https://gcanti.github.io/fp-ts/guides/do-notation.html);最近發(fā)現(xiàn) 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]
的笛卡爾積即可,然后對結果集合中的每個值,都去創(chuàng)建 generator 并把值喂給他。一個例子見下面:
但顯然 yield 不一定只有兩個,我們需要支持任意個 yield 的情況,因此根據(jù)需求,這里編寫一個獲取任意數(shù)量列表的笛卡爾積的函數(shù):
然后就可以做抽象了:
玩耍一下:
再來考慮另一個典型的 Monad——Maybe,簡單抽象一波并實現(xiàn) map 和 flatMap:
考慮下面的 Haskell 代碼:
對應的 generator 和 flatMap 的形式是:
那么,如何去使用這個 generator 呢?考慮 Maybe 的特性——這里有兩個 Maybe 值,其中任何一個為 Nothing 時,結果就為 Nothing;若兩個都為 Just,則我們把它們的值取出來,應用給這個 generator,具體流程如下(注意該代碼與上面數(shù)組這樣操作的異同):
于是和數(shù)組一樣的問題——如果有多個 yield 怎么辦呢?我們需要某種方法去接受一個Array<Maybe<A>>
,返回一個Maybe<Array<A>>
,它的實現(xiàn)和數(shù)組的實現(xiàn)基本相同:
然后便可以實現(xiàn) maybe 的 do,它的實現(xiàn)和數(shù)組的版本完全相同,除了 sequence 的實例不同:
看來這個形式可能可以適用于所有 Monad。玩耍一下: