-----------------------------------------------------------------------------
-- |
-- Module : Control.Monad.Trans.Abort
-- Copyright : (c) Gregory Crosswhite 2010
-- License : BSD3
-- Maintainer : gcross@phys.washington.edu
-- Stability : provisional
-- Portability : portable
--
-- This module provides a monad and a monad transformer that allow the
-- user to abort a monadic computation and immediately return a
-- result.
-----------------------------------------------------------------------------
module Control.Monad.Trans.Abort (
-- * The Abort monad
Abort,
runAbort,
-- * The AbortT monad transformer
AbortT(..),
runAbortT,
-- * Abort operations
abort
) where
import Control.Applicative
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Data.Functor
import Data.Functor.Identity
-- ---------------------------------------------------------------------------
-- | An abort monad, parametrized by the type @r@ of the value to return.
type Abort r = AbortT r Identity
-- | Execute the abort monad computation and return the resulting value.
runAbort :: Abort r r -- ^ the monadic computation to run
-> r -- ^ the result of the computation
runAbort = runIdentity . runAbortT
-- ---------------------------------------------------------------------------
-- | An abort monad transformer parametrized by
--
-- * @r@ - the value that will ultimately be returned; and
--
-- * @m@ - the inner monad.
--
-- The 'AbortT' type wraps a monadic value that is either
--
-- * 'Left' @r@, which indicates that the monadic computation has
-- terminated with result @r@ and so all further steps in the computation
-- should be ignored; or
--
-- * 'Right' @a@, which indicates that the computation is proceding normally
-- and that its current value is @a@.
newtype AbortT r m a = AbortT { unwrapAbortT :: m (Either r a) }
instance Functor m => Functor (AbortT r m) where
fmap f = AbortT . fmap (either Left (Right . f)) . unwrapAbortT
instance Applicative m => Applicative (AbortT r m) where
pure = AbortT . fmap Right . pure
(AbortT m) <*> (AbortT x) = AbortT ((fmap h m) <*> x)
where
h (Left g) = const (Left g)
h (Right f) = either Left (Right . f)
instance Monad m => Monad (AbortT r m) where
return = AbortT . return . Right
(AbortT m) >>= f = AbortT $ m >>= either (return . Left) (unwrapAbortT . f)
instance MonadIO m => MonadIO (AbortT r m) where
liftIO = lift . liftIO
instance MonadTrans (AbortT r) where
lift = AbortT . (>>= return . Right)
-- | Execute the abort monad computation and return the resulting
-- (monadic) value.
runAbortT :: Monad m
=> AbortT r m r -- ^ the monadic computation to run
-> m r -- ^ the (monadic) result of the computation
runAbortT (AbortT m) = m >>= either return return
-- ---------------------------------------------------------------------------
-- | Abort the computation and immediately return a result; all steps
-- in the computation after this monadic computation will be ignored.
--
-- Note that since no further computation is performed after this, there is
-- no way for subsequent computations to access the monadic value, and so it
-- can be assigned an arbitrary type.
abort :: Monad m
=> r -- ^ the result to return
-> AbortT r m a -- ^ a monadic value that has the effect of
-- terminating the computation and immediately
-- returning a value; note that since all
-- subsequent steps in the computation will be
-- ignored, this monadic value can take an
-- arbitrary type since its value will never
-- be accessed
abort = AbortT . return . Left