Examples of the Cont monad. Actually we stack up other monad transformers on it for interesting things such as state. We do it like StateT s (Cont y), although we get similar behaviour with ContT y (State s) because instance lifting code is written to commute them. > import Control.Monad.Cont > import Control.Monad.State.Strict > import Control.Monad.Reader > import Control.Monad Basic principle. callCC :: ((a -> m b) -> m a) -> m a Or, 3rd-rank polymorphic implementation ((forall b. a -> m b) -> m a) -> m a) x <- callCC (\c -> .... c x1 ... return x0) In this code, c :: a -> m b and x0,x1,x :: a Think of c x1 as an action for early exit. If execution hits c x1, we exit early and x becomes x1. Otherwise, normal exit means x becomes x0. Note that c x1 :: m b with unconstrained b, so it can blend into its context. (Clearly, it doesn't really return a value of type b; execution jumps elsewhere and this is just type checking.) In this example, starting from initial state n, the loop increases it until the state becomes 5, then exits and returns that value. Note that c s :: m () to satisfy the type of when. > loop :: (Monad m) => m a -> m b > loop p = fix (p >>) > fun :: Int -> Int > fun n = runCont (evalStateT p n) id > where > p = do > { callCC $ \c -> loop $ do > { modify (+ 1) > ; s <- get > ; when (s == 5) (c s) > } > } This example is more advanced. Can we "leak" c to the outer scope and use it elsewhere? Yes. c still needs to be applied to some argument, let's call that x for now. (We will solve for x soon.) It's more convenient to return c x, and the user just needs to bind it to l and execute it later. When c x is executed, execution jumps back to its callCC origin, and x is returned and bound to the user's l, and it should be c x again so that the whole thing is repeatable. Therefore we have x = c x = fix c. Typing: l, fix c :: m b, c :: m b -> m b. In this example b = Int by blending into the if-then-else. > setjmp :: (MonadCont m) => m (m b) > setjmp = callCC (\c -> return (fix c)) > jumpy :: Int -> Int > jumpy n = runCont (evalStateT p n) id > where > p = do > { l <- setjmp > ; modify (+ 1) > ; s <- get > ; if s == 5 then return s else l > } Exception throwing and handling is a close cousin of callCC. Here we implement it. The action to be protected needs to know how to throw an exception; here we assume it takes a thrower as a parameter, and it calls the provided thrower when it wants to. Inside callCC, which provides c, we can define the thrower to run a handler and then use c to exit early; now we can run the action with this thrower. We use this to take square roots repeatedly of the state until it is close enough to 1; for states 0 or negative, we throw an exception, and the handler returns 0 or NaN respectively. > catchC :: (MonadCont m) => > ((e -> m b) -> m a) -- action, takes a thrower parameter > -> (e -> m a) -- handler > -> m a > catchC action handler = callCC (\c -> action (\e -> handler e >>= c)) > data Bad = Zero | Neg deriving Show > catchme :: Double -> Double > catchme n = runCont (evalStateT p n) id > where > p = do > { catchC q handler > } > q throw = do > { s <- get > ; when (s < 0) (throw Neg) > ; when (s <= 0) (throw Zero) > ; l <- setjmp > ; t <- get > ; if abs(t - 1) < 0.01 then return t else modify sqrt >> l > } > handler Zero = return 0 > handler Neg = return (sqrt (-1)) It is bothersome to mandate every protected action to take a thrower parameter. Now we tag on a ReaderT layer to make throwers implicit. As a bonus we also get to install a "top level" handler. Doing this introduces an infinite type. (The monad is a MonadReader of the thrower, and the thrower mentions that monad again.) A lightweight way of untying this is to be specific about our monad stack (RSC below) and newtyping the thrower. There are more advanced solutions. > type RSC e s y = ReaderT (Thrower e s y) (StateT s (Cont y)) > newtype Thrower e s y = Thrower (e -> RSC e s y ()) The throw command throwI asks for the thrower from the environment and executes it. The thrower returns (). The throw command returns an arbitrary b to blend into its caller context. This gap can be bridged by appending a meaningless polymorphic action after the thrower. (Alternative solution: the Thrower type is Thrower (forall b. e -> RSC e s y b), and callCC needs to be 2nd-rank too.) > throwI :: e -> RSC e s y b > throwI e = do > { Thrower thrower <- ask > ; thrower e > ; undefined > } The catch command catchI constructs the new thrower from the handler and uses it as the new environment for running the action. The thrower calls the handler and then jumps outside, as in the previous example. But in addition the handler needs to be arranged to run under the old environment so that it can "re-throw" exceptions. (If we don't code this up, the handler is run under the new environment (because it's called by the action under the new environment), and re-throwing causes looping.) This is seamlessly done with "local", which not only shadows the old environment, but also lets us map from the old to the new, so we can actually say we are constructing the new thrower from the old. > catchI :: RSC e s y a > -> (e -> RSC e s y a) > -> RSC e s y a > catchI action handler = > callCC (\c -> local (mkt c) action) > where mkt c r = Thrower (\e -> with r (handler e >>= c)) > with r m = local (const r) m This example usage takes square roots of the state until it's close to 1. For 0 or negative states, we throw an exception. Like the previous example, in the 0 case we return 0. Unlike the previous example, in the negative case we re-throw the exception to demonstrate that re-throwing works its way to the outer handler. > exceptional :: Double -> Double > exceptional n = runCont (evalStateT (runReaderT p topthrow) n) id > where > topthrow = Thrower (\e -> error ("unhandled exception " ++ show e)) > p = do > { catchI q handler > } > q = do > { s <- get > ; when (s < 0) (throwI Neg) > ; when (s <= 0) (throwI Zero) > ; l <- setjmp > ; t <- get > ; if abs(t - 1) < 0.01 then return t else modify sqrt >> l > } > handler Zero = return 0 > handler Neg = throwI Neg > {- > class (MonadCont m) => MonadCont2 m where > callCC2 :: ((forall b. a -> m b) -> m a) -> m a > instance (Monad m) => MonadCont2 (ContT r m) where > callCC2 f = ContT (\c -> runContT (f (\a -> ContT (\_ -> c a))) c) > -}