Reputation: 5073
newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }
instance (Monad m, Error e) => Monad (ErrorT e m) where
return a = ErrorT $ return (Right a)
m >>= k = ErrorT $ do
a <- runErrorT m
case a of
Left l -> return (Left l)
Right r -> runErrorT (k r)
fail msg = ErrorT $ return (Left (strMsg msg))
Let's consider above piece of code. It makes me trouble to understand it. How does it work in context of Monads/Transormer Monads? Especially, the problem is understanding why return a = ErrorT $ return (Right a)
return return (Right a)
. I don't understand why just it.
The second issue. m >>= k
is basically understood by me ( I hope so ;) )but why in Right r -> runErrorT (k r)
appeared runError?
Upvotes: 0
Views: 106
Reputation: 116139
ErrorT
is just a wrapper. That is, the type ErrorT e m a
is isomorphic to the type m (Either e a)
: the latter can be converted into the former by the constructor ErrorT
, while its associated destructor runErrorT
converts in the other direction.
The definitions of return
and >>=
must wrap/unwrap values to respect the types. This wrapping/unwrapping has no concrete effect at run time, it is just there so that the program type checks and the compiler can choose the correct monad instance (m
or ErrorT e m
, depending on whether a value it's wrapped).
Here
return a = ErrorT $ return (Right a)
the part return (Right a)
has type m (Either e a)
, but we want ErrorT e m a
instead. Applying ErrorT
fixes that.
Similarly, in this code:
m >>= k = ErrorT $ do
a <- runErrorT m
case a of
Left l -> return (Left l)
Right r -> runErrorT (k r)
we need to return ErrorT m e b
. This is done by using ErrorT
applied to some value of type m (Either e b)
. The part Left l
has type Either e b
, so return
adds the last m
on top. Instead the part k r
has type ErrorT e m b
, so we need to unwrap it with runErrorT
(so that the whole do
can be rewrapped again).
Upvotes: 3