I'm going over Monad Transformers and I understand their main role is to provide a monadic container to hold monads, of different types, which provides a common interface from which to manipulate the 'nested' monads within a computation.
I've attempted to implement my own transformer:
data CustomTransformer a = CustomTransformer
class TransformerClass m a where
lift :: m a -> CustomTransformer (m a)
instance TransformerClass Maybe a where
lift (Just a) = CustomerTransformer (Just a)
Going through this paper, I understand that this is incorrect. Their example shows:
class MonadTrans r where
lift :: Monad m => m a -> (r m) a
This nests the action a
in the monad transformer r m
.
I don't understand how using a monad transformer aids in dealing with multiple monadic types within a computation? Can anyone provide a simple explanation and example?
I find it helpful to understand the kinds that are at play here.
Firstly, as you know, a monad is a type constructor m :: * -> *
paired with two operations, return :: a -> m a
and (>>=) :: m a -> (a -> m b) -> m b
.
class Monad (m :: * -> *) where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
The idea of monad transformers is that they're a sort of type-level function that turns a monad into another monad. So given that a monad is a single-parameter type constructor * -> *
, a monad transformer must be a type of kind (* -> *) -> (* -> *)
, or (* -> *) -> * -> *
once you remove the parentheses. A monad transformer is a two-parameter type, whose first parameter is a monad and whose second parameter is a value.
More concretely, a monad transformer is a type t :: (* -> *) -> * -> *
, such that whenever m
is a monad t m
is also a monad. We also require that t m
be a larger monad than m
, in the sense that any action in m
can be embedded in t m
.
class MonadTrans t where
transform :: Monad m :- Monad (t m)
lift :: Monad m => m a -> t m a
I'm using the "entailment" operator :-
from Kmett's constraints
package in the definition of transform
; transform
is a proof that m
being a Monad
implies that t m
is a Monad
. (The version of MonadTrans
in transformers
omits the transform
member because when it was written GHC didn't support the :-
operator.)
Importantly, t m a
(aka (t m) a
) means something different than t (m a)
. The former is a two-parameter type t
applied to m
and a
. The latter is a one-parameter type t
applied to m a
.
A very simple - because I'm on my phone - example of what a definition of a monad transformer looks like:
newtype IdentityT m a = IdentityT { runIdentityT :: m a }
instance Monad m => Monad (IdentityT m) where
return = IdentityT . return
IdentityT m >>= f = IdentityT $ m >>= (runIdentityT . f)
instance MonadTrans IdentityT where
transform = Sub Dict
lift = IdentityT
Note that IdentityT
is a two-parameter datatype; the first parameter m :: * -> *
is a monad and the second parameter a :: *
is a regular type.
ghci> :k IdentityT
IdentityT :: (* -> *) -> * -> *
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With