我正在从“Learn you a Haskell for Great Good”教程中学习 Haskell,并且我已经完成了关于writer monads的部分。这是我无法弄清楚的例子。
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = writer (x, ["Got number: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (a*b) -- shouldn't return (3*5) result in (15,[]) ?
ghci> runWriter $ multWithLog
(15,["Got number: 3","Got number: 5"]) -- how did that happen?
我试图了解块返回w的Writer w amonad中的 monoidic 值是如何do改变的。本教程没有详细说明mappending 是如何发生的。
作为 monad的类型声明Writer和实例声明Writer由教程给出
newtype Writer w a = Writer { runWriter :: (a, w) }
instance (Monoid w) => Monad (Writer w) where
return x = Writer (x, mempty)
(Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
如果根据实例声明return x产生结果Writer (x, mempty)并且mempty对于 monoid[a]是[],不应该return (a*b),等于return (3*5),评估为(15,[])?
ghci> return (15) :: Writer [String] Int
WriterT (Identity (15,[]))
我把上面的命令给了 ghci ,它返回一个WriterT类型值,元组包含一个空列表,正如预期的那样。
multWithLog :: Writer [String] Int
multWithLog = logNumber 3 >>= (\a ->
logNumber 5 >>= (\b ->
return (a*b)))
我改用do绑定运算符重写了该块。上面的代码给出了与教程中的原始代码相同的结果。
我的印象是>>=只Int 3从结果中提取logNumber 3并将其提供给(\a -> logNumber 5 ...etc.),然后logNumber在不同的值 ( 5) 上执行函数,依此类推。这些操作是如何导致[String]Writer monad 的部分被改变的?