attempt 0.0.1 → 0.0.2
raw patch · 3 files changed
+57/−29 lines, 3 filesdep +monadlocdep ~control-monad-failure
Dependencies added: monadloc
Dependency ranges changed: control-monad-failure
Files
- Control/Monad/Attempt.hs +11/−8
- Data/Attempt.hs +42/−18
- attempt.cabal +4/−3
Control/Monad/Attempt.hs view
@@ -24,7 +24,8 @@ import Control.Applicative import Control.Monad import Control.Monad.Trans-import Control.Exception (Exception, SomeException (..))+import Control.Monad.Loc+import Control.Exception (Exception) newtype AttemptT m v = AttemptT { runAttemptT :: m (Attempt v)@@ -36,22 +37,24 @@ pure = return (<*>) = ap instance Monad m => Monad (AttemptT m) where- return = AttemptT . return . Success+ return = AttemptT . return . return (AttemptT mv) >>= f = AttemptT $ do v <- mv- case v of- Success v' -> runAttemptT $ f v'- Failure e -> return $ Failure e+ attempt (return . failure) (runAttemptT . f) v instance (Exception e, Monad m) => MonadFailure e (AttemptT m) where- failure = AttemptT . return . Failure . SomeException+ failure = AttemptT . return . failure instance (Monad m, Exception e) => WrapFailure e (AttemptT m) where wrapFailure f (AttemptT mv) = AttemptT $ liftM (wrapFailure f) mv instance MonadTrans AttemptT where- lift = AttemptT . liftM Success where+ lift = AttemptT . liftM return where instance MonadIO m => MonadIO (AttemptT m) where- liftIO = AttemptT . liftM Success . liftIO where+ liftIO = AttemptT . liftM return . liftIO where instance Monad m => FromAttempt (AttemptT m) where fromAttempt = attempt failure return+instance MonadLoc m => MonadLoc (AttemptT m) where+ withLoc loc (AttemptT a) = AttemptT $ do+ current <- withLoc loc a+ return $ withLoc loc current -- | Instances of 'FromAttempt' specify a manner for embedding 'Attempt' -- failures directly into the target data type. For example, the 'IO' instance
Data/Attempt.hs view
@@ -22,10 +22,11 @@ -- might be a more appropriate fit. module Data.Attempt ( -- * Data type and type class- Attempt (..)+ Attempt , FromAttempt (..) , fa , joinAttempt+ , monadicStackTrace -- * General handling of 'Attempt's , attempt , makeHandler@@ -48,29 +49,40 @@ import Data.Generics import Data.Either (lefts) import Control.Monad.Failure-+import Control.Monad.Loc+import GHC.Show (appPrec, appPrec1) -- | Contains either a 'Success' value or a 'Failure' exception.-data Attempt v =- Success v- | Failure E.SomeException- deriving (Show, Typeable)+data Attempt v+ = Success v+ | forall e. E.Exception e => Failure [String] e+ deriving (Typeable) +instance Show v => Show (Attempt v) where+ showsPrec p (Success v)+ = showParen (p > appPrec) $ showString "Success " . showsPrec appPrec1 v+ showsPrec p (Failure st v)+ = showParen (p > appPrec) $+ showString "Failure " . showsPrec appPrec1 st . showString " "+ . showsPrec appPrec1 v+ instance Functor Attempt where fmap f (Success v) = Success $ f v- fmap _ (Failure e) = Failure e+ fmap _ (Failure st e) = Failure st e instance Applicative Attempt where pure = Success (<*>) = ap instance Monad Attempt where return = Success (Success v) >>= f = f v- (Failure e) >>= _ = Failure e+ (Failure st e) >>= _ = Failure st e instance E.Exception e => MonadFailure e Attempt where- failure = Failure . E.SomeException+ failure = Failure [] instance E.Exception e => WrapFailure e Attempt where wrapFailure _ (Success v) = Success v- wrapFailure f (Failure (E.SomeException e)) =- Failure $ E.SomeException $ f e+ wrapFailure f (Failure st e) = Failure st $ f e+instance MonadLoc Attempt where+ withLoc _ (Success v) = Success v+ withLoc s (Failure st e) = Failure (s:st) e -- | Any type which can be converted from an 'Attempt'. The included instances are your \"usual suspects\" for dealing with error handling. They include: --@@ -102,7 +114,7 @@ instance FromAttempt (Either String) where fromAttempt = attempt (Left . show) Right instance FromAttempt (Either E.SomeException) where- fromAttempt = attempt (Left . E.SomeException) Right+ fromAttempt = attempt (Left . E.toException) Right -- | This is not a simple translation of the Control.Monad.join function. -- Instead, for 'Monad's which are instances of 'FromAttempt', it removes the@@ -113,36 +125,48 @@ joinAttempt :: (FromAttempt m, Monad m) => m (Attempt v) -> m v joinAttempt = (>>= fromAttempt) +-- | Extra the monadic stack trace from the given 'Attempt' value. Returns the+-- empty list when the given value is a 'Success'.+monadicStackTrace :: Attempt a -> [String]+monadicStackTrace (Success _) = []+monadicStackTrace (Failure st _) = st+ -- | Process either the exception or value in an 'Attempt' to produce a result. -- -- This function is modeled after 'maybe' and 'either'. The first argument must -- accept any instances of 'E.Exception'. If you want to handle multiple types -- of exceptions, see 'makeHandler'. The second argument converts the success -- value.+--+-- Note that this function does not expose all the data available in an+-- 'Attempt' value. Notably, the monadic stack trace is not passed on to the+-- error handler. If desired, use the 'monadicStackTrace' function to extract+-- it. attempt :: (forall e. E.Exception e => e -> b) -- ^ error handler -> (a -> b) -- ^ success handler -> Attempt a -> b attempt _ f (Success v) = f v-attempt f _ (Failure e) = f e+attempt f _ (Failure _ e) = f e -- | Convert multiple 'AttemptHandler's and a default value into an exception -- handler. -- -- This is a convenience function when you want to have special handling for a -- few types of 'E.Exception's and provide another value for anything else.-makeHandler :: [AttemptHandler v] -> v -> (forall e. E.Exception e => e -> v)+makeHandler :: [AttemptHandler v]+ -> v+ -> (forall e. E.Exception e => e -> v) makeHandler [] v _ = v makeHandler (AttemptHandler h:hs) v e =- case cast e of- Nothing -> makeHandler hs v e+ case E.fromException (E.toException e) of Just e' -> h e'+ Nothing -> makeHandler hs v e -- | A simple wrapper value necesary due to the Haskell type system. Wraps a -- function from a *specific* 'E.Exception' type to some value. data AttemptHandler v = forall e. E.Exception e => AttemptHandler (e -> v) - -- | Tests for a 'Failure' value. isFailure :: Attempt v -> Bool isFailure = attempt (const True) (const False)@@ -171,5 +195,5 @@ partitionAttempts :: [Attempt v] -> ([E.SomeException], [v]) partitionAttempts = foldr (attempt f s) ([],[]) where- f a (l, r) = (E.SomeException a:l, r)+ f a (l, r) = (E.toException a:l, r) s a (l, r) = (l, a:r)
attempt.cabal view
@@ -1,8 +1,8 @@ name: attempt-version: 0.0.1+version: 0.0.2 license: BSD3 license-file: LICENSE-author: Michael Snoyman <michael@snoyman.com>+author: Michael Snoyman, Nicolas Pouillard maintainer: Michael Snoyman <michael@snoyman.com> synopsis: Error handling using extensible exceptions outside the IO monad. description: Defines a data type, Attempt, which has a Success and Failure constructor. Failure contains an extensible exception.@@ -16,7 +16,8 @@ build-depends: base >= 4 && < 5, syb, transformers >= 0.1.4.0,- control-monad-failure >= 0.2+ control-monad-failure >= 0.4,+ monadloc >= 0.4 exposed-modules: Data.Attempt Control.Monad.Attempt ghc-options: -Wall