errors 1.4.3 → 1.4.4
raw patch · 8 files changed
+857/−841 lines, 8 filessetup-changed
Files
- Control/Error.hs +101/−101
- Control/Error/Safe.hs +249/−249
- Control/Error/Script.hs +58/−58
- Control/Error/Util.hs +195/−179
- Data/EitherR.hs +193/−193
- LICENSE +24/−24
- Setup.hs +2/−2
- errors.cabal +35/−35
Control/Error.hs view
@@ -1,101 +1,101 @@-{-| Import this module in your code to access the entire library's- functionality:--> import Control.Error-- This module exports the entire library as well as useful exports from other- standard error-handling libraries:-- * "Control.Error.Safe": Generalizes the @safe@ library, including 'Either',- 'EitherT', and 'MonadPlus' variations on total functions-- * "Control.Error.Script": Support for simple scripts that catch all errors- and transform them to 'String's-- * "Control.Error.Util": Utility functions and conversions between common- error-handling types-- * @Control.Monad.Trans.Either@: The 'EitherT' monad transformer-- * @Control.Monad.Trans.Maybe@: The 'MaybeT' monad transformer-- * @Data.Either@: 'Either' utility functions-- * "Data.EitherR": throw and catch functions, and their corresponding- \"success\" monads-- * @Data.Maybe@: 'Maybe' utility functions-- * @Safe@: Total versions of partial Prelude functions-- This module does not re-export partial functions from other libraries.--}--module Control.Error (- -- * Re-exports- module Control.Error.Safe,- module Control.Error.Script,- module Control.Error.Util,- module Control.Monad.Trans.Either,- module Control.Monad.Trans.Maybe,- module Data.Either,- module Data.EitherR,- module Data.Maybe,- module Safe- ) where--import Control.Error.Safe-import Control.Error.Script-import Control.Error.Util-import Control.Monad.Trans.Either (- EitherT(EitherT, runEitherT),- eitherT,- bimapEitherT,- mapEitherT,- hoistEither,- left,- right )-import Control.Monad.Trans.Maybe (- MaybeT(MaybeT, runMaybeT),- mapMaybeT,- liftCallCC,- liftCatch,- liftListen,- liftPass )-import Data.Either (either, lefts, rights, partitionEithers)-import Data.EitherR-import Data.Maybe (- maybe,- isJust,- isNothing,- fromMaybe,- listToMaybe,- maybeToList,- catMaybes,- mapMaybe )-import Safe (- tailDef,- tailMay,- tailSafe,- initDef,- initMay,- initSafe,- headDef,- headMay,- lastDef,- lastMay,- minimumDef,- minimumMay,- maximumDef,- maximumMay,- foldr1Def,- foldr1May,- foldl1Def',- foldl1May',- fromJustDef,- atDef,- atMay,- readDef,- readMay,- lookupJustDef,- findJustDef )+{-| Import this module in your code to access the entire library's + functionality: + +> import Control.Error + + This module exports the entire library as well as useful exports from other + standard error-handling libraries: + + * "Control.Error.Safe": Generalizes the @safe@ library, including 'Either', + 'EitherT', and 'MonadPlus' variations on total functions + + * "Control.Error.Script": Support for simple scripts that catch all errors + and transform them to 'String's + + * "Control.Error.Util": Utility functions and conversions between common + error-handling types + + * @Control.Monad.Trans.Either@: The 'EitherT' monad transformer + + * @Control.Monad.Trans.Maybe@: The 'MaybeT' monad transformer + + * @Data.Either@: 'Either' utility functions + + * "Data.EitherR": throw and catch functions, and their corresponding + \"success\" monads + + * @Data.Maybe@: 'Maybe' utility functions + + * @Safe@: Total versions of partial Prelude functions + + This module does not re-export partial functions from other libraries. +-} + +module Control.Error ( + -- * Re-exports + module Control.Error.Safe, + module Control.Error.Script, + module Control.Error.Util, + module Control.Monad.Trans.Either, + module Control.Monad.Trans.Maybe, + module Data.Either, + module Data.EitherR, + module Data.Maybe, + module Safe + ) where + +import Control.Error.Safe +import Control.Error.Script +import Control.Error.Util +import Control.Monad.Trans.Either ( + EitherT(EitherT, runEitherT), + eitherT, + bimapEitherT, + mapEitherT, + hoistEither, + left, + right ) +import Control.Monad.Trans.Maybe ( + MaybeT(MaybeT, runMaybeT), + mapMaybeT, + liftCallCC, + liftCatch, + liftListen, + liftPass ) +import Data.Either (either, lefts, rights, partitionEithers) +import Data.EitherR +import Data.Maybe ( + maybe, + isJust, + isNothing, + fromMaybe, + listToMaybe, + maybeToList, + catMaybes, + mapMaybe ) +import Safe ( + tailDef, + tailMay, + tailSafe, + initDef, + initMay, + initSafe, + headDef, + headMay, + lastDef, + lastMay, + minimumDef, + minimumMay, + maximumDef, + maximumMay, + foldr1Def, + foldr1May, + foldl1Def', + foldl1May', + fromJustDef, + atDef, + atMay, + readDef, + readMay, + lookupJustDef, + findJustDef )
Control/Error/Safe.hs view
@@ -1,249 +1,249 @@-{-| This module extends the @safe@ library's functions with corresponding- versions compatible with 'Either' and 'EitherT', and also provides a few- 'Maybe'-compatible functions missing from @safe@.-- I suffix the 'Either'-compatible functions with @Err@ and prefix the- 'EitherT'-compatible functions with @try@.-- Note that this library re-exports the 'Maybe' compatible functions from- @safe@ in the "Control.Error" module, so they are not provided here.-- The \'@Z@\'-suffixed functions generalize the 'Maybe' functions to also work- with anything that implements 'MonadPlus', including:-- * Lists-- * Most parsers-- * 'EitherT' (if the left value is a 'Monoid')--}--module Control.Error.Safe (- -- * Maybe-compatible functions- assertMay,- rightMay,-- -- * Either-compatible functions- tailErr,- initErr,- headErr,- lastErr,- minimumErr,- maximumErr,- foldr1Err,- foldl1Err,- foldl1Err',- atErr,- readErr,- assertErr,- justErr,-- -- * EitherT-compatible functions- tryTail,- tryInit,- tryHead,- tryLast,- tryMinimum,- tryMaximum,- tryFoldr1,- tryFoldl1,- tryFoldl1',- tryAt,- tryRead,- tryAssert,- tryJust,- tryRight,-- -- * MonadPlus-compatible functions- tailZ,- initZ,- headZ,- lastZ,- minimumZ,- maximumZ,- foldr1Z,- foldl1Z,- foldl1Z',- atZ,- readZ,- assertZ,- justZ,- rightZ- ) where--import Control.Error.Util (note)-import Control.Monad (MonadPlus(mzero))-import Control.Monad.Trans.Either (EitherT, hoistEither)-import qualified Safe as S---- | An assertion that fails in the 'Maybe' monad-assertMay :: Bool -> Maybe ()-assertMay = assertZ---- | A 'fromRight' that fails in the 'Maybe' monad-rightMay :: Either e a -> Maybe a-rightMay = rightZ---- | A 'tail' that fails in the 'Either' monad-tailErr :: e -> [a] -> Either e [a]-tailErr e = note e . S.tailMay---- | An 'init' that fails in the 'Either' monad-initErr :: e -> [a] -> Either e [a]-initErr e = note e . S.initMay---- | A 'head' that fails in the 'Either' monad-headErr :: e -> [a] -> Either e a-headErr e = note e . S.headMay---- | A 'last' that fails in the 'Either' monad-lastErr :: e -> [a] -> Either e a-lastErr e = note e . S.lastMay---- | A 'minimum' that fails in the 'Either' monad-minimumErr :: (Ord a) => e -> [a] -> Either e a-minimumErr e = note e . S.minimumMay---- | A 'maximum' that fails in the 'Either' monad-maximumErr :: (Ord a) => e -> [a] -> Either e a-maximumErr e = note e . S.maximumMay---- | A 'foldr1' that fails in the 'Either' monad-foldr1Err :: e -> (a -> a -> a) -> [a] -> Either e a-foldr1Err e step xs = note e $ S.foldr1May step xs---- | A 'foldl1' that fails in the 'Either' monad-foldl1Err :: e -> (a -> a -> a) -> [a] -> Either e a-foldl1Err e step xs = note e $ S.foldl1May step xs---- | A 'foldl1'' that fails in the 'Either' monad-foldl1Err' :: e -> (a -> a -> a) -> [a] -> Either e a-foldl1Err' e step xs = note e $ S.foldl1May' step xs---- | A ('!!') that fails in the 'Either' monad-atErr :: e -> [a] -> Int -> Either e a-atErr e xs n = note e $ S.atMay xs n---- | A 'read' that fails in the 'Either' monad-readErr :: (Read a) => e -> String -> Either e a-readErr e = note e . S.readMay---- | An assertion that fails in the 'Either' monad-assertErr :: e -> Bool -> Either e ()-assertErr e p = if p then Right () else Left e---- | A 'fromJust' that fails in the 'Either' monad-justErr :: e -> Maybe a -> Either e a-justErr e = maybe (Left e) Right---- | A 'tail' that fails in the 'EitherT' monad-tryTail :: (Monad m) => e -> [a] -> EitherT e m [a]-tryTail e xs = hoistEither $ tailErr e xs---- | An 'init' that fails in the 'EitherT' monad-tryInit :: (Monad m) => e -> [a] -> EitherT e m [a]-tryInit e xs = hoistEither $ initErr e xs---- | A 'head' that fails in the 'EitherT' monad-tryHead :: (Monad m) => e -> [a] -> EitherT e m a-tryHead e xs = hoistEither $ headErr e xs---- | A 'last' that fails in the 'EitherT' monad-tryLast :: (Monad m) => e -> [a] -> EitherT e m a-tryLast e xs = hoistEither $ lastErr e xs---- | A 'minimum' that fails in the 'EitherT' monad-tryMinimum :: (Monad m, Ord a) => e -> [a] -> EitherT e m a-tryMinimum e xs = hoistEither $ maximumErr e xs---- | A 'maximum' that fails in the 'EitherT' monad-tryMaximum :: (Monad m, Ord a) => e -> [a] -> EitherT e m a-tryMaximum e xs = hoistEither $ maximumErr e xs---- | A 'foldr1' that fails in the 'EitherT' monad-tryFoldr1 :: (Monad m) => e -> (a -> a -> a) -> [a] -> EitherT e m a-tryFoldr1 e step xs = hoistEither $ foldr1Err e step xs---- | A 'foldl1' that fails in the 'EitherT' monad-tryFoldl1 :: (Monad m) => e -> (a -> a -> a) -> [a] -> EitherT e m a-tryFoldl1 e step xs = hoistEither $ foldl1Err e step xs---- | A 'foldl1'' that fails in the 'EitherT' monad-tryFoldl1' :: (Monad m) => e -> (a -> a -> a) -> [a] -> EitherT e m a-tryFoldl1' e step xs = hoistEither $ foldl1Err' e step xs---- | A ('!!') that fails in the 'EitherT' monad-tryAt :: (Monad m) => e -> [a] -> Int -> EitherT e m a-tryAt e xs n = hoistEither $ atErr e xs n---- | A 'read' that fails in the 'EitherT' monad-tryRead :: (Monad m, Read a) => e -> String -> EitherT e m a-tryRead e str = hoistEither $ readErr e str---- | An assertion that fails in the 'EitherT' monad-tryAssert :: (Monad m) => e -> Bool -> EitherT e m ()-tryAssert e p = hoistEither $ assertErr e p---- | A 'fromJust' that fails in the 'EitherT' monad-tryJust :: (Monad m) => e -> Maybe a -> EitherT e m a-tryJust e m = hoistEither $ justErr e m---- | A 'fromRight' that fails in the 'EitherT' monad-tryRight :: (Monad m) => Either e a -> EitherT e m a-tryRight = hoistEither---- | A 'tail' that fails using 'mzero'-tailZ :: (MonadPlus m) => [a] -> m [a]-tailZ = maybe mzero return . S.tailMay---- | An 'init' that fails using 'mzero'-initZ :: (MonadPlus m) => [a] -> m [a]-initZ = maybe mzero return . S.initMay---- | A 'head' that fails using 'mzero'-headZ :: (MonadPlus m) => [a] -> m a-headZ = maybe mzero return . S.headMay---- | A 'last' that fails using 'mzero'-lastZ :: (MonadPlus m) => [a] -> m a-lastZ = maybe mzero return . S.lastMay---- | A 'minimum' that fails using 'mzero'-minimumZ :: (MonadPlus m) => (Ord a) => [a] -> m a-minimumZ = maybe mzero return . S.minimumMay---- | A 'maximum' that fails using 'mzero'-maximumZ :: (MonadPlus m) => (Ord a) => [a] -> m a-maximumZ = maybe mzero return . S.maximumMay---- | A 'foldr1' that fails using 'mzero'-foldr1Z :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a-foldr1Z step xs = maybe mzero return $ S.foldr1May step xs---- | A 'foldl1' that fails using 'mzero'-foldl1Z :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a-foldl1Z step xs = maybe mzero return $ S.foldl1May step xs---- | A 'foldl1'' that fails using 'mzero'-foldl1Z' :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a-foldl1Z' step xs = maybe mzero return $ S.foldl1May' step xs---- | A ('!!') that fails using 'mzero'-atZ :: (MonadPlus m) => [a] -> Int -> m a-atZ xs n = maybe mzero return $ S.atMay xs n---- | A 'read' that fails using 'mzero'-readZ :: (MonadPlus m) => (Read a) => String -> m a-readZ = maybe mzero return . S.readMay---- | An assertion that fails using 'mzero'-assertZ :: (MonadPlus m) => Bool -> m ()-assertZ p = if p then return () else mzero---- | A 'fromJust' that fails using 'mzero'-justZ :: (MonadPlus m) => Maybe a -> m a-justZ = maybe mzero return---- | A 'fromRight' that fails using 'mzero'-rightZ :: (MonadPlus m) => Either e a -> m a-rightZ = either (const mzero) return+{-| This module extends the @safe@ library's functions with corresponding + versions compatible with 'Either' and 'EitherT', and also provides a few + 'Maybe'-compatible functions missing from @safe@. + + I suffix the 'Either'-compatible functions with @Err@ and prefix the + 'EitherT'-compatible functions with @try@. + + Note that this library re-exports the 'Maybe' compatible functions from + @safe@ in the "Control.Error" module, so they are not provided here. + + The \'@Z@\'-suffixed functions generalize the 'Maybe' functions to also work + with anything that implements 'MonadPlus', including: + + * Lists + + * Most parsers + + * 'EitherT' (if the left value is a 'Monoid') +-} + +module Control.Error.Safe ( + -- * Maybe-compatible functions + assertMay, + rightMay, + + -- * Either-compatible functions + tailErr, + initErr, + headErr, + lastErr, + minimumErr, + maximumErr, + foldr1Err, + foldl1Err, + foldl1Err', + atErr, + readErr, + assertErr, + justErr, + + -- * EitherT-compatible functions + tryTail, + tryInit, + tryHead, + tryLast, + tryMinimum, + tryMaximum, + tryFoldr1, + tryFoldl1, + tryFoldl1', + tryAt, + tryRead, + tryAssert, + tryJust, + tryRight, + + -- * MonadPlus-compatible functions + tailZ, + initZ, + headZ, + lastZ, + minimumZ, + maximumZ, + foldr1Z, + foldl1Z, + foldl1Z', + atZ, + readZ, + assertZ, + justZ, + rightZ + ) where + +import Control.Error.Util (note) +import Control.Monad (MonadPlus(mzero)) +import Control.Monad.Trans.Either (EitherT, hoistEither) +import qualified Safe as S + +-- | An assertion that fails in the 'Maybe' monad +assertMay :: Bool -> Maybe () +assertMay = assertZ + +-- | A 'fromRight' that fails in the 'Maybe' monad +rightMay :: Either e a -> Maybe a +rightMay = rightZ + +-- | A 'tail' that fails in the 'Either' monad +tailErr :: e -> [a] -> Either e [a] +tailErr e = note e . S.tailMay + +-- | An 'init' that fails in the 'Either' monad +initErr :: e -> [a] -> Either e [a] +initErr e = note e . S.initMay + +-- | A 'head' that fails in the 'Either' monad +headErr :: e -> [a] -> Either e a +headErr e = note e . S.headMay + +-- | A 'last' that fails in the 'Either' monad +lastErr :: e -> [a] -> Either e a +lastErr e = note e . S.lastMay + +-- | A 'minimum' that fails in the 'Either' monad +minimumErr :: (Ord a) => e -> [a] -> Either e a +minimumErr e = note e . S.minimumMay + +-- | A 'maximum' that fails in the 'Either' monad +maximumErr :: (Ord a) => e -> [a] -> Either e a +maximumErr e = note e . S.maximumMay + +-- | A 'foldr1' that fails in the 'Either' monad +foldr1Err :: e -> (a -> a -> a) -> [a] -> Either e a +foldr1Err e step xs = note e $ S.foldr1May step xs + +-- | A 'foldl1' that fails in the 'Either' monad +foldl1Err :: e -> (a -> a -> a) -> [a] -> Either e a +foldl1Err e step xs = note e $ S.foldl1May step xs + +-- | A 'foldl1'' that fails in the 'Either' monad +foldl1Err' :: e -> (a -> a -> a) -> [a] -> Either e a +foldl1Err' e step xs = note e $ S.foldl1May' step xs + +-- | A ('!!') that fails in the 'Either' monad +atErr :: e -> [a] -> Int -> Either e a +atErr e xs n = note e $ S.atMay xs n + +-- | A 'read' that fails in the 'Either' monad +readErr :: (Read a) => e -> String -> Either e a +readErr e = note e . S.readMay + +-- | An assertion that fails in the 'Either' monad +assertErr :: e -> Bool -> Either e () +assertErr e p = if p then Right () else Left e + +-- | A 'fromJust' that fails in the 'Either' monad +justErr :: e -> Maybe a -> Either e a +justErr e = maybe (Left e) Right + +-- | A 'tail' that fails in the 'EitherT' monad +tryTail :: (Monad m) => e -> [a] -> EitherT e m [a] +tryTail e xs = hoistEither $ tailErr e xs + +-- | An 'init' that fails in the 'EitherT' monad +tryInit :: (Monad m) => e -> [a] -> EitherT e m [a] +tryInit e xs = hoistEither $ initErr e xs + +-- | A 'head' that fails in the 'EitherT' monad +tryHead :: (Monad m) => e -> [a] -> EitherT e m a +tryHead e xs = hoistEither $ headErr e xs + +-- | A 'last' that fails in the 'EitherT' monad +tryLast :: (Monad m) => e -> [a] -> EitherT e m a +tryLast e xs = hoistEither $ lastErr e xs + +-- | A 'minimum' that fails in the 'EitherT' monad +tryMinimum :: (Monad m, Ord a) => e -> [a] -> EitherT e m a +tryMinimum e xs = hoistEither $ maximumErr e xs + +-- | A 'maximum' that fails in the 'EitherT' monad +tryMaximum :: (Monad m, Ord a) => e -> [a] -> EitherT e m a +tryMaximum e xs = hoistEither $ maximumErr e xs + +-- | A 'foldr1' that fails in the 'EitherT' monad +tryFoldr1 :: (Monad m) => e -> (a -> a -> a) -> [a] -> EitherT e m a +tryFoldr1 e step xs = hoistEither $ foldr1Err e step xs + +-- | A 'foldl1' that fails in the 'EitherT' monad +tryFoldl1 :: (Monad m) => e -> (a -> a -> a) -> [a] -> EitherT e m a +tryFoldl1 e step xs = hoistEither $ foldl1Err e step xs + +-- | A 'foldl1'' that fails in the 'EitherT' monad +tryFoldl1' :: (Monad m) => e -> (a -> a -> a) -> [a] -> EitherT e m a +tryFoldl1' e step xs = hoistEither $ foldl1Err' e step xs + +-- | A ('!!') that fails in the 'EitherT' monad +tryAt :: (Monad m) => e -> [a] -> Int -> EitherT e m a +tryAt e xs n = hoistEither $ atErr e xs n + +-- | A 'read' that fails in the 'EitherT' monad +tryRead :: (Monad m, Read a) => e -> String -> EitherT e m a +tryRead e str = hoistEither $ readErr e str + +-- | An assertion that fails in the 'EitherT' monad +tryAssert :: (Monad m) => e -> Bool -> EitherT e m () +tryAssert e p = hoistEither $ assertErr e p + +-- | A 'fromJust' that fails in the 'EitherT' monad +tryJust :: (Monad m) => e -> Maybe a -> EitherT e m a +tryJust e m = hoistEither $ justErr e m + +-- | A 'fromRight' that fails in the 'EitherT' monad +tryRight :: (Monad m) => Either e a -> EitherT e m a +tryRight = hoistEither + +-- | A 'tail' that fails using 'mzero' +tailZ :: (MonadPlus m) => [a] -> m [a] +tailZ = maybe mzero return . S.tailMay + +-- | An 'init' that fails using 'mzero' +initZ :: (MonadPlus m) => [a] -> m [a] +initZ = maybe mzero return . S.initMay + +-- | A 'head' that fails using 'mzero' +headZ :: (MonadPlus m) => [a] -> m a +headZ = maybe mzero return . S.headMay + +-- | A 'last' that fails using 'mzero' +lastZ :: (MonadPlus m) => [a] -> m a +lastZ = maybe mzero return . S.lastMay + +-- | A 'minimum' that fails using 'mzero' +minimumZ :: (MonadPlus m) => (Ord a) => [a] -> m a +minimumZ = maybe mzero return . S.minimumMay + +-- | A 'maximum' that fails using 'mzero' +maximumZ :: (MonadPlus m) => (Ord a) => [a] -> m a +maximumZ = maybe mzero return . S.maximumMay + +-- | A 'foldr1' that fails using 'mzero' +foldr1Z :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a +foldr1Z step xs = maybe mzero return $ S.foldr1May step xs + +-- | A 'foldl1' that fails using 'mzero' +foldl1Z :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a +foldl1Z step xs = maybe mzero return $ S.foldl1May step xs + +-- | A 'foldl1'' that fails using 'mzero' +foldl1Z' :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a +foldl1Z' step xs = maybe mzero return $ S.foldl1May' step xs + +-- | A ('!!') that fails using 'mzero' +atZ :: (MonadPlus m) => [a] -> Int -> m a +atZ xs n = maybe mzero return $ S.atMay xs n + +-- | A 'read' that fails using 'mzero' +readZ :: (MonadPlus m) => (Read a) => String -> m a +readZ = maybe mzero return . S.readMay + +-- | An assertion that fails using 'mzero' +assertZ :: (MonadPlus m) => Bool -> m () +assertZ p = if p then return () else mzero + +-- | A 'fromJust' that fails using 'mzero' +justZ :: (MonadPlus m) => Maybe a -> m a +justZ = maybe mzero return + +-- | A 'fromRight' that fails using 'mzero' +rightZ :: (MonadPlus m) => Either e a -> m a +rightZ = either (const mzero) return
Control/Error/Script.hs view
@@ -1,58 +1,58 @@-{-|- Use this module if you like to write simple scripts with 'String'-based- errors, but you prefer to use 'EitherT' to handle errors rather than- @Control.Exception@.--> import Control.Error->-> main = runScript $ do-> str <- scriptIO getLine-> n <- tryRead "Read failed" str-> scriptIO $ print (n + 1)--}--module Control.Error.Script (- -- * The Script Monad- Script,- runScript,- scriptIO- ) where--import Control.Exception (try, SomeException)-import Control.Monad (liftM)-import Control.Monad.IO.Class (MonadIO(liftIO))-import Control.Monad.Trans.Either (EitherT(EitherT, runEitherT))-import Control.Error.Util (errLn)-import Data.EitherR (fmapL)-import System.Exit (exitFailure)---- Documentation-import Control.Monad.Trans.Class (lift)-import System.IO (stderr)---- | An 'IO' action that can fail with a 'String' error message-type Script = EitherT String IO--{-| Runs the 'Script' monad-- Prints the first error to 'stderr' and exits with 'exitFailure'--}-runScript :: Script a -> IO a-runScript s = do- e <- runEitherT s- case e of- Left e -> do- errLn e- exitFailure- Right a -> return a--{-| 'scriptIO' resembles 'lift', except it catches all exceptions and converts- them to 'String's.-- Note that 'scriptIO' is compatible with the 'Script' monad.--}-scriptIO :: (MonadIO m) => IO a -> EitherT String m a-scriptIO = EitherT- . liftIO- . liftM (fmapL show)- . (try :: IO a -> IO (Either SomeException a))+{-| + Use this module if you like to write simple scripts with 'String'-based + errors, but you prefer to use 'EitherT' to handle errors rather than + @Control.Exception@. + +> import Control.Error +> +> main = runScript $ do +> str <- scriptIO getLine +> n <- tryRead "Read failed" str +> scriptIO $ print (n + 1) +-} + +module Control.Error.Script ( + -- * The Script Monad + Script, + runScript, + scriptIO + ) where + +import Control.Exception (try, SomeException) +import Control.Monad (liftM) +import Control.Monad.IO.Class (MonadIO(liftIO)) +import Control.Monad.Trans.Either (EitherT(EitherT, runEitherT)) +import Control.Error.Util (errLn) +import Data.EitherR (fmapL) +import System.Exit (exitFailure) + +-- Documentation +import Control.Monad.Trans.Class (lift) +import System.IO (stderr) + +-- | An 'IO' action that can fail with a 'String' error message +type Script = EitherT String IO + +{-| Runs the 'Script' monad + + Prints the first error to 'stderr' and exits with 'exitFailure' +-} +runScript :: Script a -> IO a +runScript s = do + e <- runEitherT s + case e of + Left e -> do + errLn e + exitFailure + Right a -> return a + +{-| 'scriptIO' resembles 'lift', except it catches all exceptions and converts + them to 'String's. + + Note that 'scriptIO' is compatible with the 'Script' monad. +-} +scriptIO :: (MonadIO m) => IO a -> EitherT String m a +scriptIO = EitherT + . liftIO + . liftM (fmapL show) + . (try :: IO a -> IO (Either SomeException a))
Control/Error/Util.hs view
@@ -1,179 +1,195 @@--- | This module exports miscellaneous error-handling functions.--module Control.Error.Util (- -- * Conversion- -- $conversion- hush,- hushT,- note,- noteT,- hoistMaybe,- (??),- (!?),-- -- * MaybeT- maybeT,- just,- nothing,-- -- * Either- isLeft,- isRight,- fmapR,- AllE(..),- AnyE(..),-- -- * EitherT- fmapRT,-- -- * Error Reporting- err,- errLn,-- -- * Exceptions- tryIO,- syncIO - ) where--import Control.Applicative (Applicative, pure, (<$>))-import qualified Control.Exception as Ex-import Control.Monad (liftM)-import Control.Monad.IO.Class (MonadIO(liftIO))-import Control.Monad.Trans.Either (EitherT(EitherT, runEitherT))-import Control.Monad.Trans.Maybe (MaybeT(MaybeT, runMaybeT))-import Data.Dynamic (Dynamic)-import Data.Monoid (Monoid(mempty, mappend))-import System.Exit (ExitCode)-import System.IO (hPutStr, hPutStrLn, stderr)---- For Documentation-import Data.EitherR (fmapL, fmapLT)--{- $conversion- Use these functions to convert between 'Maybe', 'Either', 'MaybeT', and- 'EitherT'.-- Note that 'hoistEither' and 'eitherT' are provided by the @either@ package.--}--- | Suppress the 'Left' value of an 'Either'-hush :: Either a b -> Maybe b-hush = either (const Nothing) Just---- | Suppress the 'Left' value of an 'EitherT'-hushT :: (Monad m) => EitherT a m b -> MaybeT m b-hushT = MaybeT . liftM hush . runEitherT---- | Tag the 'Nothing' value of a 'Maybe'-note :: a -> Maybe b -> Either a b-note a = maybe (Left a) Right---- | Tag the 'Nothing' value of a 'MaybeT'-noteT :: (Monad m) => a -> MaybeT m b -> EitherT a m b-noteT a = EitherT . liftM (note a) . runMaybeT---- | Lift a 'Maybe' to the 'MaybeT' monad-hoistMaybe :: (Monad m) => Maybe b -> MaybeT m b-hoistMaybe = MaybeT . return---- | Convert a 'Maybe' value into the 'EitherT' monad-(??) :: Applicative m => Maybe a -> e -> EitherT e m a-(??) a e = EitherT (pure $ note e a)---- | Convert an applicative 'Maybe' value into the 'EitherT' monad-(!?) :: Applicative m => m (Maybe a) -> e -> EitherT e m a-(!?) a e = EitherT (note e <$> a)--{-| Case analysis for 'MaybeT'-- Use the first argument if the 'MaybeT' computation fails, otherwise apply- the function to the successful result.--}-maybeT :: Monad m => m b -> (a -> m b) -> MaybeT m a -> m b-maybeT mb kb (MaybeT ma) = ma >>= maybe mb kb---- | Analogous to 'Just' and equivalent to 'return'-just :: (Monad m) => a -> MaybeT m a-just a = MaybeT (return (Just a))---- | Analogous to 'Nothing' and equivalent to 'mzero'-nothing :: (Monad m) => MaybeT m a-nothing = MaybeT (return Nothing)---- | Returns whether argument is a 'Left'-isLeft :: Either a b -> Bool-isLeft = either (const True) (const False)---- | Returns whether argument is a 'Right'-isRight :: Either a b -> Bool-isRight = either (const False) (const True)---- | 'fmap' specialized to 'Either', given a name symmetric to 'fmapL'-fmapR :: (a -> b) -> Either l a -> Either l b-fmapR = fmap--{-| Run multiple 'Either' computations and succeed if all of them succeed-- 'mappend's all successes or failures--}-newtype AllE e r = AllE { runAllE :: Either e r }--instance (Monoid e, Monoid r) => Monoid (AllE e r) where- mempty = AllE (Right mempty)- mappend (AllE (Right x)) (AllE (Right y)) = AllE (Right (mappend x y))- mappend (AllE (Right _)) (AllE (Left y)) = AllE (Left y)- mappend (AllE (Left x)) (AllE (Right _)) = AllE (Left x)- mappend (AllE (Left x)) (AllE (Left y)) = AllE (Left (mappend x y))--{-| Run multiple 'Either' computations and succeed if any of them succeed-- 'mappend's all successes or failures--}-newtype AnyE e r = AnyE { runAnyE :: Either e r }--instance (Monoid e, Monoid r) => Monoid (AnyE e r) where- mempty = AnyE (Right mempty)- mappend (AnyE (Right x)) (AnyE (Right y)) = AnyE (Right (mappend x y))- mappend (AnyE (Right x)) (AnyE (Left _)) = AnyE (Right x)- mappend (AnyE (Left _)) (AnyE (Right y)) = AnyE (Right y)- mappend (AnyE (Left x)) (AnyE (Left y)) = AnyE (Left (mappend x y))---- | 'fmap' specialized to 'EitherT', given a name symmetric to 'fmapLT'-fmapRT :: (Monad m) => (a -> b) -> EitherT l m a -> EitherT l m b-fmapRT = liftM---- | Write a string to standard error-err :: String -> IO ()-err = hPutStr stderr---- | Write a string with a newline to standard error-errLn :: String -> IO ()-errLn = hPutStrLn stderr---- | Catch 'Ex.IOException's and convert them to the 'EitherT' monad-tryIO :: (MonadIO m) => IO a -> EitherT Ex.IOException m a-tryIO = EitherT . liftIO . Ex.try--{-| Catch all exceptions, except for asynchronous exceptions found in @base@- and convert them to the 'EitherT' monad--}-syncIO :: MonadIO m => IO a -> EitherT Ex.SomeException m a-syncIO a = EitherT . liftIO $ Ex.catches (Right <$> a)- [ Ex.Handler $ \e -> Ex.throw (e :: Ex.ArithException)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.ArrayException)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.AssertionFailed)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.AsyncException)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.BlockedIndefinitelyOnMVar)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.BlockedIndefinitelyOnSTM)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.Deadlock)- , Ex.Handler $ \e -> Ex.throw (e :: Dynamic)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.ErrorCall)- , Ex.Handler $ \e -> Ex.throw (e :: ExitCode)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.NestedAtomically)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.NoMethodError)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.NonTermination)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.PatternMatchFail)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.RecConError)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.RecSelError)- , Ex.Handler $ \e -> Ex.throw (e :: Ex.RecUpdError)- , Ex.Handler $ return . Left- ]+-- | This module exports miscellaneous error-handling functions. + +module Control.Error.Util ( + -- * Conversion + -- $conversion + hush, + hushT, + note, + noteT, + hoistMaybe, + (??), + (!?), + failWith, + failWithM, + + -- * MaybeT + maybeT, + just, + nothing, + + -- * Either + isLeft, + isRight, + fmapR, + AllE(..), + AnyE(..), + + -- * EitherT + fmapRT, + + -- * Error Reporting + err, + errLn, + + -- * Exceptions + tryIO, + syncIO + ) where + +import Control.Applicative (Applicative, pure, (<$>)) +import qualified Control.Exception as Ex +import Control.Monad (liftM) +import Control.Monad.IO.Class (MonadIO(liftIO)) +import Control.Monad.Trans.Either (EitherT(EitherT, runEitherT)) +import Control.Monad.Trans.Maybe (MaybeT(MaybeT, runMaybeT)) +import Data.Dynamic (Dynamic) +import Data.Monoid (Monoid(mempty, mappend)) +import System.Exit (ExitCode) +import System.IO (hPutStr, hPutStrLn, stderr) + +-- For Documentation +import Data.EitherR (fmapL, fmapLT) + +{- $conversion + Use these functions to convert between 'Maybe', 'Either', 'MaybeT', and + 'EitherT'. + + Note that 'hoistEither' and 'eitherT' are provided by the @either@ package. +-} +-- | Suppress the 'Left' value of an 'Either' +hush :: Either a b -> Maybe b +hush = either (const Nothing) Just + +-- | Suppress the 'Left' value of an 'EitherT' +hushT :: (Monad m) => EitherT a m b -> MaybeT m b +hushT = MaybeT . liftM hush . runEitherT + +-- | Tag the 'Nothing' value of a 'Maybe' +note :: a -> Maybe b -> Either a b +note a = maybe (Left a) Right + +-- | Tag the 'Nothing' value of a 'MaybeT' +noteT :: (Monad m) => a -> MaybeT m b -> EitherT a m b +noteT a = EitherT . liftM (note a) . runMaybeT + +-- | Lift a 'Maybe' to the 'MaybeT' monad +hoistMaybe :: (Monad m) => Maybe b -> MaybeT m b +hoistMaybe = MaybeT . return + +-- | Convert a 'Maybe' value into the 'EitherT' monad +(??) :: Applicative m => Maybe a -> e -> EitherT e m a +(??) a e = EitherT (pure $ note e a) + +-- | Convert an applicative 'Maybe' value into the 'EitherT' monad +(!?) :: Applicative m => m (Maybe a) -> e -> EitherT e m a +(!?) a e = EitherT (note e <$> a) + +{-| Convert a 'Maybe' value into the 'EitherT' monad + + Named version of ('??') with arguments flipped +-} +failWith :: Applicative m => e -> Maybe a -> EitherT e m a +failWith e a = a ?? e + +{- | Convert an applicative 'Maybe' value into the 'EitherT' monad + + Named version of ('!?') with arguments flipped +-} +failWithM :: Applicative m => e -> m (Maybe a) -> EitherT e m a +failWithM e a = a !? e + +{-| Case analysis for 'MaybeT' + + Use the first argument if the 'MaybeT' computation fails, otherwise apply + the function to the successful result. +-} +maybeT :: Monad m => m b -> (a -> m b) -> MaybeT m a -> m b +maybeT mb kb (MaybeT ma) = ma >>= maybe mb kb + +-- | Analogous to 'Just' and equivalent to 'return' +just :: (Monad m) => a -> MaybeT m a +just a = MaybeT (return (Just a)) + +-- | Analogous to 'Nothing' and equivalent to 'mzero' +nothing :: (Monad m) => MaybeT m a +nothing = MaybeT (return Nothing) + +-- | Returns whether argument is a 'Left' +isLeft :: Either a b -> Bool +isLeft = either (const True) (const False) + +-- | Returns whether argument is a 'Right' +isRight :: Either a b -> Bool +isRight = either (const False) (const True) + +-- | 'fmap' specialized to 'Either', given a name symmetric to 'fmapL' +fmapR :: (a -> b) -> Either l a -> Either l b +fmapR = fmap + +{-| Run multiple 'Either' computations and succeed if all of them succeed + + 'mappend's all successes or failures +-} +newtype AllE e r = AllE { runAllE :: Either e r } + +instance (Monoid e, Monoid r) => Monoid (AllE e r) where + mempty = AllE (Right mempty) + mappend (AllE (Right x)) (AllE (Right y)) = AllE (Right (mappend x y)) + mappend (AllE (Right _)) (AllE (Left y)) = AllE (Left y) + mappend (AllE (Left x)) (AllE (Right _)) = AllE (Left x) + mappend (AllE (Left x)) (AllE (Left y)) = AllE (Left (mappend x y)) + +{-| Run multiple 'Either' computations and succeed if any of them succeed + + 'mappend's all successes or failures +-} +newtype AnyE e r = AnyE { runAnyE :: Either e r } + +instance (Monoid e, Monoid r) => Monoid (AnyE e r) where + mempty = AnyE (Right mempty) + mappend (AnyE (Right x)) (AnyE (Right y)) = AnyE (Right (mappend x y)) + mappend (AnyE (Right x)) (AnyE (Left _)) = AnyE (Right x) + mappend (AnyE (Left _)) (AnyE (Right y)) = AnyE (Right y) + mappend (AnyE (Left x)) (AnyE (Left y)) = AnyE (Left (mappend x y)) + +-- | 'fmap' specialized to 'EitherT', given a name symmetric to 'fmapLT' +fmapRT :: (Monad m) => (a -> b) -> EitherT l m a -> EitherT l m b +fmapRT = liftM + +-- | Write a string to standard error +err :: String -> IO () +err = hPutStr stderr + +-- | Write a string with a newline to standard error +errLn :: String -> IO () +errLn = hPutStrLn stderr + +-- | Catch 'Ex.IOException's and convert them to the 'EitherT' monad +tryIO :: (MonadIO m) => IO a -> EitherT Ex.IOException m a +tryIO = EitherT . liftIO . Ex.try + +{-| Catch all exceptions, except for asynchronous exceptions found in @base@ + and convert them to the 'EitherT' monad +-} +syncIO :: MonadIO m => IO a -> EitherT Ex.SomeException m a +syncIO a = EitherT . liftIO $ Ex.catches (Right <$> a) + [ Ex.Handler $ \e -> Ex.throw (e :: Ex.ArithException) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.ArrayException) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.AssertionFailed) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.AsyncException) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.BlockedIndefinitelyOnMVar) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.BlockedIndefinitelyOnSTM) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.Deadlock) + , Ex.Handler $ \e -> Ex.throw (e :: Dynamic) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.ErrorCall) + , Ex.Handler $ \e -> Ex.throw (e :: ExitCode) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.NestedAtomically) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.NoMethodError) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.NonTermination) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.PatternMatchFail) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.RecConError) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.RecSelError) + , Ex.Handler $ \e -> Ex.throw (e :: Ex.RecUpdError) + , Ex.Handler $ return . Left + ]
Data/EitherR.hs view
@@ -1,193 +1,193 @@-{-| This module provides 'throwE' and 'catchE' for 'Either'. These two- functions reside here because 'throwE' and 'catchE' correspond to 'return'- and ('>>=') for the flipped 'Either' monad: 'EitherR'. Similarly, this- module defines 'throwT' and 'catchT' for 'EitherT', which correspond to the- 'Monad' operations for 'EitherRT'.-- These throw and catch functions improve upon @MonadError@ because:-- * 'catch' is more general and allows you to change the left value's type-- * They are Haskell98-- More advanced users can use 'EitherR' and 'EitherRT' to program in an- entirely symmetric \"success monad\" where exceptional results are the norm- and successful results terminate the computation. This allows you to chain- error-handlers using @do@ notation and pass around exceptional values of- varying types until you can finally recover from the error:--> runEitherRT $ do-> e2 <- ioExceptionHandler e1-> bool <- arithmeticExceptionhandler e2-> when bool $ lift $ putStrLn "DEBUG: Arithmetic handler did something"-- If any of the above error handlers 'succeed', no other handlers are tried.-- If you choose not to typefully distinguish between the error and sucess- monad, then use 'flipE' and 'flipET', which swap the type variables without- changing the type.--}--module Data.EitherR (- -- * EitherR- EitherR(..),-- -- ** Operations in the EitherR monad- succeed,-- -- ** Conversions to the Either monad- throwE,- catchE,- handleE,- fmapL,-- -- ** Flip alternative- flipE,-- -- * EitherRT- EitherRT(..),-- -- ** Operations in the EitherRT monad- succeedT,-- -- ** Conversions to the EitherT monad- throwT,- catchT,- handleT,- fmapLT,-- -- ** Flip alternative- flipET,- ) where--import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))-import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))-import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.Monad.IO.Class (MonadIO(liftIO))-import Control.Monad.Trans.Either (EitherT(EitherT, runEitherT), left, right)-import Data.Monoid (Monoid(mempty, mappend))--{-| If \"@Either e r@\" is the error monad, then \"@EitherR r e@\" is the- corresponding success monad, where:-- * 'return' is 'throwE'.-- * ('>>=') is 'catchE'.-- * Successful results abort the computation--}-newtype EitherR r e = EitherR { runEitherR :: Either e r }--instance Functor (EitherR r) where- fmap = liftM--instance Applicative (EitherR r) where- pure = return- (<*>) = ap--instance Monad (EitherR r) where- return e = EitherR (Left e)- EitherR m >>= f = case m of- Left e -> f e- Right r -> EitherR (Right r)--instance (Monoid r) => Alternative (EitherR r) where- empty = EitherR (Right mempty)- e1@(EitherR (Left _)) <|> _ = e1- _ <|> e2@(EitherR (Left _)) = e2- EitherR (Right r1) <|> EitherR (Right r2)- = EitherR (Right (mappend r1 r2))--instance (Monoid r) => MonadPlus (EitherR r) where- mzero = empty- mplus = (<|>)---- | Complete error handling, returning a result-succeed :: r -> EitherR r e-succeed r = EitherR (return r)---- | 'throwE' in the error monad corresponds to 'return' in the success monad-throwE :: e -> Either e r-throwE e = runEitherR (return e)---- | 'catchE' in the error monad corresponds to ('>>=') in the success monad-catchE :: Either a r -> (a -> Either b r) -> Either b r-e `catchE` f = runEitherR $ EitherR e >>= \a -> EitherR (f a)---- | 'catchE' with the arguments flipped-handleE :: (a -> Either b r) -> Either a r -> Either b r-handleE = flip catchE---- | Map a function over the 'Left' value of an 'Either'-fmapL :: (a -> b) -> Either a r -> Either b r-fmapL f = runEitherR . fmap f . EitherR---- | Flip the type variables of 'Either'-flipE :: Either a b -> Either b a-flipE e = case e of- Left a -> Right a- Right b -> Left b---- | 'EitherR' converted into a monad transformer-newtype EitherRT r m e = EitherRT { runEitherRT :: EitherT e m r }--instance (Monad m) => Functor (EitherRT r m) where- fmap = liftM--instance (Monad m) => Applicative (EitherRT r m) where- pure = return- (<*>) = ap--instance (Monad m) => Monad (EitherRT r m) where- return e = EitherRT (left e)- m >>= f = EitherRT $ EitherT $ do- x <- runEitherT $ runEitherRT m- runEitherT $ runEitherRT $ case x of- Left e -> f e- Right r -> EitherRT (right r)--instance (Monad m, Monoid r) => Alternative (EitherRT r m) where- empty = EitherRT $ EitherT $ return $ Right mempty- e1 <|> e2 = EitherRT $ EitherT $ do- x1 <- runEitherT $ runEitherRT e1- case x1 of- Left l -> return (Left l)- Right r1 -> do- x2 <- runEitherT $ runEitherRT e2- case x2 of- Left l -> return (Left l)- Right r2 -> return (Right (mappend r1 r2))--instance (Monad m, Monoid r) => MonadPlus (EitherRT r m) where- mzero = empty- mplus = (<|>)--instance MonadTrans (EitherRT r) where- lift = EitherRT . EitherT . liftM Left--instance (MonadIO m) => MonadIO (EitherRT r m) where- liftIO = lift . liftIO---- | Complete error handling, returning a result-succeedT :: (Monad m) => r -> EitherRT r m e-succeedT r = EitherRT (return r)---- | 'throwT' in the error monad corresponds to 'return' in the success monad-throwT :: (Monad m) => e -> EitherT e m r-throwT e = runEitherRT (return e)---- | 'catchT' in the error monad corresponds to ('>>=') in the success monad-catchT :: (Monad m) => EitherT a m r -> (a -> EitherT b m r) -> EitherT b m r-e `catchT` f = runEitherRT $ EitherRT e >>= \a -> EitherRT (f a)---- | 'catchT' with the arguments flipped-handleT :: (Monad m) => (a -> EitherT b m r) -> EitherT a m r -> EitherT b m r-handleT = flip catchT---- | Map a function over the 'Left' value of an 'EitherT'-fmapLT :: (Monad m) => (a -> b) -> EitherT a m r -> EitherT b m r-fmapLT f = runEitherRT . fmap f . EitherRT---- | Flip the type variables of an 'EitherT'-flipET :: (Monad m) => EitherT a m b -> EitherT b m a-flipET = EitherT . liftM flipE . runEitherT+{-| This module provides 'throwE' and 'catchE' for 'Either'. These two + functions reside here because 'throwE' and 'catchE' correspond to 'return' + and ('>>=') for the flipped 'Either' monad: 'EitherR'. Similarly, this + module defines 'throwT' and 'catchT' for 'EitherT', which correspond to the + 'Monad' operations for 'EitherRT'. + + These throw and catch functions improve upon @MonadError@ because: + + * 'catch' is more general and allows you to change the left value's type + + * They are Haskell98 + + More advanced users can use 'EitherR' and 'EitherRT' to program in an + entirely symmetric \"success monad\" where exceptional results are the norm + and successful results terminate the computation. This allows you to chain + error-handlers using @do@ notation and pass around exceptional values of + varying types until you can finally recover from the error: + +> runEitherRT $ do +> e2 <- ioExceptionHandler e1 +> bool <- arithmeticExceptionhandler e2 +> when bool $ lift $ putStrLn "DEBUG: Arithmetic handler did something" + + If any of the above error handlers 'succeed', no other handlers are tried. + + If you choose not to typefully distinguish between the error and sucess + monad, then use 'flipE' and 'flipET', which swap the type variables without + changing the type. +-} + +module Data.EitherR ( + -- * EitherR + EitherR(..), + + -- ** Operations in the EitherR monad + succeed, + + -- ** Conversions to the Either monad + throwE, + catchE, + handleE, + fmapL, + + -- ** Flip alternative + flipE, + + -- * EitherRT + EitherRT(..), + + -- ** Operations in the EitherRT monad + succeedT, + + -- ** Conversions to the EitherT monad + throwT, + catchT, + handleT, + fmapLT, + + -- ** Flip alternative + flipET, + ) where + +import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>))) +import Control.Monad (liftM, ap, MonadPlus(mzero, mplus)) +import Control.Monad.Trans.Class (MonadTrans(lift)) +import Control.Monad.IO.Class (MonadIO(liftIO)) +import Control.Monad.Trans.Either (EitherT(EitherT, runEitherT), left, right) +import Data.Monoid (Monoid(mempty, mappend)) + +{-| If \"@Either e r@\" is the error monad, then \"@EitherR r e@\" is the + corresponding success monad, where: + + * 'return' is 'throwE'. + + * ('>>=') is 'catchE'. + + * Successful results abort the computation +-} +newtype EitherR r e = EitherR { runEitherR :: Either e r } + +instance Functor (EitherR r) where + fmap = liftM + +instance Applicative (EitherR r) where + pure = return + (<*>) = ap + +instance Monad (EitherR r) where + return e = EitherR (Left e) + EitherR m >>= f = case m of + Left e -> f e + Right r -> EitherR (Right r) + +instance (Monoid r) => Alternative (EitherR r) where + empty = EitherR (Right mempty) + e1@(EitherR (Left _)) <|> _ = e1 + _ <|> e2@(EitherR (Left _)) = e2 + EitherR (Right r1) <|> EitherR (Right r2) + = EitherR (Right (mappend r1 r2)) + +instance (Monoid r) => MonadPlus (EitherR r) where + mzero = empty + mplus = (<|>) + +-- | Complete error handling, returning a result +succeed :: r -> EitherR r e +succeed r = EitherR (return r) + +-- | 'throwE' in the error monad corresponds to 'return' in the success monad +throwE :: e -> Either e r +throwE e = runEitherR (return e) + +-- | 'catchE' in the error monad corresponds to ('>>=') in the success monad +catchE :: Either a r -> (a -> Either b r) -> Either b r +e `catchE` f = runEitherR $ EitherR e >>= \a -> EitherR (f a) + +-- | 'catchE' with the arguments flipped +handleE :: (a -> Either b r) -> Either a r -> Either b r +handleE = flip catchE + +-- | Map a function over the 'Left' value of an 'Either' +fmapL :: (a -> b) -> Either a r -> Either b r +fmapL f = runEitherR . fmap f . EitherR + +-- | Flip the type variables of 'Either' +flipE :: Either a b -> Either b a +flipE e = case e of + Left a -> Right a + Right b -> Left b + +-- | 'EitherR' converted into a monad transformer +newtype EitherRT r m e = EitherRT { runEitherRT :: EitherT e m r } + +instance (Monad m) => Functor (EitherRT r m) where + fmap = liftM + +instance (Monad m) => Applicative (EitherRT r m) where + pure = return + (<*>) = ap + +instance (Monad m) => Monad (EitherRT r m) where + return e = EitherRT (left e) + m >>= f = EitherRT $ EitherT $ do + x <- runEitherT $ runEitherRT m + runEitherT $ runEitherRT $ case x of + Left e -> f e + Right r -> EitherRT (right r) + +instance (Monad m, Monoid r) => Alternative (EitherRT r m) where + empty = EitherRT $ EitherT $ return $ Right mempty + e1 <|> e2 = EitherRT $ EitherT $ do + x1 <- runEitherT $ runEitherRT e1 + case x1 of + Left l -> return (Left l) + Right r1 -> do + x2 <- runEitherT $ runEitherRT e2 + case x2 of + Left l -> return (Left l) + Right r2 -> return (Right (mappend r1 r2)) + +instance (Monad m, Monoid r) => MonadPlus (EitherRT r m) where + mzero = empty + mplus = (<|>) + +instance MonadTrans (EitherRT r) where + lift = EitherRT . EitherT . liftM Left + +instance (MonadIO m) => MonadIO (EitherRT r m) where + liftIO = lift . liftIO + +-- | Complete error handling, returning a result +succeedT :: (Monad m) => r -> EitherRT r m e +succeedT r = EitherRT (return r) + +-- | 'throwT' in the error monad corresponds to 'return' in the success monad +throwT :: (Monad m) => e -> EitherT e m r +throwT e = runEitherRT (return e) + +-- | 'catchT' in the error monad corresponds to ('>>=') in the success monad +catchT :: (Monad m) => EitherT a m r -> (a -> EitherT b m r) -> EitherT b m r +e `catchT` f = runEitherRT $ EitherRT e >>= \a -> EitherRT (f a) + +-- | 'catchT' with the arguments flipped +handleT :: (Monad m) => (a -> EitherT b m r) -> EitherT a m r -> EitherT b m r +handleT = flip catchT + +-- | Map a function over the 'Left' value of an 'EitherT' +fmapLT :: (Monad m) => (a -> b) -> EitherT a m r -> EitherT b m r +fmapLT f = runEitherRT . fmap f . EitherRT + +-- | Flip the type variables of an 'EitherT' +flipET :: (Monad m) => EitherT a m b -> EitherT b m a +flipET = EitherT . liftM flipE . runEitherT
LICENSE view
@@ -1,24 +1,24 @@-Copyright (c) 2012, 2013, Gabriel Gonzalez-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.- * Neither the name of Gabriel Gonzalez nor the names of other contributors- may be used to endorse or promote products derived from this software- without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 OWNER OR CONTRIBUTORS 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.+Copyright (c) 2012, 2013, Gabriel Gonzalez +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. + * Neither the name of Gabriel Gonzalez nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 OWNER OR CONTRIBUTORS 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.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
errors.cabal view
@@ -1,35 +1,35 @@-Name: errors-Version: 1.4.3-Cabal-Version: >=1.8.0.2-Build-Type: Simple-License: BSD3-License-File: LICENSE-Copyright: 2012, 2013 Gabriel Gonzalez-Author: Gabriel Gonzalez-Maintainer: Gabriel439@gmail.com-Bug-Reports: https://github.com/Gabriel439/Haskell-Errors-Library/issues-Synopsis: Simplified error-handling-Description:- The one-stop shop for all your error-handling needs! Just import- "Control.Error".- .- This library encourages an error-handling style that directly uses the type- system, rather than out-of-band exceptions.-Category: Control, Error Handling-Source-Repository head- Type: git- Location: https://github.com/Gabriel439/Haskell-Errors-Library--Library- Build-Depends:- base >= 4 && < 5 ,- either >= 3.1 && < 4.1,- safe >= 0.3.3 && < 0.4,- transformers >= 0.2 && < 0.4- Exposed-Modules:- Control.Error,- Control.Error.Safe,- Control.Error.Script,- Control.Error.Util,- Data.EitherR- GHC-Options: -O2+Name: errors +Version: 1.4.4 +Cabal-Version: >=1.8.0.2 +Build-Type: Simple +License: BSD3 +License-File: LICENSE +Copyright: 2012, 2013 Gabriel Gonzalez +Author: Gabriel Gonzalez +Maintainer: Gabriel439@gmail.com +Bug-Reports: https://github.com/Gabriel439/Haskell-Errors-Library/issues +Synopsis: Simplified error-handling +Description: + The one-stop shop for all your error-handling needs! Just import + "Control.Error". + . + This library encourages an error-handling style that directly uses the type + system, rather than out-of-band exceptions. +Category: Control, Error Handling +Source-Repository head + Type: git + Location: https://github.com/Gabriel439/Haskell-Errors-Library + +Library + Build-Depends: + base >= 4 && < 5 , + either >= 3.1 && < 4.1, + safe >= 0.3.3 && < 0.4, + transformers >= 0.2 && < 0.4 + Exposed-Modules: + Control.Error, + Control.Error.Safe, + Control.Error.Script, + Control.Error.Util, + Data.EitherR + GHC-Options: -O2