{- | Copyright : 2010 Aristid Breitkreuz License : BSD3 Stability : experimental Portability : portable Short-circuit evaluation. -} module Control.Shortcircuit ( -- * Type classes HasFalse(..) , HasTrue(..) , Shortcircuit(..) -- * Short circuit tests , isFalse , if' , unless' , (??) , (||) , (&&) , firstTrueOf , lastFalseOf -- * Monadic short circuits , orM , andM , firstTrueOfM , lastFalseOfM ) where import Control.Monad import Control.Monad.Instances () import Data.Maybe import Prelude hiding ((||), (&&)) -- | Types with a defined false value. class HasFalse a where -- | A false value for this type. -- If 'Shortcircuit' @a@ holds, @isTrue false == False@ must hold. false :: a -- | Types with a defined true value. class HasTrue a where -- | A true value for this type. -- If 'Shortcircuit' @a@ holds, @isTrue true == True@ must hold. true :: a -- | Types that support short circuits. class Shortcircuit a where -- | Whether the value is true-like (i.e. not false-like). isTrue :: a -> Bool -- | Whether the value is false-like (i.e. not true-like). isFalse :: (Shortcircuit a) => a -> Bool isFalse = not . isTrue -- | @if then else@ generalised to 'Shortcircuit'. if' :: (Shortcircuit a) => a -> b -> b -> b if' x a b | isTrue x = a | otherwise = b -- | The opposite of 'if''. unless' :: (Shortcircuit a) => a -> b -> b -> b unless' x a b | isFalse x = a | otherwise = b -- | Like 'if'', but with different argument order, allowing infix use. (??) :: (Shortcircuit a) => b -> b -> a -> b a ?? b = \x -> if' x a b -- | 'Prelude.||' generalised to 'Shortcircuit'. (||) :: (Shortcircuit a) => a -> a -> a (||) = join if' -- | 'Prelude.&&' generalised to 'Shortcircuit'. (&&) :: (Shortcircuit a) => a -> a -> a (&&) = join unless' -- | Returns the first true-ish value from a list, or 'false'. firstTrueOf :: (Shortcircuit a, HasFalse a) => [a] -> a firstTrueOf = foldr (||) false -- | Returns the last false-ish value from a list, or 'true'. lastFalseOf :: (Shortcircuit a, HasTrue a) => [a] -> a lastFalseOf = foldr (&&) true -- | Short-circuit two actions, performing the second only if the first returned a false-ish value. orM :: (Monad m, Shortcircuit a) => m a -> m a -> m a orM a b = a >>= \x -> (return x ?? b) x -- | Short-circuit two actions, performing the second only if the first returned a true-ish value. andM :: (Monad m, Shortcircuit a) => m a -> m a -> m a andM a b = a >>= \x -> (b ?? return x) x -- | Short-circuit a list of actions, performing only until a true-ish value is found, or the list exhausted. firstTrueOfM :: (Monad m, Shortcircuit a, HasFalse a) => [m a] -> m a firstTrueOfM = foldr orM (return false) -- | Short-circuit a list of actions, performing only until a false-ish value is found, or the list exhausted. lastFalseOfM :: (Monad m, Shortcircuit a, HasTrue a) => [m a] -> m a lastFalseOfM = foldr andM (return true) instance HasTrue Bool where true = True instance HasFalse Bool where false = False instance Shortcircuit Bool where isTrue = (== True) instance HasFalse (Maybe a) where false = Nothing instance Shortcircuit (Maybe a) where isTrue = isJust instance Shortcircuit (Either a b) where isTrue (Left _) = False isTrue (Right _) = True