packages feed

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 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