采取类似的功能sequence
, filterM
, liftM2
, join
并思考它们如何为每个单子发挥作用IO
, []
, (->) a
, Writer
, State
。例如,sequence
for IO monad 顺序执行 IO 操作:
[IO a] -> IO [a]
写下签名并尝试使用它们。有些组合很有趣,有些则不那么有趣。
过滤器示例:
{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Monad
import Control.Monad.State
import Control.Monad.Reader
a = filterM (\x -> do putStrLn $ "Put " ++ show x ++ "?"
fmap (=="Y") getLine)
b = filterM (const [False,True])
c m xs = runState (filterM k xs) 0
where k x = do a <- get
let p = a + x <= m
when p $ put (a+x)
return p
d = filterM (flip id)
a
使用 IO 过滤列表 - 它向用户询问每个项目。
b
不确定地过滤列表 - 每个项目都不确定地包含和不包含。结果,你得到了 powerset。 (尝试一下!)
c
过滤列表,维护状态。在这种情况下,这是贪婪的背包 - 你有容量的背包m
并想要插入尽可能多的项目xs
.
d
过滤列表,保持只读状态。这并不有趣。我使用过滤函数作为状态,这给出了flip filter
.
单一功能filterM
可以做所有这些事情。
如果您为其他函数编写类似的代码,您将有足够的直觉来注意到其他地方的一元函数。例如,如何获得以下函数?
dup f x = f x x
那么呢dup' f x = f x x x
?