Copyright | Bas van Dijk Anders Kaseorg |
---|---|
License | BSD3 |
Maintainer | Bas van Dijk <v.dijk.bas@gmail.com> |
Safe Haskell | Safe |
Language | Haskell2010 |
This module defines the type class
MonadBaseControl
, a subset of
MonadBase
into which generic control operations such as
catch
can be
lifted from
IO
or any other base monad. Instances are based on monad
transformers in
MonadTransControl
, which includes all standard monad
transformers in the
transformers
library except
ContT
.
See the
lifted-base
package which uses
monad-control
to lift
IO
operations from the
base
library (like
catch
or
bracket
) into any monad
that is an instance of
MonadBase
or
MonadBaseControl
.
See the following tutorial by Michael Snoyman on how to use this package:
https://www.yesodweb.com/book/monad-control
Quick implementation guide
Given a base monad
B
and a stack of transformers
T
:
-
Define instances
MonadTransControl
TT
, using thedefaultLiftWith
defaultRestoreT
T
. -
Define an instance
MonadBaseControl
B Binstance MonadBaseControl B B where type StM B a = a liftBaseWith f = f
id
restoreM =return
-
Define instances
MonadBaseControl
B m =>MonadBaseControl
B (T m)instance MonadBaseControl b m => MonadBaseControl b (T m) where type StM (T m) a =
ComposeSt
T m a liftBaseWith f =defaultLiftBaseWith
restoreM =defaultRestoreM
Synopsis
- class MonadTrans t => MonadTransControl t where
- type Run t = forall n b. Monad n => t n b -> n ( StT t b)
- type RunDefault t t' = forall n b. Monad n => t n b -> n ( StT t' b)
- defaultLiftWith :: ( Monad m, MonadTransControl n) => ( forall b. n m b -> t m b) -> ( forall o b. t o b -> n o b) -> ( RunDefault t n -> m a) -> t m a
- defaultRestoreT :: ( Monad m, MonadTransControl n) => (n m a -> t m a) -> m ( StT n a) -> t m a
- type RunDefault2 t n n' = forall m b. ( Monad m, Monad (n' m)) => t m b -> m ( StT n' ( StT n b))
- defaultLiftWith2 :: ( Monad m, Monad (n' m), MonadTransControl n, MonadTransControl n') => ( forall b. n (n' m) b -> t m b) -> ( forall o b. t o b -> n (n' o) b) -> ( RunDefault2 t n n' -> m a) -> t m a
- defaultRestoreT2 :: ( Monad m, Monad (n' m), MonadTransControl n, MonadTransControl n') => (n (n' m) a -> t m a) -> m ( StT n' ( StT n a)) -> t m a
-
class
MonadBase
b m =>
MonadBaseControl
b m | m -> b
where
- type StM m a :: *
- liftBaseWith :: ( RunInBase m b -> b a) -> m a
- restoreM :: StM m a -> m a
- type RunInBase m b = forall a. m a -> b ( StM m a)
- type ComposeSt t m a = StM m ( StT t a)
- type RunInBaseDefault t m b = forall a. t m a -> b ( ComposeSt t m a)
- defaultLiftBaseWith :: ( MonadTransControl t, MonadBaseControl b m) => ( RunInBaseDefault t m b -> b a) -> t m a
- defaultRestoreM :: ( MonadTransControl t, MonadBaseControl b m) => ComposeSt t m a -> t m a
- control :: MonadBaseControl b m => ( RunInBase m b -> b ( StM m a)) -> m a
- controlT :: ( MonadTransControl t, Monad (t m), Monad m) => ( Run t -> m ( StT t a)) -> t m a
- embed :: MonadBaseControl b m => (a -> m c) -> m (a -> b ( StM m c))
- embed_ :: MonadBaseControl b m => (a -> m ()) -> m (a -> b ())
- captureT :: ( MonadTransControl t, Monad (t m), Monad m) => t m ( StT t ())
- captureM :: MonadBaseControl b m => m ( StM m ())
- liftBaseOp :: MonadBaseControl b m => ((a -> b ( StM m c)) -> b ( StM m d)) -> (a -> m c) -> m d
- liftBaseOp_ :: MonadBaseControl b m => (b ( StM m a) -> b ( StM m c)) -> m a -> m c
- liftBaseDiscard :: MonadBaseControl b m => (b () -> b a) -> m () -> m a
- liftBaseOpDiscard :: MonadBaseControl b m => ((a -> b ()) -> b c) -> (a -> m ()) -> m c
- liftThrough :: ( MonadTransControl t, Monad (t m), Monad m) => (m ( StT t a) -> m ( StT t b)) -> t m a -> t m b
MonadTransControl
class MonadTrans t => MonadTransControl t where Source #
The
MonadTransControl
type class is a stronger version of
:
MonadTrans
Instances of
know how to
MonadTrans
actions in the base monad to
the transformed monad. These lifted actions, however, are completely unaware
of the monadic state added by the transformer.
lift
instances are aware of the monadic state of the
transformer and allow to save and restore this state.
MonadTransControl
This allows to lift functions that have a monad transformer in both positive and negative position. Take, for example, the function
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
instances can only lift the return type of the
MonadTrans
withFile
function:
withFileLifted :: MonadTrans t => FilePath -> IOMode -> (Handle -> IO r) -> t IO r withFileLifted file mode action = lift (withFile file mode action)
However,
is not powerful enough to make
MonadTrans
withFileLifted
accept a function that returns
t IO
. The reason is that we need to take
away the transformer layer in order to pass the function to
.
withFile
allows us to do this:
MonadTransControl
withFileLifted' :: (Monad (t IO), MonadTransControl t) => FilePath -> IOMode -> (Handle -> t IO r) -> t IO r withFileLifted' file mode action = liftWith (\run -> withFile file mode (run . action)) >>= restoreT . return
Monadic state of
t
.
The monadic state of a monad transformer is the result type of its
run
function, e.g.:
runReaderT
::ReaderT
r m a -> r -> m aStT
(ReaderT
r) a ~ arunStateT
::StateT
s m a -> s -> m (a, s)StT
(StateT
s) a ~ (a, s)runMaybeT
::MaybeT
m a -> m (Maybe
a)StT
MaybeT
a ~Maybe
a
Provided type instances:
StTIdentityT
a ~ a StTMaybeT
a ~Maybe
a StT (ErrorT
e) a ~Error
e =>Either
e a StT (ExceptT
e) a ~Either
e a StTListT
a ~ [a] StT (ReaderT
r) a ~ a StT (StateT
s) a ~ (a, s) StT (WriterT
w) a ~Monoid
w => (a, w) StT (RWST
r w s) a ~Monoid
w => (a, s, w)
liftWith :: Monad m => ( Run t -> m a) -> t m a Source #
liftWith
is similar to
lift
in that it lifts a computation from
the argument monad to the constructed monad.
Instances should satisfy similar laws as the
MonadTrans
laws:
liftWith (\_ -> return a) = return a
liftWith (\_ -> m >>= f) = liftWith (\_ -> m) >>= (\a -> liftWith (\_ -> f a))
The difference with
lift
is that before lifting the
m
computation
liftWith
captures the state of
t
. It then provides the
m
computation with a
Run
function that allows running
t n
computations in
n
(for all
n
) on the captured state, e.g.
withFileLifted :: (Monad (t IO), MonadTransControl t) => FilePath -> IOMode -> (Handle -> t IO r) -> t IO r withFileLifted file mode action = liftWith (\run -> withFile file mode (run . action)) >>= restoreT . return
If the
Run
function is ignored,
liftWith
coincides with
lift
:
lift f = liftWith (\_ -> f)
Implementations use the
function associated with a transformer:
Run
liftWith ::Monad
m => ((Monad
n =>ReaderT
r n b -> n b) -> m a) ->ReaderT
r m a liftWith f =ReaderT
(\r -> f (\action ->runReaderT
action r)) liftWith ::Monad
m => ((Monad
n =>StateT
s n b -> n (b, s)) -> m a) ->StateT
s m a liftWith f =StateT
(\s ->liftM
(\x -> (x, s)) (f (\action ->runStateT
action s))) liftWith ::Monad
m => ((Monad
n =>MaybeT
n b -> n (Maybe
b)) -> m a) ->MaybeT
m a liftWith f =MaybeT
(liftM
Just
(frunMaybeT
))
restoreT :: Monad m => m ( StT t a) -> t m a Source #
Construct a
t
computation from the monadic state of
t
that is
returned from a
Run
function.
Instances should satisfy:
liftWith (\run -> run t) >>= restoreT . return = t
restoreT
is usually implemented through the constructor of the monad
transformer:
ReaderT
:: (r -> m a) ->ReaderT
r m a restoreT :: m a ->ReaderT
r m a restoreT action =ReaderT
{ runReaderT =const
action }StateT
:: (s -> m (a, s)) ->StateT
s m a restoreT :: m (a, s) ->StateT
s m a restoreT action =StateT
{ runStateT =const
action }MaybeT
:: m (Maybe
a) ->MaybeT
m a restoreT :: m (Maybe
a) ->MaybeT
m a restoreT action =MaybeT
action
Example type signatures:
restoreT ::Monad
m => m a ->IdentityT
m a restoreT ::Monad
m => m (Maybe
a) ->MaybeT
m a restoreT :: (Monad
m,Error
e) => m (Either
e a) ->ErrorT
e m a restoreT ::Monad
m => m (Either
e a) ->ExceptT
e m a restoreT ::Monad
m => m [a] ->ListT
m a restoreT ::Monad
m => m a ->ReaderT
r m a restoreT ::Monad
m => m (a, s) ->StateT
s m a restoreT :: (Monad
m,Monoid
w) => m (a, w) ->WriterT
w m a restoreT :: (Monad
m,Monoid
w) => m (a, s, w) ->RWST
r w s m a
Instances
MonadTransControl MaybeT Source # | |
MonadTransControl ListT Source # | |
Monoid w => MonadTransControl ( WriterT w) Source # | |
Monoid w => MonadTransControl ( WriterT w) Source # | |
MonadTransControl ( StateT s) Source # | |
MonadTransControl ( StateT s) Source # | |
MonadTransControl ( ReaderT r) Source # | |
MonadTransControl ( IdentityT :: ( Type -> Type ) -> Type -> Type ) Source # | |
MonadTransControl ( ExceptT e) Source # | |
Error e => MonadTransControl ( ErrorT e) Source # | |
Monoid w => MonadTransControl ( RWST r w s) Source # | |
Monoid w => MonadTransControl ( RWST r w s) Source # | |
type Run t = forall n b. Monad n => t n b -> n ( StT t b) Source #
A function that runs a transformed monad
t n
on the monadic state that
was captured by
liftWith
A
Run t
function yields a computation in
n
that returns the monadic state
of
t
. This state can later be used to restore a
t
computation using
restoreT
.
Example type equalities:
RunIdentityT
~ forall n b.Monad
n =>IdentityT
n b -> n b RunMaybeT
~ forall n b.Monad
n =>MaybeT
n b -> n (Maybe
b) Run (ErrorT
e) ~ forall n b. (Monad
n,Error
e) =>ErrorT
e n b -> n (Either
e b) Run (ExceptT
e) ~ forall n b.Monad
n =>ExceptT
e n b -> n (Either
e b) RunListT
~ forall n b.Monad
n =>ListT
n b -> n [b] Run (ReaderT
r) ~ forall n b.Monad
n =>ReaderT
r n b -> n b Run (StateT
s) ~ forall n b.Monad
n =>StateT
s n b -> n (a, s) Run (WriterT
w) ~ forall n b. (Monad
n,Monoid
w) =>WriterT
w n b -> n (a, w) Run (RWST
r w s) ~ forall n b. (Monad
n,Monoid
w) =>RWST
r w s n b -> n (a, s, w)
This type is usually satisfied by the
run
function of a transformer:
flip
runReaderT
:: r -> Run (ReaderT
r)flip
runStateT
:: s -> Run (StateT
s)runMaybeT
:: RunMaybeT
Defaults
The following functions can be used to define a
MonadTransControl
instance
for a monad transformer which simply is a newtype around another monad
transformer which already has a
MonadTransControl
instance. For example:
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE TypeFamilies #-} newtype CounterT m a = CounterT {unCounterT :: StateT Int m a} deriving (Monad, MonadTrans) instance MonadTransControl CounterT where type StT CounterT a = StT (StateT Int) a liftWith =defaultLiftWith
CounterT unCounterT restoreT =defaultRestoreT
CounterT
type RunDefault t t' = forall n b. Monad n => t n b -> n ( StT t' b) Source #
A function like
Run
that runs a monad transformer
t
which wraps the
monad transformer
t'
. This is used in
defaultLiftWith
.
:: ( Monad m, MonadTransControl n) | |
=> ( forall b. n m b -> t m b) |
Monad constructor |
-> ( forall o b. t o b -> n o b) |
Monad deconstructor |
-> ( RunDefault t n -> m a) | |
-> t m a |
Default definition for the
liftWith
method.
:: ( Monad m, MonadTransControl n) | |
=> (n m a -> t m a) |
Monad constructor |
-> m ( StT n a) | |
-> t m a |
Default definition for the
restoreT
method.
Defaults for a stack of two
The following functions can be used to define a
MonadTransControl
instance
for a monad transformer stack of two.
{-# LANGUAGE GeneralizedNewtypeDeriving #-} newtype CalcT m a = CalcT { unCalcT :: StateT Int (ExceptT String m) a } deriving (Monad, MonadTrans) instance MonadTransControl CalcT where type StT CalcT a = StT (ExceptT String) (StT (StateT Int) a) liftWith =defaultLiftWith2
CalcT unCalcT restoreT =defaultRestoreT2
CalcT
type RunDefault2 t n n' = forall m b. ( Monad m, Monad (n' m)) => t m b -> m ( StT n' ( StT n b)) Source #
A function like
Run
that runs a monad transformer
t
which wraps the
monad transformers
n
and
n'
. This is used in
defaultLiftWith2
.
:: ( Monad m, Monad (n' m), MonadTransControl n, MonadTransControl n') | |
=> ( forall b. n (n' m) b -> t m b) |
Monad constructor |
-> ( forall o b. t o b -> n (n' o) b) |
Monad deconstructor |
-> ( RunDefault2 t n n' -> m a) | |
-> t m a |
Default definition for the
liftWith
method.
:: ( Monad m, Monad (n' m), MonadTransControl n, MonadTransControl n') | |
=> (n (n' m) a -> t m a) |
Monad constructor |
-> m ( StT n' ( StT n a)) | |
-> t m a |
Default definition for the
restoreT
method for double
MonadTransControl
.
MonadBaseControl
class MonadBase b m => MonadBaseControl b m | m -> b where Source #
Writing instances
The usual way to write a
instance for a transformer
stack over a base monad
MonadBaseControl
B
is to write an instance
MonadBaseControl B B
for the base monad, and
MonadTransControl T
instances for every transformer
T
. Instances for
are then simply implemented using
MonadBaseControl
,
ComposeSt
,
defaultLiftBaseWith
.
defaultRestoreM
Monadic state that
m
adds to the base monad
b
.
For all base (non-transformed) monads,
StM m a ~ a
:
StMIO
a ~ a StMMaybe
a ~ a StM (Either
e) a ~ a StM [] a ~ a StM ((->) r) a ~ a StMIdentity
a ~ a StMSTM
a ~ a StM (ST
s) a ~ a
If
m
is a transformed monad,
m ~ t b
,
is the monadic state of
the transformer
StM
t
(given by its
StT
from
MonadTransControl
). For a
transformer stack,
is defined recursively:
StM
StM (IdentityT
m) a ~ComposeSt
IdentityT
m a ~ StM m a StM (MaybeT
m) a ~ComposeSt
MaybeT
m a ~ StM m (Maybe
a) StM (ErrorT
e m) a ~ComposeSt
ErrorT
m a ~Error
e => StM m (Either
e a) StM (ExceptT
e m) a ~ComposeSt
ExceptT
m a ~ StM m (Either
e a) StM (ListT
m) a ~ComposeSt
ListT
m a ~ StM m [a] StM (ReaderT
r m) a ~ComposeSt
ReaderT
m a ~ StM m a StM (StateT
s m) a ~ComposeSt
StateT
m a ~ StM m (a, s) StM (WriterT
w m) a ~ComposeSt
WriterT
m a ~Monoid
w => StM m (a, w) StM (RWST
r w s m) a ~ComposeSt
RWST
m a ~Monoid
w => StM m (a, s, w)
liftBaseWith :: ( RunInBase m b -> b a) -> m a Source #
liftBaseWith
is similar to
liftIO
and
liftBase
in that it
lifts a base computation to the constructed monad.
Instances should satisfy similar laws as the
MonadIO
and
MonadBase
laws:
liftBaseWith (\_ -> return a) = return a
liftBaseWith (\_ -> m >>= f) = liftBaseWith (\_ -> m) >>= (\a -> liftBaseWith (\_ -> f a))
As Li-yao Xia explains , parametricity guarantees that
f $ liftBaseWith q = liftBaseWith $ runInBase -> f $ q runInBase
The difference with
liftBase
is that before lifting the base computation
liftBaseWith
captures the state of
m
. It then provides the base
computation with a
RunInBase
function that allows running
m
computations in the base monad on the captured state:
withFileLifted :: MonadBaseControl IO m => FilePath -> IOMode -> (Handle -> m a) -> m a withFileLifted file mode action = liftBaseWith (\runInBase -> withFile file mode (runInBase . action)) >>= restoreM -- = control $ \runInBase -> withFile file mode (runInBase . action) -- = liftBaseOp (withFile file mode) action
is usually not implemented directly, but using
liftBaseWith
.
defaultLiftBaseWith
restoreM :: StM m a -> m a Source #
Construct a
m
computation from the monadic state of
m
that is
returned from a
RunInBase
function.
Instances should satisfy:
liftBaseWith (\runInBase -> runInBase m) >>= restoreM = m
is usually not implemented directly, but using
restoreM
.
defaultRestoreM
Instances
type RunInBase m b = forall a. m a -> b ( StM m a) Source #
A function that runs a
m
computation on the monadic state that was
captured by
liftBaseWith
A
RunInBase m
function yields a computation in the base monad of
m
that
returns the monadic state of
m
. This state can later be used to restore the
m
computation using
restoreM
.
Example type equalities:
RunInBase (IdentityT
m) b ~ forall a.IdentityT
m a -> b (StM
m a) RunInBase (MaybeT
m) b ~ forall a.MaybeT
m a -> b (StM
m (Maybe
a)) RunInBase (ErrorT
e m) b ~ forall a.Error
e =>ErrorT
e m a -> b (StM
m (Either
e a)) RunInBase (ExceptT
e m) b ~ forall a.ExceptT
e m a -> b (StM
m (Either
e a)) RunInBase (ListT
m) b ~ forall a.ListT
m a -> b (StM
m [a]) RunInBase (ReaderT
r m) b ~ forall a.ReaderT
m a -> b (StM
m a) RunInBase (StateT
s m) b ~ forall a.StateT
s m a -> b (StM
m (a, s)) RunInBase (WriterT
w m) b ~ forall a.Monoid
w =>WriterT
w m a -> b (StM
m (a, w)) RunInBase (RWST
r w s m) b ~ forall a.Monoid
w =>RWST
r w s m a -> b (StM
m (a, s, w))
For a transformed base monad
m ~ t b
,
'RunInBase m b' ~
.
Run
t
Defaults
Note that by using the following default definitions it's easy to make a
monad transformer
T
an instance of
MonadBaseControl
:
instance MonadBaseControl b m => MonadBaseControl b (T m) where type StM (T m) a =ComposeSt
T m a liftBaseWith =defaultLiftBaseWith
restoreM =defaultRestoreM
Defining an instance for a base monad
B
is equally straightforward:
instance MonadBaseControl B B where type StM B a = a liftBaseWith f = fid
restoreM =return
type ComposeSt t m a = StM m ( StT t a) Source #
Handy type synonym that composes the monadic states of
t
and
m
.
It can be used to define the
StM
for new
MonadBaseControl
instances.
type RunInBaseDefault t m b = forall a. t m a -> b ( ComposeSt t m a) Source #
A function like
RunInBase
that runs a monad transformer
t
in its base
monad
b
. It is used in
defaultLiftBaseWith
.
defaultLiftBaseWith :: ( MonadTransControl t, MonadBaseControl b m) => ( RunInBaseDefault t m b -> b a) -> t m a Source #
Default definition for the
liftBaseWith
method.
Note that it composes a
liftWith
of
t
with a
liftBaseWith
of
m
to
give a
liftBaseWith
of
t m
:
defaultLiftBaseWith = \f ->liftWith
$ \run ->liftBaseWith
$ \runInBase -> f $ runInBase . run
defaultRestoreM :: ( MonadTransControl t, MonadBaseControl b m) => ComposeSt t m a -> t m a Source #
Utility functions
control :: MonadBaseControl b m => ( RunInBase m b -> b ( StM m a)) -> m a Source #
An often used composition:
control f =
liftBaseWith
f >>=
restoreM
Example:
liftedBracket :: MonadBaseControl IO m => m a -> (a -> m b) -> (a -> m c) -> m c liftedBracket acquire release action = control $ \runInBase -> bracket (runInBase acquire) (\saved -> runInBase (restoreM saved >>= release)) (\saved -> runInBase (restoreM saved >>= action))
controlT :: ( MonadTransControl t, Monad (t m), Monad m) => ( Run t -> m ( StT t a)) -> t m a Source #
embed :: MonadBaseControl b m => (a -> m c) -> m (a -> b ( StM m c)) Source #
Embed a transformer function as an function in the base monad returning a mutated transformer state.
embed_ :: MonadBaseControl b m => (a -> m ()) -> m (a -> b ()) Source #
Performs the same function as
embed
, but discards transformer state
from the embedded function.
captureT :: ( MonadTransControl t, Monad (t m), Monad m) => t m ( StT t ()) Source #
Capture the current state of a transformer
captureM :: MonadBaseControl b m => m ( StM m ()) Source #
Capture the current state above the base monad
liftBaseOp :: MonadBaseControl b m => ((a -> b ( StM m c)) -> b ( StM m d)) -> (a -> m c) -> m d Source #
liftBaseOp
is a particular application of
liftBaseWith
that allows
lifting control operations of type:
((a -> b c) -> b c)
to:
(MonadBaseControl
b m => (a -> m c) -> m c)
For example:
liftBaseOp alloca :: (Storable a,MonadBaseControl
IO
m) => (Ptr a -> m c) -> m c
liftBaseOp_ :: MonadBaseControl b m => (b ( StM m a) -> b ( StM m c)) -> m a -> m c Source #
liftBaseOp_
is a particular application of
liftBaseWith
that allows
lifting control operations of type:
(b a -> b a)
to:
(MonadBaseControl
b m => m a -> m a)
For example:
liftBaseOp_ mask_ ::MonadBaseControl
IO
m => m a -> m a
liftBaseDiscard :: MonadBaseControl b m => (b () -> b a) -> m () -> m a Source #
liftBaseDiscard
is a particular application of
liftBaseWith
that allows
lifting control operations of type:
(b () -> b a)
to:
(MonadBaseControl
b m => m () -> m a)
Note that, while the argument computation
m ()
has access to the captured
state, all its side-effects in
m
are discarded. It is run only for its
side-effects in the base monad
b
.
For example:
liftBaseDiscard forkIO ::MonadBaseControl
IO
m => m () -> m ThreadId
liftBaseOpDiscard :: MonadBaseControl b m => ((a -> b ()) -> b c) -> (a -> m ()) -> m c Source #
liftBaseOpDiscard
is a particular application of
liftBaseWith
that allows
lifting control operations of type:
((a -> b ()) -> b c)
to:
(MonadBaseControl
b m => (a -> m ()) -> m c)
Note that, while the argument computation
m ()
has access to the captured
state, all its side-effects in
m
are discarded. It is run only for its
side-effects in the base monad
b
.
For example:
liftBaseDiscard (runServer addr port) ::MonadBaseControl
IO
m => m () -> m ()
:: ( MonadTransControl t, Monad (t m), Monad m) | |
=> (m ( StT t a) -> m ( StT t b)) | |
-> t m a | |
-> t m b |
Transform an action in
t m
using a transformer that operates on the underlying monad
m