2023年3月16日 星期四

Haskell當中的List monad用do notation時如何作到篩選

很神奇,在List monad中do notation的篩選功能guard不是語言內核,而是一個用Haskell寫的函數而已。

Haskell report 2010對於guard有如下敘述:

guard :: MonadPlus m => Bool -> m ()

guard b is return () if b is True, and mzero if b is False



意思是任何MonadPlus都有guard。MonadPlus都必須有mzero這一元素,在List當中就是[]。return () 在List當中的意思就是傳回[()],而。List的 xs >>=  f (f是一個 a -> [a]的函數)運算是傳回 [y | x <- xs, y <- f x]。這只是一個概念上的說明,內部不是這樣實做,但是效果一樣。以這段程式為例說明guard的運作方式:

import Control.Monad

listGuard :: [Int] -> [(Int, ())]
listGuard xs = do x <- xs
                  y <- guard(even x)
                  return (x,y)


如果接著輸入 listGuard [1..4],得到的結果是[(2,()),(4,())]。
把 do nation 還原成 >>=:

xs >>= \x -> guard(even x) >>= \y -> return (x, y)  

在收集y的過程中,注意到 y <- f x ,假如 f x 得到[],y就不會出現在最左端的
[y | ...],所以相應的x就被篩選掉了。因為最後的 return 是根據收集到的y一個一個執行的。listGuard程式碼當中如果把 return (x, y)改成return x,再把函數的型態做適當修改,把 y <-刪除掉,就是一般的篩選功能了。此時雖然沒有用到y,但是暗中仍是根據收集到的y一個一個執行的。

沒有留言: