haskellmonadswriter-monad

Understanding example on Writer Monad


I am learning about the Writer Monad on the book Learn You A Haskell.

this is the piece of code:

import Control.Monad.Writer

logNumber :: Int -> Writer [String] Int
logNumber num = writer (num, ["Got number: " ++ show num])

multWithLog :: Writer [String] Int
multWithLog = do
  a <- logNumber 3
  b <- logNumber 5
  return (a * b)

When running multWithLog, this is the result:

*Main> runWriter multWithLog
(15,["Got number: 3","Got number: 5"])

On this lines:

a <- logNumber 3
b <- logNumber 5

It is easy to see that a = 3 and b = 5, since both of them are being multiplied on the return function.

What I don't understand is why those values are 3 and 5. Shouldn't a and b be the values that contain inside the Writer Monad? In this case tuples?

For example, with this was with the Maybe Monad, a and bwould be 3 and 5:

do
  a <- Just 3
  b <- Just 5
  return (a * b)

In this case it makes sense to me, since a and b receive the content inside Just. But with the initial example, a and b only receive part of the value.


Solution

  • It is easy to see that a = 3 and b = 5, since both of them are being multiplied on the return function. What I don't understand is why those values are 3 and 5. Shouldn't a and b be the values that contain inside the Writer Monad? In this case tuples?

    No. I think the simplest way to answer this is to just implement the Writer type and study its Monad class instance:

    newtype Writer w a = Writer { runWriter :: (a, w) }
    
    instance Functor (Writer w) where
      fmap f (Writer (a, w)) = Writer (f a, w)
    
    instance Monoid w => Applicative (Writer w) where
      pure a = Writer (a, mempty)
      Writer (f, w) <*> Writer (a, w') = Writer (f a, w <> w')
    
    instance Monoid w => Monad (Writer w) where
      return = pure
      Writer (a, w) >>= f = let (b, w') = runWriter (f a)
                            in Writer (b, w <> w')
    
    tell :: w -> Writer w ()
    tell w = Writer ((), w)
    

    As you can see in the instance method for >>=, the function f is applied to the a value, not the whole tuple. The syntax a <- logNumber 3 is desugared using >>=, so the value that's bound to a will be the first element of the tuple that Writer wraps around.