{-# LANGUAGE TypeOperators #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} -- | Effects which fail. module Control.Eff.Fail( Fail , die , runFail , ignoreFail , onFail ) where import Data.Typeable import Control.Eff import Control.Monad -- | 'Fail' represents effects which can fail. This is akin to the Maybe monad. data Fail v = Fail deriving (Functor, Typeable) -- | Makes an effect fail, preventing future effects from happening. die :: Member Fail r => Eff r () die = send (const (inj Fail)) {-# INLINE die #-} -- | Runs a failable effect, such that failed computation return 'Nothing', and -- 'Just' the return value on success. runFail :: Eff (Fail :> r) a -> Eff r (Maybe a) runFail m = loop (admin m) where loop (Val x) = return (Just x) loop (E u) = handleRelay u loop (const (return Nothing)) {-# INLINE runFail #-} -- | Given a computation to run on failure, and a computation that can fail, -- this function runs the computation that can fail, and if it fails, gets -- the return value from the other computation. This hides the fact that a -- failure even happened, and returns a default value for when it does. onFail :: Eff r a -- ^ The computation to run on failure. -> Eff (Fail :> r) a -- ^ The computation which can fail. -> Eff r a onFail sideshow mainEvent = do r <- runFail mainEvent case r of Nothing -> sideshow Just y -> return y {-# INLINE onFail #-} -- | Ignores a failure event. Since the event can fail, you cannot inspect its -- return type, because it has none on failure. To inspect it, use 'runFail'. ignoreFail :: Eff (Fail :> r) a -> Eff r () ignoreFail = onFail (return ()) . void {-# INLINE ignoreFail #-}