attempt 0.0.0 → 0.0.1
raw patch · 5 files changed
+77/−211 lines, 5 filesdep +control-monad-failurePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: control-monad-failure
API changes (from Hackage documentation)
- Control.Monad.Attempt: instance (Monad m) => MonadAttempt (AttemptT m)
- Control.Monad.Attempt.Class: StringException :: String -> StringException
- Control.Monad.Attempt.Class: class (Monad m) => MonadAttempt m
- Control.Monad.Attempt.Class: failure :: (MonadAttempt m, Exception e) => e -> m v
- Control.Monad.Attempt.Class: failureString :: (MonadAttempt m) => String -> m v
- Control.Monad.Attempt.Class: instance Exception StringException
- Control.Monad.Attempt.Class: instance Show StringException
- Control.Monad.Attempt.Class: instance Typeable StringException
- Control.Monad.Attempt.Class: newtype StringException
- Control.Monad.Attempt.Class: wrapFailure :: (MonadAttempt m, Exception eOut) => (forall eIn. (Exception eIn) => eIn -> eOut) -> m v -> m v
- Data.Attempt: instance MonadAttempt Attempt
- Data.Attempt.Helper: CouldNotRead :: String -> CouldNotRead
- Data.Attempt.Helper: EmptyList :: EmptyList
- Data.Attempt.Helper: KeyNotFound :: k -> [(k, v)] -> KeyNotFound k v
- Data.Attempt.Helper: NegativeIndex :: NegativeIndex
- Data.Attempt.Helper: assert :: (MonadAttempt m, Exception e) => Bool -> v -> e -> m v
- Data.Attempt.Helper: at :: (MonadAttempt m) => [a] -> Int -> m a
- Data.Attempt.Helper: data EmptyList
- Data.Attempt.Helper: data KeyNotFound k v
- Data.Attempt.Helper: data NegativeIndex
- Data.Attempt.Helper: head :: (MonadAttempt m) => [a] -> m a
- Data.Attempt.Helper: init :: (MonadAttempt m) => [a] -> m [a]
- Data.Attempt.Helper: instance (Show k) => Show (KeyNotFound k v)
- Data.Attempt.Helper: instance (Typeable k, Typeable v, Show k) => Exception (KeyNotFound k v)
- Data.Attempt.Helper: instance Exception CouldNotRead
- Data.Attempt.Helper: instance Exception EmptyList
- Data.Attempt.Helper: instance Exception NegativeIndex
- Data.Attempt.Helper: instance Exception OutOfBoundsIndex
- Data.Attempt.Helper: instance Show CouldNotRead
- Data.Attempt.Helper: instance Show EmptyList
- Data.Attempt.Helper: instance Show NegativeIndex
- Data.Attempt.Helper: instance Show OutOfBoundsIndex
- Data.Attempt.Helper: instance Typeable CouldNotRead
- Data.Attempt.Helper: instance Typeable EmptyList
- Data.Attempt.Helper: instance Typeable NegativeIndex
- Data.Attempt.Helper: instance Typeable OutOfBoundsIndex
- Data.Attempt.Helper: instance Typeable2 KeyNotFound
- Data.Attempt.Helper: join :: (FromAttempt m, Monad m) => m (Attempt v) -> m v
- Data.Attempt.Helper: last :: (MonadAttempt m) => [a] -> m a
- Data.Attempt.Helper: lookup :: (Typeable k, Typeable v, Show k, Eq k, MonadAttempt m) => k -> [(k, v)] -> m v
- Data.Attempt.Helper: newtype CouldNotRead
- Data.Attempt.Helper: read :: (MonadAttempt m, Read a) => String -> m a
- Data.Attempt.Helper: readFile :: (MonadAttempt m, MonadIO m) => FilePath -> m String
- Data.Attempt.Helper: tail :: (MonadAttempt m) => [a] -> m [a]
+ Control.Monad.Attempt: instance (Exception e, Monad m) => MonadFailure e (AttemptT m)
+ Control.Monad.Attempt: instance (Monad m, Exception e) => WrapFailure e (AttemptT m)
+ Data.Attempt: AttemptHandler :: (e -> v) -> AttemptHandler v
+ Data.Attempt: failures :: [Attempt v] -> [SomeException]
+ Data.Attempt: fromSuccess :: Attempt v -> v
+ Data.Attempt: instance (Exception e) => MonadFailure e Attempt
+ Data.Attempt: instance (Exception e) => WrapFailure e Attempt
+ Data.Attempt: isFailure :: Attempt v -> Bool
+ Data.Attempt: isSuccess :: Attempt v -> Bool
+ Data.Attempt: joinAttempt :: (FromAttempt m, Monad m) => m (Attempt v) -> m v
+ Data.Attempt: partitionAttempts :: [Attempt v] -> ([SomeException], [v])
+ Data.Attempt: successes :: [Attempt v] -> [v]
- Data.Attempt: Failure :: e -> Attempt v
+ Data.Attempt: Failure :: SomeException -> Attempt v
Files
- Control/Monad/Attempt.hs +9/−4
- Control/Monad/Attempt/Class.hs +0/−58
- Data/Attempt.hs +65/−13
- Data/Attempt/Helper.hs +0/−132
- attempt.cabal +3/−4
Control/Monad/Attempt.hs view
@@ -24,6 +24,7 @@ import Control.Applicative import Control.Monad import Control.Monad.Trans+import Control.Exception (Exception, SomeException (..)) newtype AttemptT m v = AttemptT { runAttemptT :: m (Attempt v)@@ -36,10 +37,14 @@ (<*>) = ap instance Monad m => Monad (AttemptT m) where return = AttemptT . return . Success- (AttemptT mv) >>= f = AttemptT $- mv >>= attempt (return . Failure) (runAttemptT . f)-instance Monad m => MonadAttempt (AttemptT m) where- failure = AttemptT . return . Failure+ (AttemptT mv) >>= f = AttemptT $ do+ v <- mv+ case v of+ Success v' -> runAttemptT $ f v'+ Failure e -> return $ Failure e+instance (Exception e, Monad m) => MonadFailure e (AttemptT m) where+ failure = AttemptT . return . Failure . SomeException+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
− Control/Monad/Attempt/Class.hs
@@ -1,58 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE Rank2Types #-}---------------------------------------------------------------- Module : Control.Monad.Attempt.Class--- Copyright : Michael Snoyman--- License : BSD3------ Maintainer : Michael Snoyman <michael@snoyman.com>--- Stability : Unstable--- Portability : portable----------------------------------------------------------------- | Defines a type class for any monads which may report failure using--- extensible exceptions.-module Control.Monad.Attempt.Class- ( MonadAttempt (..)- , StringException (..)- ) where--import Control.Exception-import Data.Generics---- | Any 'Monad' which may report failure using extensible exceptions. This--- most obviously applies to the Attempt data type, but you should just as well--- use this for arbitrary 'Monad's.------ Usage should be straight forward: 'return' successes and 'failure' errors.--- If you simply want to send a string error message, use 'failureString'.--- Although tempting to do so, 'fail' is *not* used as a synonym for--- 'failureString'; 'fail' should not be used at all.------ Minimal complete definition: 'failure' and 'wrapFailure'.-class Monad m => MonadAttempt m where- failure :: Exception e => e -> m v-- -- | Call 'failure' by wrapping the argument in a 'StringException'.- failureString :: String -> m v- failureString = failure . StringException-- -- | Wrap the failure value, if any, with the given function. This is- -- useful in particular when you want all the exceptions returned from a- -- certain library to be of a certain type, even if they were generated by- -- a different library.- wrapFailure :: Exception eOut- => (forall eIn. Exception eIn => eIn -> eOut)- -> m v- -> m v---- | A simple exception which simply contains a string. Note that the 'Show'--- instance simply returns the contained string.-newtype StringException = StringException String- deriving Typeable-instance Show StringException where- show (StringException s) = s-instance Exception StringException
Data/Attempt.hs view
@@ -21,30 +21,39 @@ -- you want this kind of functionality, something like control-monad-exception -- might be a more appropriate fit. module Data.Attempt- ( Attempt (..)+ ( -- * Data type and type class+ Attempt (..) , FromAttempt (..) , fa+ , joinAttempt+ -- * General handling of 'Attempt's , attempt , makeHandler- , AttemptHandler- , module Control.Monad.Attempt.Class+ , AttemptHandler (..)+ -- * Individual 'Attempt's+ , isFailure+ , isSuccess+ , fromSuccess+ -- * Lists of 'Attempt's+ , successes+ , failures+ , partitionAttempts+ -- * Reexport the 'MonadFailure' class+ , module Control.Monad.Failure ) where import qualified Control.Exception as E import Control.Monad (ap) import Control.Applicative import Data.Generics-import Control.Monad.Attempt.Class+import Data.Either (lefts)+import Control.Monad.Failure -- | Contains either a 'Success' value or a 'Failure' exception. data Attempt v = Success v- | forall e. E.Exception e => Failure e- deriving (Typeable)--instance Show v => Show (Attempt v) where- show (Success v) = "Success " ++ show v- show (Failure e) = "Failure " ++ show e+ | Failure E.SomeException+ deriving (Show, Typeable) instance Functor Attempt where fmap f (Success v) = Success $ f v@@ -56,10 +65,12 @@ return = Success (Success v) >>= f = f v (Failure e) >>= _ = Failure e-instance MonadAttempt Attempt where- failure = Failure+instance E.Exception e => MonadFailure e Attempt where+ failure = Failure . E.SomeException+instance E.Exception e => WrapFailure e Attempt where wrapFailure _ (Success v) = Success v- wrapFailure f (Failure e) = Failure $ f e+ wrapFailure f (Failure (E.SomeException e)) =+ Failure $ E.SomeException $ f e -- | Any type which can be converted from an 'Attempt'. The included instances are your \"usual suspects\" for dealing with error handling. They include: --@@ -93,6 +104,15 @@ instance FromAttempt (Either E.SomeException) where fromAttempt = attempt (Left . E.SomeException) 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+-- inner 'Attempt' type, reporting errors as defined in the 'FromAttempt'+-- instance.+--+-- For example, join (Just (failureString \"foo\")) == Nothing.+joinAttempt :: (FromAttempt m, Monad m) => m (Attempt v) -> m v+joinAttempt = (>>= fromAttempt)+ -- | 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@@ -121,3 +141,35 @@ -- | 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)++-- | Tests for a 'Success' value.+isSuccess :: Attempt v -> Bool+isSuccess = attempt (const False) (const True)++-- | This is an unsafe, partial function which should only be used if you+-- either know that a function will succeed or don't mind the occassional+-- runtime exception.+fromSuccess :: Attempt v -> v+fromSuccess = attempt (error . show) id++-- | Returns only the 'Success' values.+successes :: [Attempt v] -> [v]+successes l = [ v | Success v <- l ]++-- | Returns only the 'Failure' values, each wrapped in a 'SomeException'.+failures :: [Attempt v] -> [E.SomeException]+failures = lefts . map eitherExceptionFromAttempt where+ eitherExceptionFromAttempt :: Attempt v -> Either E.SomeException v+ eitherExceptionFromAttempt = fa++-- | Return all of the 'Failure's and 'Success'es separately in a tuple.+partitionAttempts :: [Attempt v] -> ([E.SomeException], [v])+partitionAttempts = foldr (attempt f s) ([],[])+ where+ f a (l, r) = (E.SomeException a:l, r)+ s a (l, r) = (l, a:r)
− Data/Attempt/Helper.hs
@@ -1,132 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}---------------------------------------------------------------- Module : Data.Attempt.Helper--- Copyright : Michael Snoyman--- License : BSD3------ Maintainer : Michael Snoyman <michael@snoyman.com>--- Stability : Unstable--- Portability : portable-------------------------------------------------------------- | Replacements for standard functions to represent failure with a--- 'MonadAttempt'. Lots of inspiration taken from the "safe" package.-module Data.Attempt.Helper- ( -- * Non-standard functions- join- -- * Exception types- , KeyNotFound (..)- , EmptyList (..)- , CouldNotRead (..)- , NegativeIndex (..)- -- * Standard functions reimplemented- , lookup- , tail- , init- , head- , last- , read- , at- , assert- -- * IO functions with exceptions handled- , readFile- ) where--import Prelude hiding (lookup, tail, init, head, last, read, readFile)-import qualified Prelude-import Data.Attempt-import Control.Monad.Attempt.Class-import Data.Generics-import qualified Control.Exception as E-import Control.Monad (liftM)-import Control.Monad.Trans---- | This is not a simple translation of the Control.Monad.join function.--- Instead, for 'Monad's which are instances of 'FromAttempt', it removes the--- inner 'Attempt' type, reporting errors as defined in the 'FromAttempt'--- instance.------ For example, join (Just (failureString \"foo\")) == Nothing.-join :: (FromAttempt m, Monad m) => m (Attempt v) -> m v-join = (>>= fromAttempt)---- | Exception type for the 'lookup' function.-data KeyNotFound k v = KeyNotFound k [(k, v)]- deriving Typeable-instance Show k => Show (KeyNotFound k v) where- show (KeyNotFound key _) = "Could not find requested key: " ++ show key-instance (Typeable k, Typeable v, Show k) => E.Exception (KeyNotFound k v)--lookup :: (Typeable k, Typeable v, Show k, Eq k, MonadAttempt m)- => k- -> [(k, v)]- -> m v-lookup k m = maybe (failure $ KeyNotFound k m) return $ Prelude.lookup k m---- | Exception type for functions which expect non-empty lists.-data EmptyList = EmptyList- deriving (Show, Typeable)-instance E.Exception EmptyList--tail :: MonadAttempt m => [a] -> m [a]-tail [] = failure EmptyList-tail (_:rest) = return rest--init :: MonadAttempt m => [a] -> m [a]-init [] = failure EmptyList-init x = return $ Prelude.init x--head :: MonadAttempt m => [a] -> m a-head [] = failure EmptyList-head (x:_) = return x--last :: MonadAttempt m => [a] -> m a-last [] = failure EmptyList-last x = return $ Prelude.last x---- | Report errors from the 'read' function.-newtype CouldNotRead = CouldNotRead String- deriving (Typeable, Show)-instance E.Exception CouldNotRead--read :: (MonadAttempt m, Read a) => String -> m a-read s = case [x | (x,t) <- reads s, ("","") <- lex t] of- [x] -> return x- _ -> failure $ CouldNotRead s---- | For functions which expect index values >= 0.-data NegativeIndex = NegativeIndex- deriving (Typeable, Show)-instance E.Exception NegativeIndex-data OutOfBoundsIndex = OutOfBoundsIndex- deriving (Typeable, Show)-instance E.Exception OutOfBoundsIndex---- | Same as Prelude.!!. Name stolen from safe library.-at :: MonadAttempt m => [a] -> Int -> m a-at [] _ = failure OutOfBoundsIndex-at (x:_) 0 = return x-at (_:xs) n- | n < 0 = failure NegativeIndex- | otherwise = at xs $ n - 1---- | Assert a value to be true. If true, returns the first value as a succss.--- Otherwise, returns the second value as a failure.-assert :: (MonadAttempt m, E.Exception e)- => Bool- -> v- -> e- -> m v-assert b v e = if b then return v else failure e---- | The standard readFile function with any 'E.IOException's returned as a--- failure instead of a runtime exception.-readFile :: (MonadAttempt m, MonadIO m) => FilePath -> m String-readFile fp = do- contents <- liftIO $ E.handle- (\e -> return $ Left (e :: E.IOException))- (liftM Right $ Prelude.readFile fp)- case contents of- Left e -> failure e- Right v -> return v
attempt.cabal view
@@ -1,5 +1,5 @@ name: attempt-version: 0.0.0+version: 0.0.1 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>@@ -15,9 +15,8 @@ library build-depends: base >= 4 && < 5, syb,- transformers >= 0.1.4.0+ transformers >= 0.1.4.0,+ control-monad-failure >= 0.2 exposed-modules: Data.Attempt- Data.Attempt.Helper Control.Monad.Attempt- Control.Monad.Attempt.Class ghc-options: -Wall