Contstuff
From HaskellWiki
(Added lots and lots of stuff.) |
m (Removed the introduction header) |
||
| Line 1: | Line 1: | ||
| - | |||
| - | |||
The [http://hackage.haskell.org/package/contstuff contstuff library] implements a number of monad transformers and monads, which make heavy use of [[continuation passing style]] (CPS). This makes them both fast and flexible. Please note that this is neither a CPS tutorial nor a monad transformer tutorial. You should understand these concepts, before attempting to use ''contstuff''. | The [http://hackage.haskell.org/package/contstuff contstuff library] implements a number of monad transformers and monads, which make heavy use of [[continuation passing style]] (CPS). This makes them both fast and flexible. Please note that this is neither a CPS tutorial nor a monad transformer tutorial. You should understand these concepts, before attempting to use ''contstuff''. | ||
Revision as of 02:00, 30 September 2010
The contstuff library implements a number of monad transformers and monads, which make heavy use of continuation passing style (CPS). This makes them both fast and flexible. Please note that this is neither a CPS tutorial nor a monad transformer tutorial. You should understand these concepts, before attempting to use contstuff.
Contents |
1 Basics
1.1 ContT
The ContT monad transformer is the simplest of all CPS-based monads:
newtype ContT r m aIt essentially gives you access to the current continuation, which means that it lets you label certain points of execution and reuse these points later in interesting ways. With ContT you get an elegant encoding of computations, which support:
- abortion (premature termination),
- resumption (start a computation at a certain spot),
- branches (aka goto),
- result accumulation,
- etc.
runContT :: (a -> m r) -> ContT r m a -> m r evalContT :: Applicative m => ContT r m r -> m r
1.2 Abortion
Let's have a look at a small example:
testComp1 :: ContT () IO () testComp1 = forever $ do txt <- io getLine case txt of "info" -> io $ putStrLn "This is a test computation." "quit" -> abort () _ -> return ()
1.3 Resumption and branches
You can capture the current continuation using the commonlabelCC :: a -> ContT r m (a, Label (ContT r m) a) goto :: Label (ContT r m) a -> a -> ContT r m b
These slightly complicated looking functions are actually very simple to use:
testComp2 :: ContT r IO () testComp2 = do (i, again) <- labelCC 0 io (print i) when (i < 10) $ goto again (i+1) io (putStrLn $ "Final result: " ++ show i)
Labels are first class values in contstuff. This means you can carry them around. They are only limited in that they can't be carried outside of a ContT computation.
1.4 Lifting
As noted earlier there are three lifting functions, which you can use to access monads in lower layers of the transformer stack:
lift :: (Transformer t, Monad m) => m a -> t m a base :: (LiftBase m a) => Base m a -> m a io :: (Base m a ~ IO a, LiftBase m a) => Base m a -> m a
1.5 Accumulating results
ContT does not require the underlying functor to be a monad. Whenever the underlying functor is antestComp3 :: Num a => ContT r [] (a, a) testComp3 = do x <- pure 10 <|> pure 20 y <- pure (x+1) <|> pure (x-1) return (x, y)
listA :: (Alternative f) => [a] -> f a
testComp3' :: Num a => ContT r [] (a, a) testComp3' = do x <- listA [10, 20] y <- listA [x+1, x-1] return (x, y)
testComp4 :: Num a => ContT (a, a) [] (a, a) testComp4 = do x <- listA [10, 20] when (x == 10) (abort (10, 10)) y <- listA [x+1, x-1] return (x, y)
2 State
The contstuff library also comes with a monad transformer for stateful computations. These computations carry state of a certain type and can access it at any time. It's called StateT, just like in other transformer libraries, but this one has very different semantics and also takes an additional parameter:
newtype StateT r s m aIt is basically a generalization of ContT. In fact you can use all the features of ContT in a StateT computation, too, including abortion, labels, accumulation, etc.
The2.1 Accessing the state
There are many functions to access the implicit state. These don't belong to StateT directly, but instead to a type class called Stateful, of which StateT is an instance. The associated type-- Where 'm' is a Stateful monad, 'StateOf m' is the type of its state. get :: (Stateful m) => m (StateOf m) put :: (Stateful m) => StateOf m -> m () putLazy :: (Stateful m) => StateOf m -> m () -- Convenience functions. getField :: (Functor m, Stateful m) => (StateOf m -> a) -> m a modify :: (Monad m, Stateful m) => (StateOf m -> StateOf m) -> m () modifyLazy :: (Monad m, Stateful m) => (StateOf m -> StateOf m) -> m () modifyField :: (Monad m, Stateful m) => (StateOf m -> a) -> (a -> StateOf m) -> m () modifyFieldLazy :: (Monad m, Stateful m) => (StateOf m -> a) -> (a -> StateOf m) -> m ()
2.2 Running
To run a stateful computation you can use therunStateT :: s -> (s -> a -> m r) -> StateT r s m a -> m r evalStateT :: (Applicative m) => s -> StateT r s m r -> m r execStateT :: (Applicative m) => s -> StateT s s m a -> m s
3 Exceptions
Contstuff provides an EitherT monad transformer:
newtype EitherT r e m aThis monad transformer is a generalization of ContT in that it supports two continuations. The second one can be accessed indirectly by the various exception handling functions.
3.1 Raising and catching
There are a number of functions to handle exceptions, which belong to a class-- Where 'm' is a monad supporting exceptions, 'Exception m' is the -- type of the exceptions. raise :: (HasExceptions m) => Exception m -> m a try :: (HasExceptions m) => m a -> m (Either (Exception m) a) -- Convenience functions. catch :: (HasExceptions m, Monad m) => m a -> (Exception m -> m a) -> m a handle :: (HasExceptions m, Monad m) => (Exception m -> m a) -> m a -> m a finally :: (HasExceptions m, Monad m) => m a -> m b -> m a bracket :: (HasExceptions m, Monad m) => m res -> (res -> m b) -> (res -> m a) -> m a bracket_ :: (HasExceptions m, Monad m) => m a -> m b -> m c -> m c
3.2 Running
To run an EitherT computation you can use therunEitherT :: (a -> m r) -> (e -> m r) -> EitherT r e m a -> m r evalEitherT :: (Applicative m) => EitherT (Either e a) e m a -> m (Either e a)
4 Choice/nondeterminism
The ChoiceT monad transformer is basically a list monad transformer and a proper one at that. It is also very fast, because choice is implemented as a CPS-based left fold function:
newtype ChoiceT r i m a4.1 Running
You can run a ChoiceT computation by using the slightly scaryrunChoiceT :: (i -> a -> (i -> m r) -> m r) -> i -> (i -> m r) -> ChoiceT r i m a -> m r
This function takes a folding function, a base element, a final continuation (the folding function uses CPS) and a ChoiceT computation. Of course in practice you mostly just want a list of results or the first result or something like that. Luckily there are two convenience functions to do just that:
findFirst :: (Alternative f, Applicative m) => ChoiceT (f a) (f a) m a -> m (f a) findAll :: (Alternative f, Applicative m) => ChoiceT (f a) (f a) m a -> m (f a)
maybeChoiceT :: Applicative m => ChoiceT (Maybe a) (Maybe a) m a -> m (Maybe a) listChoiceT :: Applicative m => ChoiceT [a] [a] m a -> m [a]
4.2 Convenience functions
Often you just want to encode a list of choices. For this you can use thelistA :: (Alternative f) => [a] -> f a
choice :: [a] -> ChoiceT r i m a
