2

I am trying to nest writer monad twice using Monad Transformers. Here is a sketch:

import Control.Monad.Identity
import Control.Monad.Writer

data Struct = S Bool

instance Monoid Struct where
    mempty =  S True
    mappend (S a) (S b) = S (a && b)

data Collision = C Bool

instance Monoid Collision where
    mempty =  C False
    mappend (C a) (C b) = C (a || b)

type CSInt = WriterT Collision (WriterT Struct Identity) Int

foo :: Int -> CSInt
foo x = do (tell (S False)) ; return x

The foo function does not compile, as I need to use tell on Struct monad, not Collision. Is it possible at all?

krokodil
  • 1,326
  • 10
  • 18

1 Answers1

3

Having multiple similar layers is in fact one case where the mtl approach, which is meant to make lift implicit, has trouble as you mentionned. So you can use lift explicitly.

lift :: Writer Struct Bool -> WriterT Collision (Writer Struct) Bool

Thus

foo x = do lift (tell (S False)) ; return x
Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56
  • Thanks. If I may ask a follow-up question. How can I define function unwrapping all this, similar to `runWriter`. Signature: `runW :: CSInt -> (Int, Bool, Bool)` – krokodil Mar 03 '17 at 23:50
  • You can use `runWriter`/`runWriterT`, or just pattern match on the `CSInt` (see the definition of `WriterT`). – Li-yao Xia Mar 04 '17 at 00:11