简体   繁体   中英

Make Monoid instance of Writer (Haskell)

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM