attempt 0.3.1 → 0.3.1.1
raw patch · 4 files changed
+264/−263 lines, 4 filessetup-changed
Files
- Data/Attempt.hs +209/−208
- LICENSE +25/−25
- Setup.lhs +11/−11
- attempt.cabal +19/−19
Data/Attempt.hs view
@@ -1,208 +1,209 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE TypeFamilies #-}----------------------------------------------------------------- Module : Data.Attempt--- Copyright : Michael Snoyman--- License : BSD3------ Maintainer : Michael Snoyman <michael@snoyman.com>--- Stability : Unstable--- Portability : portable------ A universal data type for computations which may fail.-------------------------------------------------------------- | A universal data type for computations which may fail. Errors are reported--- using extensible exceptions. These exceptions are not explicitly stated; if--- you want this kind of functionality, something like control-monad-exception--- might be a more appropriate fit.-module Data.Attempt- ( -- * Data type and type class- Attempt (..)- , FromAttempt (..)- , fa- , joinAttempt- -- * General handling of 'Attempt's- , attempt- , makeHandler- , AttemptHandler (..)- -- * Individual 'Attempt's- , isFailure- , isSuccess- , fromSuccess- -- * Lists of 'Attempt's- , successes- , failures- , partitionAttempts- -- * Runtime exceptions- , attemptIO- -- * Reexport the 'Failure' class- , module Control.Failure- ) where--import qualified Control.Exception as E-import Control.Monad (ap)-import Control.Applicative-import Data.Data-import Data.Either (lefts)-import Control.Failure-import GHC.Show (appPrec, appPrec1)--- | 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- showsPrec p (Success v)- = showParen (p > appPrec) $ showString "Success " . showsPrec appPrec1 v- showsPrec p (Failure v)- = showParen (p > appPrec) $- showString "Failure " . showsPrec appPrec1 v--instance Functor Attempt where- fmap f (Success v) = Success $ f v- fmap _ (Failure e) = Failure e-instance Applicative Attempt where- pure = Success- (<*>) = ap-instance Monad Attempt where- return = Success- (Success v) >>= f = f v- (Failure e) >>= _ = Failure e-instance E.Exception e => Failure e Attempt where- failure = Failure-instance E.Exception e => WrapFailure e Attempt where- wrapFailure _ (Success v) = Success v- wrapFailure f (Failure e) = Failure $ f e--instance Try Attempt where- type Error Attempt = E.SomeException- try (Success v) = return v- try (Failure e) = failure $ E.toException e---- | Any type which can be converted from an 'Attempt'. The included instances are your \"usual suspects\" for dealing with error handling. They include:------ 'IO': For the IO instance, any exceptions in the 'Attempt' are thrown as--- runtime exceptions.------ 'Maybe': Returns 'Nothing' on 'Failure', or 'Just' on 'Success'.------ List: Returns the empty list on 'Failure', or a singleton list on 'Success'.------ 'Either' 'String': Returns 'Left' ('show' exception) on 'Failure', or 'Right' on--- 'Success'.------ 'Either' 'E.Exception': Returns 'Left' exception on 'Failure', or 'Right' on--- 'Success'.-class FromAttempt a where- fromAttempt :: Attempt v -> a v---- | A shortcut for 'fromAttempt'.-fa :: FromAttempt a => Attempt v -> a v-fa = fromAttempt--instance FromAttempt IO where- fromAttempt = attempt E.throwIO return-instance FromAttempt Maybe where- fromAttempt = attempt (const Nothing) Just-instance FromAttempt [] where- fromAttempt = attempt (const []) (: [])-instance FromAttempt (Either String) where- fromAttempt = attempt (Left . show) Right-instance FromAttempt (Either E.SomeException) where- 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--- 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--- 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---- | 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 [] v _ = v-makeHandler (AttemptHandler h: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)---- | 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.toException a:l, r)- s a (l, r) = (l, a:r)---- | Catches runtime (ie, IO) exceptions and inserts them into an 'Attempt'.------ Like 'handle', the first argument to this function must explicitly state the--- type of its input.-attemptIO :: (E.Exception eIn, E.Exception eOut)- => (eIn -> eOut)- -> IO v- -> IO (Attempt v)-attemptIO f =- E.handle (return . Failure . f)- . fmap Success+{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE Rank2Types #-} +{-# LANGUAGE TypeFamilies #-} + +--------------------------------------------------------- +-- +-- Module : Data.Attempt +-- Copyright : Michael Snoyman +-- License : BSD3 +-- +-- Maintainer : Michael Snoyman <michael@snoyman.com> +-- Stability : Unstable +-- Portability : portable +-- +-- A universal data type for computations which may fail. +--------------------------------------------------------- + +-- | A universal data type for computations which may fail. Errors are reported +-- using extensible exceptions. These exceptions are not explicitly stated; if +-- you want this kind of functionality, something like control-monad-exception +-- might be a more appropriate fit. +module Data.Attempt + ( -- * Data type and type class + Attempt (..) + , FromAttempt (..) + , fa + , joinAttempt + -- * General handling of 'Attempt's + , attempt + , makeHandler + , AttemptHandler (..) + -- * Individual 'Attempt's + , isFailure + , isSuccess + , fromSuccess + -- * Lists of 'Attempt's + , successes + , failures + , partitionAttempts + -- * Runtime exceptions + , attemptIO + -- * Reexport the 'Failure' class + , module Control.Failure + ) where + +import qualified Control.Exception as E +import Control.Monad (ap) +import Control.Applicative +import Data.Data +import Data.Either (lefts) +import Control.Failure +import GHC.Show (appPrec, appPrec1) +-- | 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 + showsPrec p (Success v) + = showParen (p > appPrec) $ showString "Success " . showsPrec appPrec1 v + showsPrec p (Failure v) + = showParen (p > appPrec) $ + showString "Failure " . showsPrec appPrec1 v + +instance Functor Attempt where + fmap f (Success v) = Success $ f v + fmap _ (Failure e) = Failure e +instance Applicative Attempt where + pure = Success + (<*>) = ap +instance Monad Attempt where + return = Success + (Success v) >>= f = f v + (Failure e) >>= _ = Failure e +instance E.Exception e => Failure e Attempt where + failure = Failure +instance E.Exception e => WrapFailure e Attempt where + wrapFailure _ (Success v) = Success v + wrapFailure f (Failure e) = Failure $ f e + +instance Try Attempt where + type Error Attempt = E.SomeException + try (Success v) = return v + try (Failure e) = failure $ E.toException e + +-- | Any type which can be converted from an 'Attempt'. The included instances are your \"usual suspects\" for dealing with error handling. They include: +-- +-- 'IO': For the IO instance, any exceptions in the 'Attempt' are thrown as +-- runtime exceptions. +-- +-- 'Maybe': Returns 'Nothing' on 'Failure', or 'Just' on 'Success'. +-- +-- List: Returns the empty list on 'Failure', or a singleton list on 'Success'. +-- +-- 'Either' 'String': Returns 'Left' ('show' exception) on 'Failure', or 'Right' on +-- 'Success'. +-- +-- 'Either' 'E.Exception': Returns 'Left' exception on 'Failure', or 'Right' on +-- 'Success'. +class FromAttempt a where + fromAttempt :: Attempt v -> a v + +-- | A shortcut for 'fromAttempt'. +fa :: FromAttempt a => Attempt v -> a v +fa = fromAttempt + +instance FromAttempt IO where + fromAttempt = attempt E.throwIO return +instance FromAttempt Maybe where + fromAttempt = attempt (const Nothing) Just +instance FromAttempt [] where + fromAttempt = attempt (const []) (: []) +instance FromAttempt (Either String) where + fromAttempt = attempt (Left . show) Right +instance FromAttempt (Either E.SomeException) where + 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 +-- 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 +-- 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 + +-- | 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 [] v _ = v +makeHandler (AttemptHandler h: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) + +-- | 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 :: E.Exception e => e -> ([E.SomeException], [v]) -> ([E.SomeException], [v]) + f a (l, r) = (E.toException a:l, r) + s a (l, r) = (l, a:r) + +-- | Catches runtime (ie, IO) exceptions and inserts them into an 'Attempt'. +-- +-- Like 'handle', the first argument to this function must explicitly state the +-- type of its input. +attemptIO :: (E.Exception eIn, E.Exception eOut) + => (eIn -> eOut) + -> IO v + -> IO (Attempt v) +attemptIO f = + E.handle (return . Failure . f) + . fmap Success
LICENSE view
@@ -1,25 +1,25 @@-The following license covers this documentation, and the source code, except-where otherwise indicated.--Copyright 2008, Michael Snoyman. All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:--* Redistributions of source code must retain the above copyright notice, this- list of conditions and the following disclaimer.--* Redistributions in binary form must reproduce the above copyright notice,- this list of conditions and the following disclaimer in the documentation- and/or other materials provided with the distribution.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO-EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,-OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+The following license covers this documentation, and the source code, except +where otherwise indicated. + +Copyright 2008, Michael Snoyman. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Setup.lhs view
@@ -1,11 +1,11 @@-#!/usr/bin/env runhaskell--> module Main where-> import Distribution.Simple-> import System.Cmd (system)--> main :: IO ()-> main = defaultMainWithHooks (simpleUserHooks { runTests = runTests' })--> runTests' :: a -> b -> c -> d -> IO ()-> runTests' _ _ _ _ = system "runhaskell Test.hs" >> return ()+#!/usr/bin/env runhaskell + +> module Main where +> import Distribution.Simple +> import System.Cmd (system) + +> main :: IO () +> main = defaultMainWithHooks (simpleUserHooks { runTests = runTests' }) + +> runTests' :: a -> b -> c -> d -> IO () +> runTests' _ _ _ _ = system "runhaskell Test.hs" >> return ()
attempt.cabal view
@@ -1,19 +1,19 @@-name: attempt-version: 0.3.1-license: BSD3-license-file: LICENSE-author: Michael Snoyman, Nicolas Pouillard-maintainer: Michael Snoyman <michael@snoyman.com>-synopsis: Concrete data type for handling extensible exceptions as failures.-description: Defines a data type, Attempt, which has a Success and Failure constructor. Failure contains an extensible exception.-category: Data, Failure-stability: stable-cabal-version: >= 1.2-build-type: Simple-homepage: http://github.com/snoyberg/attempt/tree/master--library- build-depends: base >= 4 && < 5,- failure >= 0.1.0 && < 0.2- exposed-modules: Data.Attempt- ghc-options: -Wall+name: attempt +version: 0.3.1.1 +license: BSD3 +license-file: LICENSE +author: Michael Snoyman, Nicolas Pouillard +maintainer: Michael Snoyman <michael@snoyman.com> +synopsis: Concrete data type for handling extensible exceptions as failures. +description: Defines a data type, Attempt, which has a Success and Failure constructor. Failure contains an extensible exception. +category: Data, Failure +stability: stable +cabal-version: >= 1.2 +build-type: Simple +homepage: http://github.com/snoyberg/attempt/tree/master + +library + build-depends: base >= 4 && < 5, + failure >= 0.1.0 && < 0.2 + exposed-modules: Data.Attempt + ghc-options: -Wall