cond 0.2 → 0.3
raw patch · 3 files changed
+215/−54 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Control.Conditional: (<<|) :: Monad m => m a -> m (Maybe a) -> m a
+ Control.Conditional: (|>>) :: (ToBool b, Monad m) => m b -> m a -> m (Maybe a)
+ Control.Conditional: class ToBool b
+ Control.Conditional: guard :: (ToBool b, MonadPlus m) => b -> m ()
+ Control.Conditional: instance ToBool (Dual Bool)
+ Control.Conditional: instance ToBool All
+ Control.Conditional: instance ToBool Any
+ Control.Conditional: instance ToBool Bool
+ Control.Conditional: toBool :: ToBool b => b -> Bool
+ Control.Conditional: unless :: (Boolean b, ToBool b, MonadPlus m) => b -> m () -> m ()
+ Control.Conditional: when :: (ToBool b, MonadPlus m) => b -> m () -> m ()
+ Control.Conditional: xorM :: (Boolean b, Monad m) => m b -> m b -> m b
+ Data.Algebra.Boolean: (&&) :: Boolean b => b -> b -> b
+ Data.Algebra.Boolean: (-->) :: Boolean b => b -> b -> b
+ Data.Algebra.Boolean: (<-->) :: Boolean b => b -> b -> b
+ Data.Algebra.Boolean: (||) :: Boolean b => b -> b -> b
+ Data.Algebra.Boolean: class Boolean b where not = (<--> false) x && y = not (x || y) x || y = not (x && y) x xor y = (x || y) && (not (x && y)) x --> y = not x || y x <--> y = (x && y) || not (x || y)
+ Data.Algebra.Boolean: false :: Boolean b => b
+ Data.Algebra.Boolean: fromBool :: Boolean b => Bool -> b
+ Data.Algebra.Boolean: instance Boolean (Dual Bool)
+ Data.Algebra.Boolean: instance Boolean (Endo Bool)
+ Data.Algebra.Boolean: instance Boolean All
+ Data.Algebra.Boolean: instance Boolean Any
+ Data.Algebra.Boolean: instance Boolean Bool
+ Data.Algebra.Boolean: not :: Boolean b => b -> b
+ Data.Algebra.Boolean: true :: Boolean b => b
+ Data.Algebra.Boolean: xor :: Boolean b => b -> b -> b
- Control.Conditional: (<&&>) :: Monad m => m Bool -> m Bool -> m Bool
+ Control.Conditional: (<&&>) :: (ToBool b, Boolean b, Monad m) => m b -> m b -> m b
- Control.Conditional: (<||>) :: Monad m => m Bool -> m Bool -> m Bool
+ Control.Conditional: (<||>) :: (ToBool b, Boolean b, Monad m) => m b -> m b -> m b
- Control.Conditional: (?) :: Bool -> (Bool -> a) -> a
+ Control.Conditional: (?) :: b -> (b -> a) -> a
- Control.Conditional: (?.) :: Category cat => Bool -> cat a a -> cat a a
+ Control.Conditional: (?.) :: (ToBool b, Category cat) => b -> cat a a -> cat a a
- Control.Conditional: (?<>) :: Monoid a => Bool -> a -> a
+ Control.Conditional: (?<>) :: (ToBool b, Monoid a) => b -> a -> a
- Control.Conditional: (??) :: a -> a -> Bool -> a
+ Control.Conditional: (??) :: ToBool b => a -> a -> b -> a
- Control.Conditional: (|>) :: Bool -> a -> Maybe a
+ Control.Conditional: (|>) :: ToBool b => b -> a -> Maybe a
- Control.Conditional: (⊳) :: Bool -> a -> Maybe a
+ Control.Conditional: (⊳) :: ToBool b => b -> a -> Maybe a
- Control.Conditional: bool :: a -> a -> Bool -> a
+ Control.Conditional: bool :: ToBool b => a -> a -> b -> a
- Control.Conditional: cond :: [(Bool, a)] -> a
+ Control.Conditional: cond :: ToBool b => [(b, a)] -> a
- Control.Conditional: condM :: Monad m => [(m Bool, m a)] -> m a
+ Control.Conditional: condM :: (ToBool b, Monad m) => [(m b, m a)] -> m a
- Control.Conditional: condPlus :: MonadPlus m => [(Bool, a)] -> m a
+ Control.Conditional: condPlus :: (ToBool b, MonadPlus m) => [(b, a)] -> m a
- Control.Conditional: condPlusM :: MonadPlus m => [(m Bool, m a)] -> m a
+ Control.Conditional: condPlusM :: (ToBool b, MonadPlus m) => [(m b, m a)] -> m a
- Control.Conditional: guardM :: MonadPlus m => m Bool -> m ()
+ Control.Conditional: guardM :: (ToBool b, MonadPlus m) => m b -> m ()
- Control.Conditional: if' :: Bool -> a -> a -> a
+ Control.Conditional: if' :: ToBool b => b -> a -> a -> a
- Control.Conditional: ifM :: Monad m => m Bool -> m a -> m a -> m a
+ Control.Conditional: ifM :: (ToBool b, Monad m) => m b -> m a -> m a -> m a
- Control.Conditional: notM :: Monad m => m Bool -> m Bool
+ Control.Conditional: notM :: (Boolean b, Monad m) => m b -> m b
- Control.Conditional: otherwiseM :: Monad m => m Bool
+ Control.Conditional: otherwiseM :: (Boolean b, Monad m) => m b
- Control.Conditional: select :: (a -> Bool) -> (a -> b) -> (a -> b) -> (a -> b)
+ Control.Conditional: select :: ToBool p => (a -> p) -> (a -> b) -> (a -> b) -> (a -> b)
- Control.Conditional: selectM :: Monad m => (a -> m Bool) -> (a -> m b) -> (a -> m b) -> (a -> m b)
+ Control.Conditional: selectM :: (ToBool p, Monad m) => (a -> m p) -> (a -> m b) -> (a -> m b) -> (a -> m b)
- Control.Conditional: unlessM :: Monad m => m Bool -> m () -> m ()
+ Control.Conditional: unlessM :: (ToBool b, Boolean b, Monad m) => m b -> m () -> m ()
- Control.Conditional: whenM :: Monad m => m Bool -> m () -> m ()
+ Control.Conditional: whenM :: (ToBool b, Monad m) => m b -> m () -> m ()
Files
- cond.cabal +11/−4
- src/Control/Conditional.hs +107/−50
- src/Data/Algebra/Boolean.hs +97/−0
cond.cabal view
@@ -1,5 +1,5 @@ Name: cond-Version: 0.2+Version: 0.3 Synopsis: Basic conditional and boolean operators with monadic variants. Category: Control, Logic, Monad License: BSD3@@ -10,11 +10,17 @@ Cabal-Version: >= 1.6 Build-Type: Simple Description:- A very simple library implementing various conditional operations, as well- as some functions for dealing with conditions in monadic code.+ This library provides:+ .+ * Implementations of various overloaded conditional operations+ .+ * Lifted monadic variants of those operations and common boolean operators+ .+ * A typeclass for boolean algebras.+ . Feel free to send ideas and suggestions for new conditional operators to the maintainer.-+ . Monadic looping constructs are not included as part of this package, since the monad-loops package has a fairly complete collection of them already. @@ -26,4 +32,5 @@ hs-source-dirs: src ghc-options: -Wall exposed-modules: Control.Conditional+ Data.Algebra.Boolean build-depends: base >= 3 && < 5
src/Control/Conditional.hs view
@@ -1,15 +1,19 @@+{-# LANGUAGE FlexibleInstances #-} -- |A convenient set of useful conditional operators. module Control.Conditional- ( -- * Simple conditional operators- if', (??), bool+ ( -- *Conversion typeclass+ ToBool(..)+ -- * Basic conditional operators+ , if', (??), bool+ , ifM, (<||>), (<&&>), notM, xorM -- * Lisp-style conditional operators - , cond, condPlus+ , cond, condPlus, condM, condPlusM, otherwiseM -- * Conditional operator on categories , (?.) -- * Conditional operator on monoids , (?<>) -- * Conditional operator on functions- , select+ , select, selectM -- * C-style ternary conditional , (?) -- *Hoare's conditional choice operator@@ -24,42 +28,62 @@ -- For more information see -- <http://zenzike.com/posts/2011-08-01-the-conditional-choice-operator> , (|>), (<|)+ -- **Lifted conditional choice+ -- |In addition, you can write lifted conditionals of the form:+ -- + -- > t <<| p |>> f+ , (|>>), (<<|) -- **Unicode variants -- |Intended to resemble the notation used in Tony Hoare's -- Unified Theories of Programming. , (⊳), (⊲)- -- * Lifted conditional and boolean operators- , ifM, (<||>), (<&&>), notM, condM, condPlusM, otherwiseM- , guardM, whenM, unlessM, selectM + -- *Generalized monadic conditionals+ , guard, guardM, when, whenM, unless, unlessM, ) where -import Control.Monad+import Data.Algebra.Boolean+import Control.Monad hiding (guard, when, unless) import Control.Category import Data.Monoid-import Prelude hiding ((.), id)+import Data.Maybe+import Prelude hiding ((.), id, (&&), (||), not) -infixr 0 <|, |>, ⊳, ⊲, ?+infixr 0 <|, |>, ⊳, ⊲, ?, <<|, |>> infixr 1 ?? infixr 2 <||> infixr 3 <&&> infixr 7 ?<> infixr 9 ?. --- |A simple conditional function.-if' :: Bool -> a -> a -> a-if' p t f = if p then t else f-{-# INLINE if' #-}+-- |Conversion of values to 'Bool'.+--+-- Instances of 'ToBool' that are also 'Boolean' should obey the following laws:+--+-- > p || q = if toBool p then true else q+--+-- > p && q = if toBool p then q else false+class ToBool b where+ toBool :: b -> Bool +instance ToBool Bool where toBool = id+instance ToBool Any where toBool = getAny+instance ToBool All where toBool = getAll+instance ToBool (Dual Bool) where toBool = getDual++-- |A simple conditional operator+if' :: ToBool b => b -> a -> a -> a+if' p t f = if toBool p then t else f+ -- |'if'' with the 'Bool' argument at the end (infixr 1).-(??) :: a -> a -> Bool -> a+(??) :: ToBool b => a -> a -> b -> a (??) t f p = if' p t f {-# INLINE (??) #-} --- |A catamorphism (aka fold) for the Bool type. This is analogous to +-- |A catamorphism (aka fold) for booleans. This is analogous to -- 'foldr', 'Data.Maybe.maybe', and 'Data.Either.either'. The first argument is -- the false case, the second argument is the true case, and the last argument -- is the predicate value.-bool :: a -> a -> Bool -> a+bool :: (ToBool b) => a -> a -> b -> a bool f t p = if' p t f {-# INLINE bool #-} @@ -71,7 +95,7 @@ -- ,(x < 0 , -1) -- ,(otherwise , 0 )] -- @-cond :: [(Bool, a)] -> a+cond :: ToBool b => [(b, a)] -> a cond [] = error "cond: no matching conditions" cond ((p,v):ls) = if' p v (cond ls) @@ -92,14 +116,14 @@ -- signum x = 0 \<| condPlus [(x > 0, 1 ) -- ,(x < 0, -1)] -- @-condPlus :: MonadPlus m => [(Bool, a)] -> m a+condPlus :: (ToBool b, MonadPlus m) => [(b, a)] -> m a condPlus [] = mzero condPlus ((p,v):ls) = if' p (return v) (condPlus ls) -- |Conditional composition. If the predicate is False, 'id' is returned -- instead of the second argument. This function, for example, can be used to -- conditionally add functions to a composition chain.-(?.) :: Category cat => Bool -> cat a a -> cat a a+(?.) :: (ToBool b, Category cat) => b -> cat a a -> cat a a p ?. c = if' p c id {-# INLINE (?.) #-} @@ -109,68 +133,89 @@ -- -- Note that after importing "Control.Monad.Instances", 'select' becomes a -- special case of 'ifM'.-select :: (a -> Bool) -> (a -> b) -> (a -> b) -> (a -> b)+select :: ToBool p => (a -> p) -> (a -> b) -> (a -> b) -> (a -> b) select p t f x = if' (p x) (t x) (f x) {-# INLINE select #-} -- |'if'' lifted to 'Monad'. Unlike 'liftM3' 'if'', this is -- short-circuiting in the monad, such that only the predicate action and one of -- the remaining argument actions are executed.-ifM :: Monad m => m Bool -> m a -> m a -> m a +ifM :: (ToBool b, Monad m) => m b -> m a -> m a -> m a ifM p t f = p >>= bool f t {-# INLINE ifM #-} --- |Lifted boolean or. Unlike 'liftM2' ('||'), This function is short-circuiting--- in the monad. Fixity is the same as '||' (infixr 2).-(<||>) :: Monad m => m Bool -> m Bool -> m Bool-(<||>) t f = ifM t (return True) f+-- |Lifted inclusive disjunction. Unlike 'liftM2' ('||'), This function is +-- short-circuiting in the monad. Fixity is the same as '||' (infixr 2).+(<||>) :: (ToBool b, Boolean b, Monad m) => m b -> m b -> m b+(<||>) t f = ifM t (return true) f {-# INLINE (<||>) #-} --- |Lifted boolean and. Unlike 'liftM2' ('&&'), this function is +-- |Lifted conjunction. Unlike 'liftM2' ('&&'), this function is -- short-circuiting in the monad. Fixity is the same as '&&' (infxr 3).-(<&&>) :: Monad m => m Bool -> m Bool -> m Bool-(<&&>) t f = ifM t f (return False)+(<&&>) :: (ToBool b, Boolean b, Monad m) => m b -> m b -> m b+(<&&>) t f = ifM t f (return false) {-# INLINE (<&&>) #-} -- |Lifted boolean negation.-notM :: Monad m => m Bool -> m Bool+notM :: (Boolean b, Monad m) => m b -> m b notM = liftM not {-# INLINE notM #-} +-- |Lifted boolean exclusive disjunction.+xorM :: (Boolean b, Monad m) => m b -> m b -> m b+xorM = liftM2 xor+ -- |'cond' lifted to 'Monad'. If no conditions match, a runtime exception -- is thrown.-condM :: Monad m => [(m Bool, m a)] -> m a +condM :: (ToBool b, Monad m) => [(m b, m a)] -> m a condM [] = error "condM: no matching conditions" condM ((p, v):ls) = ifM p v (condM ls) -- |'condPlus' lifted to 'Monad'. If no conditions match, then 'mzero' -- is returned.-condPlusM :: MonadPlus m => [(m Bool, m a)] -> m a+condPlusM :: (ToBool b, MonadPlus m) => [(m b, m a)] -> m a condPlusM [] = mzero condPlusM ((p, v):ls) = ifM p v (condPlusM ls) --- |A synonym for 'return' 'True'.-otherwiseM :: Monad m => m Bool-otherwiseM = return True+-- |A synonym for 'return' 'true'.+otherwiseM :: (Boolean b, Monad m) => m b+otherwiseM = return true --- |A variant of 'Control.Monad.when' with a monadic predicate.-whenM :: Monad m => m Bool -> m () -> m ()+-- |Generalization of 'Control.Monad.guard'+guard :: (ToBool b, MonadPlus m) => b -> m ()+guard p = if' p (return ()) mzero+{-# INLINE guard #-}++-- |Generalization of 'Control.Monad.when'+when :: (ToBool b, MonadPlus m) => b -> m () -> m ()+when p m = if' p m (return ())+{-# INLINE when #-}++-- |Generalization of 'Control.Monad.unless'+unless :: (Boolean b, ToBool b, MonadPlus m) => b -> m() -> m()+unless p m = if' (not p) m (return ())+{-# INLINE unless #-}++-- |A variant of 'when' with a monadic predicate.+whenM :: (ToBool b, Monad m) => m b -> m () -> m () whenM p m = ifM p m (return ()) {-# INLINE whenM #-} --- |A variant of 'Control.Monad.unless' with a monadic predicate.-unlessM :: Monad m => m Bool -> m () -> m ()+-- |A variant of 'unless' with a monadic predicate.+unlessM :: (ToBool b, Boolean b, Monad m) => m b -> m () -> m () unlessM p m = ifM (notM p) m (return ()) {-# INLINE unlessM #-} --- |A variant of 'Control.Monad.guard' with a monadic predicate.-guardM :: MonadPlus m => m Bool -> m ()+-- |A variant of 'guard' with a monadic predicate.+guardM :: (ToBool b, MonadPlus m) => m b -> m () guardM = (guard =<<) {-# INLINE guardM #-} -- |'select' lifted to 'Monad'.-selectM :: Monad m => (a -> m Bool) -> (a -> m b) -> (a -> m b) -> (a -> m b)+selectM :: (ToBool p, Monad m) => + (a -> m p) -> (a -> m b) -> (a -> m b) -> (a -> m b) selectM p t f x = ifM (p x) (t x) (f x) +{-# INLINE selectM #-} -- |Conditional monoid operator. If the predicate is 'False', the second -- argument is replaced with 'mempty'. The fixity of this operator is one@@ -179,7 +224,7 @@ -- It can also be used to chain multiple predicates together, like this: -- -- > even (length ls) ?<> not (null ls) ?<> ls-(?<>) :: Monoid a => Bool -> a -> a+(?<>) :: (ToBool b, Monoid a) => b -> a -> a p ?<> m = if' p m mempty {-# INLINE (?<>) #-} @@ -191,26 +236,38 @@ -- -- Note that parentheses are required in order to chain sequences of -- conditionals together. This is probably a good thing.-(?) :: Bool -> (Bool -> a) -> a+(?) :: b -> (b -> a) -> a p ? f = f p+{-# INLINE (?) #-} -- |Right bracket of the conditional choice operator. If the predicate -- is 'False', returns 'Nothing', otherwise it returns 'Just' the right-hand -- argument.-(|>) :: Bool -> a -> Maybe a-True |> _ = Nothing-False |> f = Just f+(|>) :: ToBool b => b -> a -> Maybe a+p |> v = if' p Nothing (Just v)+{-# INLINE (|>) #-} -- |Left bracket of the conditional choice operator. This is equivalent to -- 'Data.Maybe.fromMaybe' (<|) :: a -> Maybe a -> a t <| Nothing = t _ <| Just f = f+{-# INLINE (<|) #-} --- |Unicode rebinding of '|>'. +-- |A monadic variant of '|>'.+(|>>) :: (ToBool b, Monad m) => m b -> m a -> m (Maybe a)+p |>> v = ifM p (return Nothing) (liftM Just v)+{-# INLINE (|>>) #-}++-- |A monadic variant of '<|'.+(<<|) :: Monad m => m a -> m (Maybe a) -> m a+v <<| mv = liftM2 fromMaybe v mv+{-# INLINE (<<|) #-}++-- |Unicode rebinding of '<|'. (⊲) :: a -> Maybe a -> a (⊲) = (<|) --- |Unicode rebinding of '<|'.-(⊳) :: Bool -> a -> Maybe a+-- |Unicode rebinding of '|>'.+(⊳) :: ToBool b => b -> a -> Maybe a (⊳) = (|>)
+ src/Data/Algebra/Boolean.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE FlexibleInstances #-}+module Data.Algebra.Boolean + ( Boolean(..), fromBool+ ) where+import Data.Monoid (Any(..), All(..), Dual(..), Endo(..))+import Prelude hiding ((&&), (||), not)+import qualified Prelude as P+infixr 0 -->+infixr 2 ||+infixr 3 &&++-- |A class for boolean algebras. Instances of this class should obey+-- all the axioms of boolean algebra.+--+-- Minimal complete definition: 'true', 'false', 'not' or '<-->', '||' or '&&'. +class Boolean b where+ -- |Truth value.+ true :: b+ -- |False value.+ false :: b+ -- |Logical negation.+ not :: b -> b+ -- |Logical conjunction.+ (&&) :: b -> b -> b+ -- |Logical inclusive disjunction.+ (||) :: b -> b -> b+ -- |Logical exclusive disjunction.+ xor :: b -> b -> b+ -- |Logical implication+ (-->) :: b -> b -> b+ -- |Logical biconditional+ (<-->) :: b -> b -> b+ + -- Default implementations+ not = (<--> false)+ x && y = not (x || y) + x || y = not (x && y)+ x `xor` y = (x || y) && (not (x && y))+ x --> y = not x || y+ x <--> y = (x && y) || not (x || y)++-- |Injection from 'Bool' into a boolean algebra.+fromBool :: Boolean b => Bool -> b+fromBool b = if b then true else false++instance Boolean Bool where+ true = True+ false = False+ (&&) = (P.&&)+ (||) = (P.||)+ not = P.not+ xor = (/=)+ True --> True = True+ True --> False = False+ False --> _ = True+ (<-->) = (==)+ ++instance Boolean Any where+ true = Any True+ false = Any False+ not (Any p) = Any (not p)+ (Any p) && (Any q) = Any (p && q)+ (Any p) || (Any q) = Any (p || q)+ (Any p) `xor` (Any q) = Any (p `xor` q)+ (Any p) --> (Any q) = Any (p --> q)+ (Any p) <--> (Any q) = Any (p <--> q)+ +instance Boolean All where+ true = All True+ false = All False+ not (All p) = All (not p)+ (All p) && (All q) = All (p && q)+ (All p) || (All q) = All (p || q)+ (All p) `xor` (All q) = All (p `xor` q)+ (All p) --> (All q) = All (p --> q)+ (All p) <--> (All q) = All (p <--> q)+ +instance Boolean (Dual Bool) where+ true = Dual True+ false = Dual False+ not (Dual p) = Dual (not p)+ (Dual p) && (Dual q) = Dual (p && q)+ (Dual p) || (Dual q) = Dual (p || q)+ (Dual p) `xor` (Dual q) = Dual (p `xor` q)+ (Dual p) --> (Dual q) = Dual (p --> q)+ (Dual p) <--> (Dual q) = Dual (p <--> q)+ +instance Boolean (Endo Bool) where+ true = Endo (const True)+ false = Endo (const False)+ not (Endo p) = Endo (not . p)+ (Endo p) && (Endo q) = Endo (\a -> p a && q a)+ (Endo p) || (Endo q) = Endo (\a -> p a || q a)+ (Endo p) `xor` (Endo q) = Endo (\a -> p a `xor` q a)+ (Endo p) --> (Endo q) = Endo (\a -> p a --> q a)+ (Endo p) <--> (Endo q) = Endo (\a -> p a <--> q a)