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.
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 returnWriter 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 andreturn
methods, likea
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')