packages feed

monad-control 1.0.2.1 → 1.0.2.2

raw patch · 4 files changed

+201/−29 lines, 4 files

Files

CHANGELOG view
@@ -1,3 +1,8 @@+1.0.2.2++* Added some good documentation. Courtesy of Franz Thoma.++ 1.0.2.1  * Refer to Michael Snoyman's excellent tutorial on monad-control.
Control/Monad/Trans/Control.hs view
@@ -35,6 +35,33 @@ 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' T@ for all transformers @T@, using the+  @'defaultLiftWith'@ and @'defaultRestoreT'@ functions on the constructor and+  deconstructor of @T@.++* Define an instance @'MonadBaseControl' B B@ for the base monad:++    @+    instance MonadBaseControl B B where+        type StM B a   = a+        liftBaseWith f = f 'id'+        restoreM       = 'return'+    @++* Define instances @'MonadBaseControl' B m => 'MonadBaseControl' B (T m)@ for+  all transformers:++    @+    instance MonadBaseControl b m => MonadBaseControl b (T m) where+        type StM (T m) a = 'ComposeSt' T m a+        liftBaseWith f   = 'defaultLiftBaseWith'+        restoreM         = 'defaultRestoreM'+    @ -}  module Control.Monad.Trans.Control@@ -44,6 +71,7 @@       -- ** Defaults       -- $MonadTransControlDefaults     , RunDefault, defaultLiftWith, defaultRestoreT+      -- *** Defaults for a stack of two       -- $MonadTransControlDefaults2     , RunDefault2, defaultLiftWith2, defaultRestoreT2 @@ -121,13 +149,59 @@ -- MonadTransControl type class -------------------------------------------------------------------------------- +-- | The @MonadTransControl@ type class is a stronger version of @'MonadTrans'@:+--+-- Instances of @'MonadTrans'@ know how to @'lift'@ actions in the base monad to+-- the transformed monad. These lifted actions, however, are completely unaware+-- of the monadic state added by the transformer.+--+-- @'MonadTransControl'@ instances are aware of the monadic state of the+-- transformer and allow to save and restore this state.+--+-- 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+-- @+--+-- @'MonadTrans'@ instances can only lift the return type of the @withFile@+-- function:+--+-- @+-- withFileLifted :: MonadTrans t => FilePath -> IOMode -> (Handle -> IO r) -> t IO r+-- withFileLifted file mode action = lift (withFile file mode action)+-- @+--+-- However, @'MonadTrans'@ is not powerful enough to make @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'@.+-- @'MonadTransControl'@ allows us to do this:+--+-- @+-- 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+-- @ class MonadTrans t => MonadTransControl t where   -- | Monadic state of @t@.   ---  -- For clarity, because haddock does not display associated types, below are-  -- the elaborated 'StT' definitions provided by this library:+  -- The monadic state of a monad transformer is the result type of its @run@+  -- function, e.g.:   --   -- @+  -- 'runReaderT' :: 'ReaderT' r m a -> r -> m a+  -- 'StT' ('ReaderT' r) a ~ a+  --+  -- 'runStateT' :: '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:+  --+  -- @   -- StT 'IdentityT'    a ~ a   -- StT 'MaybeT'       a ~ 'Maybe' a   -- StT ('ErrorT' e)   a ~ 'Error' e => 'Either' e a@@ -152,7 +226,29 @@   -- 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.+  -- @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 (const f)@+  --+  -- Implementations use the @'Run'@ function associated with a transformer:+  --+  -- @+  -- 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' (f 'runMaybeT'))+  -- @   liftWith :: Monad m => (Run t -> m a) -> t m a    -- | Construct a @t@ computation from the monadic state of @t@ that is@@ -162,6 +258,23 @@   --   -- @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:   --   -- @@@ -197,6 +310,14 @@ -- 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'       ::      Run 'MaybeT'+-- @ type Run t = forall n b. Monad n => t n b -> n (StT t b)  @@ -207,8 +328,8 @@ -- $MonadTransControlDefaults -- -- The following functions can be used to define a 'MonadTransControl' instance--- for a monad transformer which simply wraps another monad transformer which--- already has a @MonadTransControl@ instance. For example:+-- for a monad transformer which simply is a newtype around another monad+-- transformer which already has a @MonadTransControl@ instance. For example: -- -- @ -- {-\# LANGUAGE GeneralizedNewtypeDeriving \#-}@@ -389,10 +510,18 @@ -- MonadBaseControl type class -------------------------------------------------------------------------------- +-- |+-- == Writing instances+--+-- The usual way to write a @'MonadBaseControl'@ instance for a transformer+-- stack over a base monad @B@ is to write an instance @MonadBaseControl B B@+-- for the base monad, and @MonadTransControl T@ instances for every transformer+-- @T@. Instances for @'MonadBaseControl'@ are then simply implemented using+-- @'ComposeSt'@, @'defaultLiftBaseWith'@, @'defaultRestoreM'@. class MonadBase b m => MonadBaseControl b m | m -> b where-    -- | Monadic state of @m@.+    -- | Monadic state that @m@ adds to the base monad @b@.     ---    -- For all non-transformer monads, @StM m a ~ a@:+    -- For all base (non-transformed) monads, @StM m a ~ a@:     --     -- @     -- StM 'IO'         a ~ a@@ -405,20 +534,20 @@     -- StM ('ST' s)     a ~ a     -- @     ---    -- All transformer monads\' 'StM' depends on both the monadic state of the-    -- transformer (given by its 'StT' from 'MonadTransControl'), as well as its-    -- inner monad's monadic state, given by its 'StM' from 'MonadBaseControl':+    -- If @m@ is a transformed monad, @m ~ t b@, @'StM'@ is the monadic state of+    -- the transformer @t@ (given by its 'StT' from 'MonadTransControl'). For a+    -- transformer stack, @'StM'@ is defined recursively:     --     -- @-    -- StM ('IdentityT'  m) a ~ StM m a-    -- StM ('MaybeT'     m) a ~ StM m ('Maybe' a)-    -- StM ('ErrorT' e   m) a ~ 'Error' e => StM m ('Either' e a)-    -- StM ('ExceptT' e  m) a ~ StM m ('Either' e a)-    -- StM ('ListT'      m) a ~ StM m [a]-    -- StM ('ReaderT' r  m) a ~ StM m a-    -- StM ('StateT' s   m) a ~ StM m (a, s)-    -- StM ('WriterT' w  m) a ~ 'Monoid' w => StM m (a, w)-    -- StM ('RWST' r w s m) a ~ 'Monoid' w => StM m (a, s, w)+    -- 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)     -- @     type StM m a :: * @@ -434,7 +563,17 @@     -- 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.+    -- 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+    -- @+    --+    -- @'liftBaseWith'@ is usually not implemented directly, but using+    -- @'defaultLiftBaseWith'@.     liftBaseWith :: (RunInBase m b -> b a) -> m a      -- | Construct a @m@ computation from the monadic state of @m@ that is@@ -443,6 +582,9 @@     -- Instances should satisfy:     --     -- @liftBaseWith (\\runInBase -> runInBase m) >>= restoreM = m@+    --+    -- @'restoreM'@ is usually not implemented directly, but using+    -- @'defaultRestoreM'@.     restoreM :: StM m a -> m a  -- | A function that runs a @m@ computation on the monadic state that was@@ -465,6 +607,8 @@ -- 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@. type RunInBase m b = forall a. m a -> b (StM m a)  @@ -593,6 +737,16 @@ --------------------------------------------------------------------------------  -- | 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))+-- @ control :: MonadBaseControl b m => (RunInBase m b -> b (StM m a)) -> m a control f = liftBaseWith f >>= restoreM {-# INLINABLE control #-}@@ -622,11 +776,15 @@ -- | @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)@.+-- @((a -> b c) -> b c)@ --+-- to:+--+-- @('MonadBaseControl' b m => (a -> m c) -> m c)@+-- -- For example: ----- @liftBaseOp alloca :: 'MonadBaseControl' 'IO' m => (Ptr a -> m c) -> m c@+-- @liftBaseOp alloca :: (Storable a, 'MonadBaseControl' 'IO' m) => (Ptr a -> m c) -> m c@ liftBaseOp :: MonadBaseControl b m            => ((a -> b (StM m c)) -> b (StM m d))            -> ((a ->        m c)  ->        m d)@@ -636,8 +794,12 @@ -- | @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)@.+-- @(b a -> b a)@ --+-- to:+--+-- @('MonadBaseControl' b m => m a -> m a)@+-- -- For example: -- -- @liftBaseOp_ mask_ :: 'MonadBaseControl' 'IO' m => m a -> m a@@@ -650,8 +812,12 @@ -- | @liftBaseDiscard@ is a particular application of 'liftBaseWith' that allows -- lifting control operations of type: ----- @(b () -> b a)@ to: @('MonadBaseControl' b m => m () -> m a)@.+-- @(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@.@@ -666,7 +832,11 @@ -- | @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)@.+-- @((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
README.markdown view
@@ -12,9 +12,6 @@ operators and exploits the `RankNTypes` language extension to simplify most definitions. -The package includes a copy of the `monad-peel` testsuite written by-Anders Kaseorg The tests can be performed by using `cabal test`.- [This `criterion`](https://github.com/basvandijk/bench-monad-peel-control) based benchmark shows that `monad-control` is on average about 2.5 times faster than `monad-peel`.
monad-control.cabal view
@@ -1,5 +1,5 @@ Name:                monad-control-Version:             1.0.2.1+Version:             1.0.2.2 Synopsis:            Lift control operations, like exception catching, through monad transformers License:             BSD3 License-file:        LICENSE