continue (empty) → 0.1.0
raw patch · 8 files changed
+527/−0 lines, 8 filesdep +basedep +bifunctorsdep +monad-controlsetup-changed
Dependencies added: base, bifunctors, monad-control, mtl, semigroupoids, transformers, transformers-base
Files
- Control/Continue.hs +24/−0
- Control/Continue/Class.hs +77/−0
- Control/Continue/Types.hs +174/−0
- Control/Continue/Utils.hs +51/−0
- LICENSE +32/−0
- README.md +95/−0
- Setup.lhs +12/−0
- continue.cabal +62/−0
+ Control/Continue.hs view
@@ -0,0 +1,24 @@+-- |+-- Module: Control.Continue+-- Copyright: (c) 2012 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Proxy to the various modules of the continue package.++module Control.Continue+ ( -- * Continue modules+ module Control.Continue.Class,+ module Control.Continue.Types,+ module Control.Continue.Utils,++ -- * Convenience reexports+ Alt(..),+ Plus(..)+ )+ where++import Control.Continue.Class+import Control.Continue.Types+import Control.Continue.Utils+import Data.Functor.Plus
+ Control/Continue/Class.hs view
@@ -0,0 +1,77 @@+-- |+-- Module: Control.Continue.Class+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++{-# LANGUAGE UndecidableInstances #-}++module Control.Continue.Class+ ( -- * Suspension+ MonadContinue(..)+ )+ where++import qualified Control.Monad.Trans.State.Strict as Ss+import qualified Control.Monad.Trans.Writer.Strict as Ws+import Control.Monad.Trans.Identity+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.Reader+import Control.Monad.Trans.State.Lazy+import Control.Monad.Trans.Writer.Lazy+import Data.Monoid+import Data.Functor.Plus+++-- | Type class for monads that support suspension and continuation+-- spots.++class (Plus f, Monad m, Monoid e) => MonadContinue e f m | m -> e, m -> f where+ -- | Add the given set of continuations and possibly suspend.+ addCont ::+ Either e a -- ^ What to return now (left suspends).+ -> f (m a) -- ^ What to run and return when reentering.+ -> m a++instance (MonadContinue e f m) => MonadContinue e f (IdentityT m) where+ addCont mx c = IdentityT $ addCont mx (fmap runIdentityT c)++instance (MonadContinue e f m) => MonadContinue e f (MaybeT m) where+ addCont mx c =+ MaybeT $+ addCont (fmap Just mx) (fmap runMaybeT c)++instance (MonadContinue e f m) => MonadContinue e f (ReaderT r m) where+ addCont mx c =+ ReaderT $ \env ->+ addCont mx (fmap (flip runReaderT env) c)++-- | Time travel warning: Captures the current state, not the state at+-- reentry!++instance (MonadContinue e f m) => MonadContinue e f (StateT s m) where+ addCont mx c =+ StateT $ \s ->+ addCont (fmap (flip (,) s) mx)+ (fmap (flip runStateT s) c)++-- | Time travel warning: Captures the current state, not the state at+-- reentry!++instance (MonadContinue e f m) => MonadContinue e f (Ss.StateT s m) where+ addCont mx c =+ Ss.StateT $ \s ->+ addCont (fmap (flip (,) s) mx)+ (fmap (flip Ss.runStateT s) c)++instance (MonadContinue e f m, Monoid l) => MonadContinue e f (WriterT l m) where+ addCont mx c =+ WriterT $+ addCont (fmap (flip (,) mempty) mx)+ (fmap runWriterT c)++instance (MonadContinue e f m, Monoid l) => MonadContinue e f (Ws.WriterT l m) where+ addCont mx c =+ Ws.WriterT $+ addCont (fmap (flip (,) mempty) mx)+ (fmap Ws.runWriterT c)
+ Control/Continue/Types.hs view
@@ -0,0 +1,174 @@+-- |+-- Module: Control.Continue.Types+-- Copyright: (c) 2012 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Types used in the continue library.++{-# LANGUAGE UndecidableInstances #-}++module Control.Continue.Types+ ( -- * Suspendable computations+ ContinueT(..),+ Continue,++ -- * Convenience types+ LastEx+ )+ where++import qualified Data.Bifunctor as Bi+import Control.Applicative+import Control.Arrow+import Control.Continue.Class+import Control.Exception (SomeException)+import Control.Monad+import Control.Monad.Base+import Control.Monad.Error.Class+import Control.Monad.Identity+import Control.Monad.Reader.Class+import Control.Monad.State.Class+import Control.Monad.Trans+import Control.Monad.Trans.Control+import Control.Monad.Writer.Class+import Data.Functor.Plus+import Data.Monoid+++-- | This monad transformer adds continuations under @f@ and @e@-typed+-- suspensions to @m@.++newtype ContinueT e f m a =+ ContinueT {+ runContinueT :: m (Either e a, f (ContinueT e f m a))+ }++instance (Monad m, Monoid e, Plus f) => Alternative (ContinueT e f m) where+ empty = ContinueT (return (Left mempty, zero))++ ContinueT c1 <|> ContinueT c2 =+ ContinueT $ do+ (mx1, cf1) <- c1+ (mx2, cf2) <- c2+ let mx = either (\ex1 -> Bi.first (ex1 <>) mx2) Right mx1+ return (mx, cf1 <!> cf2)++instance (Monad m, Plus f) => Applicative (ContinueT e f m) where+ pure x = ContinueT (return (Right x, zero))++ ContinueT cf <*> rx@(ContinueT cx) =+ ContinueT $ do+ (mf, cff) <- cf+ case mf of+ Left ex -> return (Left ex, fmap (<*> rx) cff)+ Right f -> do+ (mx, cfx) <- cx+ return (fmap f mx, fmap (fmap f) cfx)++instance (Functor f, Monad m) => Functor (ContinueT e f m) where+ fmap f (ContinueT c) = ContinueT (liftM (fmap f *** fmap (fmap f)) c)++instance (Monad m, Monoid e, Plus f) => Monad (ContinueT e f m) where+ return = pure++ ContinueT c >>= f =+ ContinueT $ do+ (mx, cfx') <- c+ let cfx = fmap (>>= f) cfx'+ case mx of+ Left ex -> return (Left ex, cfx)+ Right x -> do+ (my, cfy) <- runContinueT (f x)+ return (my, cfx <!> cfy)++ fail _ = ContinueT (return (Left mempty, zero))++instance (MonadBase b m, Monoid e, Plus f) => MonadBase b (ContinueT e f m) where+ liftBase = liftBaseDefault++instance (MonadBaseControl b m, Monoid e, Plus f) => MonadBaseControl b (ContinueT e f m) where+ data StM (ContinueT e f m) a =+ StContinueT (StM m (Either e a, f (ContinueT e f m a)))++ liftBaseWith k =+ ContinueT $ do+ x <-+ liftBaseWith $ \runB ->+ k $ \(ContinueT c) ->+ liftM StContinueT (runB c)+ return (Right x, zero)++ restoreM (StContinueT s) = ContinueT (restoreM s)++instance (Monad m, Monoid e, Plus f) => MonadContinue e f (ContinueT e f m) where+ addCont mx cf = ContinueT (return (mx, cf))++instance (Monad m, Monoid e, Plus f) => MonadError e (ContinueT e f m) where+ throwError ex = ContinueT (return (Left ex, zero))+ catchError (ContinueT c) h =+ ContinueT $ do+ (mx, cf) <- c+ case mx of+ Left ex -> do+ (mxh, cfh) <- runContinueT (h ex)+ return (mxh, cf <!> cfh)+ Right _ -> return (mx, cf)++-- | Warning: If feedback is broken by suspension you get a run-time+-- error.++instance (MonadFix m, Monoid e, Plus f) => MonadFix (ContinueT e f m) where+ mfix f =+ ContinueT . mfix $ \ ~(mx, _) ->+ runContinueT . f .+ either (const $ error "Feedback broken by suspension") id $ mx++instance (MonadIO m, Monoid e, Plus f) => MonadIO (ContinueT e f m) where+ liftIO = ContinueT . liftM (\x -> (Right x, zero)) . liftIO++instance (Monad m, Monoid e, Plus f) => MonadPlus (ContinueT e f m) where+ mzero = empty+ mplus = (<|>)++instance (MonadReader r m, Monoid e, Plus f) => MonadReader r (ContinueT e f m) where+ ask = lift ask+ local f (ContinueT c) = ContinueT (local f c)+ reader = ContinueT . liftM (\x -> (Right x, zero)) . reader++instance (MonadState s m, Monoid e, Plus f) => MonadState s (ContinueT e f m) where+ get = lift get+ put = lift . put+ state = lift . state++instance (Plus f) => MonadTrans (ContinueT e f) where+ lift = ContinueT . liftM (\x -> (Right x, zero))++instance (MonadWriter l m, Monoid e, Plus f) => MonadWriter l (ContinueT e f m) where+ listen (ContinueT c) =+ ContinueT $ do+ ((mx, cf), w) <- listen c+ let addLog x = (x, w)+ return (fmap addLog mx, fmap (fmap addLog) cf)++ pass (ContinueT c) =+ ContinueT . pass $ do+ (mx, cf') <- c+ let cf = fmap (fmap fst) cf'+ case mx of+ Left ex -> return ((Left ex, cf), id)+ Right (x, f) -> return ((Right x, cf), f)++ tell = lift . tell+ writer = lift . writer+++-- | 'ContinueT' over 'Identity'.++type Continue e f = ContinueT e f Identity+++-- | Type alias for the common case of using @'Last' 'SomeException'@ as+-- the suspension monoid.++type LastEx = Last SomeException
+ Control/Continue/Utils.hs view
@@ -0,0 +1,51 @@+-- |+-- Module: Control.Continue.Utils+-- Copyright: (c) 2012 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Suspension/continuation utilities.++module Control.Continue.Utils+ ( -- * Basic utilities+ addCont_,+ continue,+ continue_,+ suspend+ )+ where++import Control.Continue.Class+import Data.Functor.Plus+++-- | Add the given set of continuations without suspending.++addCont_ :: (MonadContinue e f m) => f (m ()) -> m ()+addCont_ = addCont (Right ())+++-- | Allow to continue here with the given value.++continue ::+ (MonadContinue e f m)+ => Either e a -- ^ What to return now (left suspends).+ -> f (Either e a) -- ^ What to return when reentering (left suspends).+ -> m a+continue mx = addCont mx . fmap (\mx -> addCont mx zero)+++-- | Allow to continue here.++continue_ ::+ (MonadContinue e f m)+ => f () -- ^ Reentering key.+ -> m ()+continue_ = addCont (Right ()) . fmap (const $ return ())+++-- | Suspend with the given value. Does not register any continuation+-- spots. Note that @suspend mempty@ is equivalent to @empty@.++suspend :: (MonadContinue e f m) => e -> m a+suspend ex = addCont (Left ex) zero
+ LICENSE view
@@ -0,0 +1,32 @@+continue license+Copyright (c) 2012, Ertugrul Soeylemez++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in+ the documentation and/or other materials provided with the+ distribution.++ * Neither the name of the author nor the names of any contributors+ may be used to endorse or promote products derived from this+ software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,95 @@+The ContinueT monad transformer+===============================++Quickstart tutorial+-------------------++This monad transformer allows you to set continuation spots in a+computation and reenter it at any of those spots possibly causing+different behavior:++ label name = continue_ (M.singleton name ())++ myComp = do+ label "first"+ liftIO (putStrLn "Hello")+ label "second"+ liftIO (putStrLn "World!")++This computation gives you two spots for reentry, "first" and "second".+If you reenter at "first", the whole computation will be run again. If+you reenter at "second", only the second `putStrLn` computation will be+run again.+++### Advanced reentering++The most general way to reenter is to perform a computation when+reentering:++ addCont (Right False) . M.singleton "abc" $ do+ liftIO (putStrLn "Reentered at spot abc.")+ return True++In the primary run this computation will just return `False`, but it+will also register a continuation spot. When you reenter the+computation using that spot, it will return `True` instead.+++### Suspension++A `ContinueT` monad also allows you to suspend a computation. This+basically means that the computation is aborted at some spot to be+reentered somewhere else (or at the same spot) later. You can do this+with the `empty` combinator as well as the `suspend` function. Examples+TODO.+++ContinueT explained+-------------------++`ContinueT` takes four arguments:++ newtype ContinueT e f m a++To form a monad, *e* has to be a monoid, *f* has to be a `Plus` functor+(as defined by the [semigroupoids] package) and *m* has to be a monad.+++### The reentry functor++The most important parameter is the reentry functor *f*. It represents+the type for maps of reentry points. Example:++ ContinueT e (Map String) m a++A computation of this type may register reentry points indexed by+strings. All registered reentry points are combined according to the+functor's `Plus` instance.+++### The suspension monoid++Computations may suspend, in which case control is returned to a+controller, which may be the application loop or the `(<|>)` combinator:++ c1 <|> c2++This computation runs both `c1` and `c2` (note: even if neither+suspends) and returns the result of the left-most computation that did+not suspend.++When a computation suspends it may do so with a value of type *e* that+might communicate the reason for suspending. This type must be a+monoid, because multiple branches of a `(<|>)` composition may suspend,+in which case the suspension values are combined according to the+`Monoid` instance of *e*.++A reasonable suspension monoid is `Last SomeException`, and since this+will likely be a very common choice this library defines a shorthand for+it:++ type LastEx = Last SomeException+++[semigroupoids]: <http://hackage.haskell.org/package/semigroupoids>
+ Setup.lhs view
@@ -0,0 +1,12 @@+continue setup script+Copyright (C) 2012, Ertugrul Soeylemez++Please see the LICENSE file for terms and conditions of use,+modification and distribution of this package, including this file.++> module Main where+>+> import Distribution.Simple+>+> main :: IO ()+> main = defaultMain
+ continue.cabal view
@@ -0,0 +1,62 @@+Name: continue+Version: 0.1.0+Category: Continuation-based user interaction monad+Synopsis: Control+Maintainer: Ertugrul Söylemez <es@ertes.de>+Author: Ertugrul Söylemez <es@ertes.de>+Copyright: (c) 2012 Ertugrul Söylemez+License: BSD3+License-file: LICENSE+Build-type: Simple+Stability: experimental+Cabal-version: >= 1.10+Extra-source-files: README.md+Description:+ This library implements a monad transformer for suspendable+ computations, similar and related to free comonads. It allows to+ write continuation-based web frameworks, command line applications+ and similar interfaces.++Source-repository head+ type: git+ location: git://github.com/ertes/continue.git++Library+ Build-depends:+ base >= 4.5 && < 5,+ bifunctors >= 3.0 && < 4,+ monad-control >= 0.3 && < 1,+ mtl >= 2.0 && < 3,+ semigroupoids >= 3.0 && < 4,+ transformers >= 0.3 && < 1,+ transformers-base >= 0.4 && < 1+ Default-language: Haskell2010+ Default-extensions:+ FlexibleInstances+ FunctionalDependencies+ MultiParamTypeClasses+ TypeFamilies+ Other-extensions:+ UndecidableInstances+ GHC-Options: -W+ Exposed-modules:+ Control.Continue+ Control.Continue.Class+ Control.Continue.Types+ Control.Continue.Utils++-- Test-suite continue-test+-- Type: exitcode-stdio-1.0+-- Build-depends:+-- base >= 4.5 && < 5,+-- conduit,+-- containers,+-- continue,+-- http-types,+-- wai,+-- warp+-- Default-language: Haskell2010+-- Default-extensions:+-- GHC-Options: -threaded -rtsopts+-- Hs-source-dirs: test+-- Main-is: Main.hs