Search code examples
haskellfunctional-programmingmonadstypeclasswriter-monad

The Writer monad and its type declaration


I am trying to get a better understanding of Monads and I am currently looking into the Write Monad in http://learnyouahaskell.com/for-a-few-monads-more#writer

I don't understand its type declaration that seemingly consists of two types, nor do I understand its type constraint.

In the examples they specify that:

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')  

In action it looks like this:

ghci> runWriter (return 3 :: Writer String Int)  
(3,"")  
ghci> runWriter (return 3 :: Writer (Sum Int) Int)  
(3,Sum {getSum = 0})  
ghci> runWriter (return 3 :: Writer (Product Int) Int)  
(3,Product {getProduct = 1})  

But why does it return Writer String Int rather than a tupple of these elements, I wonder?

Could I have made a Monad type (e.g. Writer2) that would return Writer String Int Float ?

Furthermore it confuses me why the instantiation only takes (Monoid w) => Monad (Writer w) rather than (Monoid w a) => Monad (Writer w a) . And why isn't the w listed in the Monad binding and return methods, like a would be included in the type signatur for the type constraint (Num a) => ... This might be because I have some conceptual shortcoming in this area.


Solution

  • It might help your confusion if you replace the more generic Writer String with the more concrete StringWriter:

    newtype StringWriter a = StringWriter (a, String)
    
    instance Monad StringWriter where  
        return x = StringWriter (x, "")  
        (StringWriter (x, v)) >>= f = let (StringWriter (y, v')) = f x
                                      in StringWriter (y, v ++ v')
    

    But why does it return Writer String Int rather than a tuple of these elements, I wonder?

    A Writer String Int is just another way to say a tuple (Int, String). Look at the signature of runWriter, it converts from a Writer String Int to (Int, String):

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

    and the data constructor Writer, which converts into the reverse direction:

    (a, w) -> Writer w a
    

    Could I have made a Monad type (e.g. Writer2) that would return Writer String Int Float?

    I am not exactly sure what you mean by that but for a monad it is essential that it works for all types, so the a in StringWriter a is important, when you declare the monad instance. (Confusingly enough, you need it but don't write it: instance Monad StringWriter.)

    Furthermore it confuses me why the instantiation only takes (Monoid w) => Monad (Writer w) rather than (Monoid w a) => Monad (Writer w a).

    A couple of points:

    • Monoid w a does not make sense, since the Monoid typeclass has only one parameter:

      class Semigroup a => Monoid a where
      
    • Monad (Writer w a) does not make sense, because the argument to the Monad typeclass is not a type but a type constructor (with one argument). This is in contrast to the Monoid typeclass which takes a concrete type (instead of a type constructor of one parameter). That is, instance Monad StringWriter is correct but instance Monad (StringWriter Int) is not. Note that StringWriter Int is a type but StringWriter is a type constructor taking one argument.

    And why isn't the w listed in the Monad binding and return methods, like a would be included in the type signature for the type constraint (Num a) => ...

    It is not listed, because the function type declarations are not listed. You could enable the InstanceSigs extension and write them explicitly:

    {-# LANGUAGE InstanceSigs #-}
    
    instance (Monoid w) => Monad (Writer w) where
    
      return :: (Monoid w) => a -> Writer w a
      return x = Writer (x, mempty)  
    
      (>>=) :: (Monoid w) => Writer w a -> (a -> Writer w b) -> Writer w b
      (Writer (x,v)) >>= f = let (Writer (y, v')) = f x
                             in Writer (y, v `mappend` v')