I'm learning Haskell from the "Learn you a Haskell for Great Good" tutorial and I've got to the part on writer monads. Here's the example that I can't figure out.
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?
I am trying understand how the monoidic value w
in the Writer w a
monad returned by the do
block got changed. The tutorial did not go into details on how the mappend
ing took place.
The type declaration for Writer
and the instance declaration for Writer
as a monad is given by the tutorial as
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')
if return x
results in Writer (x, mempty)
as per the instance declaration and mempty
for monoid [a]
is []
, shouldn't return (a*b)
, which amounts to return (3*5)
, evaluate to (15,[])
?
ghci> return (15) :: Writer [String] Int
WriterT (Identity (15,[]))
I gave the above command to ghci and it returns a WriterT
type value, the tuple contains an empty list as expected.
multWithLog :: Writer [String] Int
multWithLog = logNumber 3 >>= (\a ->
logNumber 5 >>= (\b ->
return (a*b)))
I've rewritten the do
block using bind operators instead. The above code gave identical result as the original code in the tutorial.
I'm under the impression that >>=
only extracted Int
3
from the result of logNumber 3
and gave it to (\a -> logNumber 5 ...etc.)
, which then did the logNumber
function on a different value (5
) and so on. How did these operations lead to the [String]
part of the Writer monad being altered?
return
means in Haskell - which is pretty common, so don't feel bad about it. In most imperative languages areturn x
at the bottom of a function definition means that the result of the function isx
. Butreturn
in Haskell is just a function, and the actual value ofmultWithLog
is the expression obtained from evaluating that entiredo
block, not just the last line withreturn
. In particular the successive lines of thedo
block are connected by applications of>>=
, and it is this which appends the log values "behind the scenes". – Robin Zigmondm >>= f
doesn't simply return whateverf
returns. It creates a new Writer value using bothm
and the return value off
. – chepner