1
votes

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.

2
Do not conform to the rules: relax them by enabling -XTypeSynonymInstances as the compiler suggests. -XFlexiblesInstances may also be required.user2407038

2 Answers

3
votes

Everybody is doing so much work. The bind operator on a writer monad already appends the ws. 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.

2
votes

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.