Transformers provides a monad transformer AccumT
.
Mtl provides a type class MonadAccum
.
There should be an instance (Monoid w, Monad m) ⇒ MonadAccum w (AccumT w m)
, but this is not the case.
AccumT
and MonadAccum
AccumT
is a monad transformer that’s similar to StateT
and WriterT
.
AccumT
definitionnewtype AccumT w m a = AccumT { runAccumT :: w -> m (a, w) }
Note
|
AccumT is actually isomorphic to StateT .
|
MonadState
allows you to modify a state (s
) similar to a variable in imperative languages.
MonadAccum
only appends to that variable (w
) via (<>)
from Monoid
.
MonadAccum
is similar to MonadWriter
in that way, but also allows you to look at what has already been accumulated.
MonadAccum
definitionclass (Monoid w, Monad m) => MonadAccum w m | m -> w where
add :: w -> m () -- Append to variable.
look :: m w -- Look at current state of variable.
The current instance
AccumT
's MonadAccum
instance currentlyinstance Monoid w => MonadAccum w (AccumT w Identity) where
add w = AccumT $ \ _ -> return ((), w)
look = AccumT $ \ w -> return (w, mempty)
Note
|
The |
Why is the MonadAccum
instance restricted to AccumT w Identity
???
We can easily implement this instance using the exact same methods, but without restricting the base monad. Therefore I propose to change this instance in the following way.
AccumT
's MonadAccum
instance proposed by meinstance (Monoid w, Monad m) => MonadAccum w (AccumT w m) where
add w = AccumT $ \ _ -> return ((), w)
look = AccumT $ \ w -> return (w, mempty)
instance Monad m => MonadExample (ExampleT m)
We should also use that pattern here.
Note
|
There are two exceptions in transformers, that don’t follow this rule.
|
The laws of MonadAccum
I’m going to check the laws of MonadAccum
to make sure I don’t break anything.
Important
|
If you find any mistakes, please let me know and I’ll fix them. |
MonadAccum
laws to prove
look *> look = look
add mempty = pure ()
add x *> add y = add (x <> y)
add x *> look = look >>= w → add x $> w <> x
To help with the proof we can use the laws of Monoid
and Monad
, because that’s how we want to constrain the MonadAccum
instance.
Monoid
laws
Left identity
mempty <> x = x
Right identity
x <> mempty = x
Associativity
x <> (y <> z) = (x <> y) <> z
Concatenation
mconcat = foldr (<>) mempty
Monad
laws
Left identity
return a >>= k = k a
Right identity
m >>= return = m
Associativity
m >>= (\x → k x >>= h) = (m >>= k) >>= h
Aside from these laws, we will only need definitions.
Proving law 1: look *> look = look
Substituting the (>>=)
definition makes the terms grow quite a bit, but we can use a direct proof.
|
|||
|
|
||
|
Definition of |
||
|
Definition of
|
||
|
Definition of |
||
|
Simplification of
do -block using Monad 's "left identity".
|
||
|
Monoid 's "right identity".
|
||
|
Definition of |
Proving law 2: add mempty = pure ()
This is a simple direct proof.
|
|
|
Definition of |
|
Definition of |
|
|
Proving law 3: add x *> add y = add (x <> y)
I guess you can probably figure out the approach by now.
Tip
|
It’s a direct proof. |
Unfortunately we will have to substitute (>>=)
again.
Overall the proof has the same structure as the proof of the first law.
|
|||
|
|
||
|
Definition of |
||
|
Definition of
|
||
|
Definition of |
||
|
Simplification of
do -block using Monad 's "left identity".
|
||
|
Definition of |
Proving law 4: add x *> look = look >>= \ w → add x $> w <> x
This time we will transform both sides of the equation and we will reach terms that are obviously equivalent.
We are starting with the left side.
|
|||
|
|
||
|
Definition of |
||
|
Definition of
|
||
|
Definition of |
||
|
Simplification of
do -block using Monad 's "left identity".
|
||
|
Monoid 's "right identity".
|
Note
|
Now we have to check that the right side is equivalent to this.
|
|
|||
|
Definition of
Simplification using function application. |
||
|
Definition of |
||
|
Simplification of
do -block using Monad 's "left identity".
|
||
|
Substituting
Functor 's ($>) using Monad .
|
||
|
Definition of
Simplification using function application. |
||
|
Definition of |
||
|
Simplification of
do -block using Monad 's "left identity".
|
||
|
Simplification of
do -block using Monad 's "left identity".
|
||
|
Monoid 's "right identity".
Monoid 's "left identity".
|
And thus we have reached our goal. Both sides of the equation are actually equivalent.