我正在“为哈萨克学到一个好东西”一书中学习filterM功能。由Miran Lipovaca撰写。对于以下示例:

keepSmall :: Int -> Writer [String] Bool
keepSmall x
   | x < 4 = do
       tell ["Keeping " ++ show x]
       return True
   | otherwise = do
      tell [show x ++ " is too large, throwing it away"]
      return False


通过将此功能与filterM一起使用所获得的结果如下:

> runWriter $ filterM keepSmall [9,1,5,2,10,3]
([1,2,3],["9 is too large, throwing it away","Keeping 1","5 is too large,
throwing it away","Keeping 2","10 is too large, throwing it away","Keeping 3"])


关于filterM结果的类型,我知道filterM具有以下类型声明:

filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]


由于本示例中使用的monad是Writer [String],由filterM生成的列表的类型是否为Writer [String] [Int]?如果是这种情况,这是为什么结果类型为([Int], [String])的原因,因为Writer w a等同于元组(a,w)

最佳答案

那是因为runWriter的类型

runWriter :: Writer w a -> (a, w)


从Hoogle来看,它实际上只是将编写器计算作为(结果,输出)对展开。这就是为什么您成对获得结果的原因。

一个小例子,看看它在其他情况下如何工作:

runWriter (tell $ return "Hello" ())
=> ((),"Hello")

10-08 12:41