Reputation: 253
in Haskell, I would like to make a Writer monad an instance of a monoid:
instance (Monoid a) => Monoid (Writer (Sum Int) a) where
mempty = return mempty
w1 `mappend` w2 = writer((s++t, s'++t'), Sum (m+n)) where
((s,s'), Sum m) = runWriter w1
((t,t'), Sum n) = runWriter w2
So, intuitively, if the "data" type of the Writer monad is a monoid, I want to be able to consider the whole Writer thing as a monoid as well (as implemented by mempty and mappend.
This doesn't work, though: The GHCI compiler says
Illegal instance declaration for `Monoid (Writer (Sum Int) a)'
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Monoid (Writer (Sum Int) a)'
and I really don't know what type is supposed to be a synonym here and how I can conform to the compiler's rules.
Upvotes: 1
Views: 346
Reputation: 60463
Everybody is doing so much work. The bind operator on a writer monad already appends the w
s. This also means it will work for an arbitrary base monad.
instance (Monoid w, Monoid a, Monad m) => Monoid (WriterT w m a) where
mempty = return mempty
mappend = liftA2 mappend
At this point it's clear that even WriterT
is superfluous, and this is actually an "instance" of this general instance
instance (Monoid a, Monad m) => Monoid (m a) where
-- same
but Haskell's class system doesn't really permit instances like this -- it would match every monoid built from a type constructor. For example this instance would match Sum Int
, and then fail because Sum
isn't a monad. So you have to specifify it seperately for each monad you're interested in.
Upvotes: 3
Reputation: 52029
Writer
is a type alias (link)
type Writer w = WriterT w Identity
so use WriterT ... Identity
instead. You'll still need to enable FlexibleInstances.
Perhaps this is what you are after:
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad.Trans.Writer
import Data.Monoid
import Data.Functor.Identity
instance (Monoid w, Monoid a) => Monoid (WriterT w Identity a) where
mempty = return mempty
m1 `mappend` m2 = writer (a1 <> a2, w1 <> w2)
where
(a1,w1) = runWriter m1
(a2,w2) = runWriter m2
Of course, this could be generalized to an arbitrary Monad instead of Identity.
Upvotes: 2