{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts, FlexibleInstances #-} {-# LANGUAGE DeriveDataTypeable #-} {- | Module : Safe.Fail Copyright : (c) Neil Mitchell 2007-2008, Jose Iborra 2009 License : BSD3 Maintainer : pepeiborra@gmail.com Stability : in-progress Portability : portable A library for safe functions, based on standard functions that may crash. This module reexports versions which produce exceptions in an arbitrary 'Failure'. -} module Safe.Failure ( -- * List Functions Safe.Failure.head, Safe.Failure.tail, Safe.Failure.init, Safe.Failure.last, Safe.Failure.minimum, Safe.Failure.maximum, Safe.Failure.foldr1, Safe.Failure.foldl1, Safe.Failure.at, Safe.Failure.lookup, -- * Maybe functions Safe.Failure.fromJust, -- * Other Prelude functions Safe.Failure.read, -- * Useful combinators def, note, -- * Assertions Safe.Failure.assert, -- * Exceptions SafeException(..), HeadFailure(..), TailFailure(..), InitFailure(..), LastFailure(..), MaximumFailure(..), MinimumFailure(..), Foldl1Failure(..), Foldr1Failure(..), IndexFailure(..), LookupFailure(..), FromJustFailure(..), ReadFailure(..), ) where import Control.Exception import Control.Failure import Data.Maybe import Data.Typeable {-| @def@, use it to return a default value in the event of an error. E.g. you can define a version of @tail@ which returns a default value when the list is empty > tailDef defaultValue = def defaultValue . tail -} def :: a -> Maybe a -> a def v = fromMaybe v {-| @note@, use it to fail with an annotated runtime error -} note :: Exception e => String -> Either e a -> a note msg = either (\e -> error (show e ++ ": " ++ msg)) id data SafeException = forall e. Exception e => SafeException e deriving (Typeable) instance Show SafeException where showsPrec p (SafeException e) = ("Safe Exception:" ++) . showsPrec p e instance Exception SafeException safeExceptionToException :: Exception e => e -> SomeException safeExceptionToException = toException . SafeException safeExceptionFromException :: Exception e => SomeException -> Maybe e safeExceptionFromException e = do { SafeException e' <- fromException e; cast e'} liftFailure :: Failure e m => (a -> Bool) -> e -> (a -> b) -> a -> m b liftFailure test e f val = if test val then failure e else return (f val) data TailFailure = TailFailure deriving (Show,Typeable) instance Exception TailFailure where fromException = safeExceptionFromException toException = safeExceptionToException tail :: Failure TailFailure m => [a] -> m [a] tail = liftFailure null TailFailure Prelude.tail data InitFailure = InitFailure deriving (Show,Typeable) instance Exception InitFailure where fromException = safeExceptionFromException toException = safeExceptionToException init :: Failure InitFailure m => [a] -> m [a] init = liftFailure null InitFailure Prelude.init data HeadFailure = HeadFailure deriving (Show,Typeable) instance Exception HeadFailure where fromException = safeExceptionFromException toException = safeExceptionToException head :: Failure HeadFailure m => [a] -> m a head = liftFailure null HeadFailure Prelude.head data LastFailure = LastFailure deriving (Show,Typeable) instance Exception LastFailure where fromException = safeExceptionFromException toException = safeExceptionToException last :: Failure LastFailure m => [a] -> m a last = liftFailure null LastFailure Prelude.last data MinimumFailure = MinimumFailure deriving (Show,Typeable) instance Exception MinimumFailure where fromException = safeExceptionFromException toException = safeExceptionToException minimum :: (Ord a, Failure MinimumFailure m) => [a] -> m a minimum = liftFailure null MinimumFailure Prelude.minimum data MaximumFailure = MaximumFailure deriving (Show,Typeable) instance Exception MaximumFailure where fromException = safeExceptionFromException toException = safeExceptionToException maximum :: (Ord a, Failure MaximumFailure m) => [a] -> m a maximum = liftFailure null MaximumFailure Prelude.maximum data Foldr1Failure = Foldr1Failure deriving (Show,Typeable) instance Exception Foldr1Failure where fromException = safeExceptionFromException toException = safeExceptionToException foldr1 :: Failure Foldr1Failure m => (a -> a -> a) -> [a] -> m a foldr1 f = liftFailure null Foldr1Failure (Prelude.foldr1 f) data Foldl1Failure = Foldl1Failure deriving (Show,Typeable) instance Exception Foldl1Failure where fromException = safeExceptionFromException toException = safeExceptionToException foldl1 :: Failure Foldl1Failure m => (a -> a -> a) -> [a] -> m a foldl1 f = liftFailure null Foldl1Failure (Prelude.foldl1 f) data FromJustFailure = FromJustFailure deriving (Show,Typeable) instance Exception FromJustFailure where fromException = safeExceptionFromException toException = safeExceptionToException fromJust :: Failure FromJustFailure m => Maybe a -> m a fromJust = liftFailure isNothing FromJustFailure Data.Maybe.fromJust data IndexFailure = IndexFailure Int deriving (Show,Typeable) instance Exception IndexFailure where fromException = safeExceptionFromException toException = safeExceptionToException at :: Failure IndexFailure m => [a] -> Int -> m a at xs n | n < 0 = failure (IndexFailure n) | otherwise = go xs n where go [] _ = failure (IndexFailure n) go (x:_) 0 = return x go (_:xx) i = go xx (i-1) data ReadFailure = ReadFailure String deriving (Show,Typeable) instance Exception ReadFailure where fromException = safeExceptionFromException toException = safeExceptionToException read :: (Read a, Failure ReadFailure m) => String -> m a read s = case [x | (x,t) <- reads s, ("","") <- lex t] of [x] -> return x _ -> failure (ReadFailure s) data LookupFailure a = LookupFailure a deriving (Show,Typeable) instance (Typeable a, Show a) => Exception (LookupFailure a) where fromException = safeExceptionFromException toException = safeExceptionToException -- | -- > lookupJust key = fromJust . lookup key lookup :: (Eq a, Failure (LookupFailure a) m) => a -> [(a,b)] -> m b lookup key = maybe (failure (LookupFailure key)) return . Prelude.lookup key -- | Assert a value to be true. If true, returns the first value as a succss. -- Otherwise, returns the second value as a failure. assert :: (Failure e m, Exception e) => Bool -> v -> e -> m v assert b v e = if b then return v else failure e