Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uses and examples of monad transformers?

Tags:

haskell

monads

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?

like image 906
Babra Cunningham Avatar asked Feb 05 '23 02:02

Babra Cunningham


1 Answers

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 :: (* -> *) -> * -> *
like image 81
Benjamin Hodgson Avatar answered Feb 16 '23 03:02

Benjamin Hodgson