Indexed Monads
TweetIn this post, I am going to introduce indexed monads, which generalize monads with additional type parameters carrying the information about the computational effects.
Motivation
The State monad represents computations with a state that can be queried and updated. For example, an Int state is queried and updated during the computation of c in the following example. While the value of the state is changed from 0 to 1, the type of the state remains the same during the entire computation.
import Control.Monad.State
test1 = runState c (0::Int) where
c = do
v <- get
put (succ v)
return v
-- (0, 1)This is okay in most cases, but we sometimes want to express a computation where not only the value but also the type of the state can be changed. The vanilla State monad is not general enough to express this requirement.
Indexed Monads
Indexed monads are a generalization of monads that index each monadic type by an initial (type)state and a final (type)state. m is a type constructor for three type arguments, p, q and a. The argument a is the type of values produced by the monadic computation. p and q represent the types of the state before and after the computation.
class IxMonad m where
ireturn :: a -> m p p a
ibind :: m p q a -> (a -> m q r b) -> m p r bireturn and ibind must meet the monad laws as the ordinary monads do. ibind is required to be associative and ireturn to be the left and the right unit of ibind.
All ordinary monads can be injected into IxMonad with a newtype wrapper MW. It is a phantom type as the type parameters p and q are not used on the right hand-side of MW.
newtype MW m p q a = MW { unMW:: m a }
instance Monad m => IxMonad (MW m) where
ireturn = MW . return
ibind (MW m) f = MW (m >>= unMW . f)Here is an example of using the ordinary State monad wrapped with MW. iget and iput wraps the result with MW newtype wrapper.
iget :: (MonadState s m) => MW m s s s
iget = MW get
iput :: (MonadState s m) => s -> MW m s s ()
iput = MW . put
test2 = runState (unMW c) (0::Int) where
c = iget `ibind` (
\v -> iput (succ v) `ibind` (
\_ -> ireturn v))
-- (0, 1)Indexed State Monad
IxStateT defines an indexed state monad where si and so represents the input and the output state type respectively. The definition of IxStateT is similar to that of StateT except that the type of the state can be changed during the computation.
newtype IxStateT m si so v = IxStateT { runIxStateT:: si -> m (so,v) }
instance Monad m => IxMonad (IxStateT m) where
ireturn x = IxStateT (\si -> return (si,x))
ibind (IxStateT m) f = IxStateT (\si -> m si >>= (\ (sm,x) -> runIxStateT (f x) sm))
vsget :: Monad m => IxStateT m si si si
vsget = IxStateT (\si -> return (si,si))
vsput :: Monad m => so -> IxStateT m si so ()
vsput x = IxStateT (\si -> return (x,()))The following example gets an Int from the state and puts a String into the state. We can see that the type of the state is changed from Int to String.
test3 = runIxStateT c (0::Int) >>= print where
c = vsget `ibind` (
\v -> vsput (show v) `ibind` (
\_ -> vsget `ibind` (
\v' -> ireturn (v,v'))))
-- ("0",(0,"0"))Do notation
The IxMonad examples above looks ugly as we couldn’t use the do notation. Fortunately, -XRebindableSyntax extension allows us to overload the do-notation by providing alternative definitions that are local to the module.
{-# LANGUAGE RebindableSyntax #-}
import Prelude hiding ((>>=), (>>), return)
import IxState
return :: (Monad m) => a -> IxStateT m si si a
return = ireturn
(>>=) :: (Monad m) => IxStateT m p q a -> (a -> IxStateT m q r b) -> IxStateT m p r b
(>>=) = ibind
(>>) :: (Monad m) => IxStateT m p q a -> IxStateT m q r b -> IxStateT m p r b
v >> w = v >>= \_ -> w
c :: (Monad m) => IxStateT m Int String (Int, String)
c = do
v <- vsget
vsput (show v)
v' <- vsget
return (v, v')Other definitions
There are multiple ways to define indexed monads. The one used here is from Robert Atkey’s Parameterised Notions of Computation.
Other definitions include:
- McBride: Kleisli Arrows of Outrageous Fortune
- Orchard: Fun with indexed monads
References
- Oleg Kiselyov’s Parameterized `monad’
- Indexed Monad section of Stephen Diehl’s What I Wish I Knew When Learning Haskell