monad-control-0.2: Lift control operations, like exception catching, through monad transformers

PortabilityRequires RankNTypes
Stabilityexperimental
MaintainerBas van Dijk <v.dijk.bas@gmail.com>

Control.Monad.Trans.Control

Contents

Description

This module defines the class MonadTransControl of monad transformers through which control operations can be lifted. Instances are included for all the standard monad transformers from the transformers library except ContT.

idLiftControl and liftLiftControlBase are provided to assist creation of MonadControlIO-like classes (see Control.Monad.IO.Control) based on core monads other than IO.

Synopsis

MonadTransControl

class MonadTrans t => MonadTransControl t whereSource

MonadTransControl is the class of monad transformers supporting an extra operation liftControl, enabling control operations (functions that use monadic actions as input instead of just output) to be lifted through the transformer.

Methods

liftControl :: Monad m => (Run t -> m a) -> t m aSource

liftControl is used to peel off the outer layer of a transformed monadic action, allowing an transformed action t m a to be treated as a base action m a.

More precisely, liftControl captures the monadic state of t at the point where it is bound (in t m), yielding a function of type:

Run t = forall n o b. (Monad n, Monad o) => t n b -> n (t o b)

This function runs a transformed monadic action t n b in the inner monad n using the captured state, and leaves the result t o b in the monad n after all side effects in n have occurred.

This can be used to lift control operations with types such as M a -> M a into the transformed monad t M:

  instance Monad M
  foo :: M a -> M a
  foo' :: (MonadTransControl t, Monad (t M)) => t M a -> t M a
  foo' a = control $ run ->    -- run :: t M a -> M (t M a)
             foo $ run a       -- uses foo :: M (t M a) -> M (t M a)

type Run t = forall n o b. (Monad n, Monad o, Monad (t o)) => t n b -> n (t o b)Source

control :: (Monad m, Monad (t m), MonadTransControl t) => (Run t -> m (t m a)) -> t m aSource

An often used composition: control = join . liftControl

Lifting

idLiftControl :: Monad m => (RunInBase m m -> m a) -> m aSource

idLiftControl acts as the "identity" liftControl operation from a monad m to itself.

idLiftControl f = f $ liftM return

It serves as the base case for a class like MonadControlIO, which allows control operations in some base monad (here IO) to be lifted through arbitrary stacks of zero or more monad transformers in one call. For example, Control.Monad.IO.Control defines:

class MonadIO m => MonadControlIO m where
    liftControlIO :: (RunInBase m IO -> IO b) -> m b
instance MonadControlIO IO where
    liftControlIO = idLiftControl

type RunInBase m base = forall b. m b -> base (m b)Source

liftLiftControlBaseSource

Arguments

:: (MonadTransControl t, Monad (t m), Monad m, Monad base) 
=> ((RunInBase m base -> base a) -> m a)

liftControlBase operation

-> (RunInBase (t m) base -> base a) -> t m a 

liftLiftControlBase is used to compose two liftControl operations: the outer provided by a MonadTransControl instance, and the inner provided as the argument.

It satisfies liftLiftControlBase idLiftControl == liftControl.

It serves as the induction step of a MonadControlIO-like class. For example, Control.Monad.IO.Control defines:

instance MonadControlIO m => MonadControlIO (StateT s m) where
    liftControlIO = liftLiftControlBase liftControlIO

using the MonadTransControl instance of StateT s.

The following shows the recursive structure of liftControlIO applied to a stack of three monad transformers with IO as the base monad: t1 (t2 (t3 IO)) a:

liftControlIO
 =
 liftLiftControlBase $
   liftLiftControlBase $
     liftLiftControlBase $
       idLiftControl
  =
   \f -> liftControl $ \run1 ->     -- Capture state of t1, run1 :: Run t1
           liftControl $ \run2 ->   -- Capture state of t2, run2 :: Run t2
             liftControl $ \run3 -> -- Capture state of t3, run3 :: Run t3
               let run :: RunInBase (t1 (t2 (t3 IO))) IO
                   run = -- Restore state
                         liftM (join . lift) -- :: IO (           t2 (t3 IO) (t1 (t2 (t3 IO)) a))   -> IO (                       t1 (t2 (t3 IO)) a)
                       . liftM (join . lift) -- :: IO (    t3 IO (t2 (t3 IO) (t1 (t2 (t3 IO)) a)))  -> IO (           t2 (t3 IO) (t1 (t2 (t3 IO)) a))
                         -- Identity conversion
                       . liftM (join . lift) -- :: IO (IO (t3 IO (t2 (t3 IO) (t1 (t2 (t3 IO)) a)))) -> IO (    t3 IO (t2 (t3 IO) (t1 (t2 (t3 IO)) a)))
                       . liftM return        -- :: IO (    t3 IO (t2 (t3 IO) (t1 (t2 (t3 IO)) a)))  -> IO (IO (t3 IO (t2 (t3 IO) (t1 (t2 (t3 IO)) a))))
                         -- Run     (computation to run:)                              (inner monad:) (restore computation:)
                       . run3 -- ::         t3 IO  (t2 (t3 IO) (t1 (t2 (t3 IO)) a)) ->        IO      (t3 IO (t2 (t3 IO) (t1 (t2 (t3 IO)) a)))
                       . run2 -- ::     t2 (t3 IO)             (t1 (t2 (t3 IO)) a)  ->     t3 IO             (t2 (t3 IO) (t1 (t2 (t3 IO)) a))
                       . run1 -- :: t1 (t2 (t3 IO))                             a   -> t2 (t3 IO)                        (t1 (t2 (t3 IO)) a)
               in f run