predicate-transformers 0.16.0.0 → 0.17.0.0
raw patch · 3 files changed
+103/−122 lines, 3 filesdep −lensdep −mtlPVP ok
version bump matches the API change (PVP)
Dependencies removed: lens, mtl
API changes (from Hackage documentation)
- PredicateTransformers: also :: Predicatory a => a -> a -> a
- PredicateTransformers: class Exceptional a
- PredicateTransformers: class Predicatory a
- PredicateTransformers: continue :: Predicatory a => a
- PredicateTransformers: instance (a GHC.Types.~ ()) => PredicateTransformers.Exceptional (GHC.Types.IO a)
- PredicateTransformers: instance (a GHC.Types.~ ()) => PredicateTransformers.Predicatory (GHC.Types.IO a)
- PredicateTransformers: instance PredicateTransformers.Exceptional GHC.Types.Bool
- PredicateTransformers: instance PredicateTransformers.Exceptional a => PredicateTransformers.Exceptional (e -> a)
- PredicateTransformers: instance PredicateTransformers.Predicatory GHC.Types.Bool
- PredicateTransformers: instance PredicateTransformers.Predicatory a => PredicateTransformers.Predicatory (e -> a)
- PredicateTransformers: otherHand :: Predicatory a => a -> a -> a
- PredicateTransformers: predCompose :: Representable f => f (Pred p a) -> f a -> f p
- PredicateTransformers: predJust :: Predicatory p => PT p a (Maybe a)
- PredicateTransformers: predLeft :: Predicatory p => PT p e (Either e a)
- PredicateTransformers: predList :: (HasCallStack, Predicatory p) => [Pred p a] -> [a] -> p
- PredicateTransformers: predRight :: Predicatory p => PT p a (Either e a)
- PredicateTransformers: pt :: (a -> b) -> PT p b a
- PredicateTransformers: satAll :: Predicatory p => [Pred p a] -> Pred p a
- PredicateTransformers: soleElement :: (Predicatory p, Foldable f) => PT p a (f a)
- PredicateTransformers: soleElementOf :: (HasCallStack, Predicatory p) => Fold s a -> PT p a s
- PredicateTransformers: something :: Predicatory p => Pred p a
- PredicateTransformers: stop :: (Predicatory a, HasCallStack) => v -> Doc ann -> a
+ PredicateTransformers: and :: Boolish a => a -> a -> a
+ PredicateTransformers: class Boolish a
+ PredicateTransformers: compose :: Representable f => f (Pred p a) -> f a -> f p
+ PredicateTransformers: fail :: (Boolish a, HasCallStack) => Doc ann -> v -> a
+ PredicateTransformers: fun :: (a -> b) -> PT p b a
+ PredicateTransformers: instance (a GHC.Types.~ ()) => PredicateTransformers.Boolish (GHC.Types.IO a)
+ PredicateTransformers: instance PredicateTransformers.Boolish GHC.Types.Bool
+ PredicateTransformers: instance PredicateTransformers.Boolish a => PredicateTransformers.Boolish (e -> a)
+ PredicateTransformers: list :: (HasCallStack, Boolish p) => [Pred p a] -> [a] -> p
+ PredicateTransformers: or :: Boolish a => a -> a -> a
+ PredicateTransformers: succeed :: Boolish a => a
- PredicateTransformers: allOf1 :: (HasCallStack, Predicatory p) => Fold s a -> PT p a s
+ PredicateTransformers: allOf1 :: (HasCallStack, Boolish p) => Getting [a] s a -> PT p a s
- PredicateTransformers: allTrue :: (Predicatory p, Foldable f) => f (Pred p a) -> Pred p a
+ PredicateTransformers: allTrue :: (Boolish p, Foldable f) => f (Pred p a) -> Pred p a
- PredicateTransformers: assess :: Exceptional a => a -> IO () -> a
+ PredicateTransformers: assess :: Boolish a => a -> IO () -> a
- PredicateTransformers: endingWith :: (HasCallStack, Predicatory p, Foldable f) => PT p a (f a)
+ PredicateTransformers: endingWith :: (HasCallStack, Boolish p, Foldable f) => PT p a (f a)
- PredicateTransformers: equals :: (HasCallStack, Predicatory p, Eq a) => a -> Pred p a
+ PredicateTransformers: equals :: (HasCallStack, Boolish p, Eq a) => a -> Pred p a
- PredicateTransformers: forced :: (Predicatory p, NFData a) => Pred p a
+ PredicateTransformers: forced :: (Boolish p, NFData a) => Pred p a
- PredicateTransformers: infixr 2 `otherHand`
+ PredicateTransformers: infixr 2 `or`
- PredicateTransformers: infixr 3 `also`
+ PredicateTransformers: infixr 3 `and`
- PredicateTransformers: kth :: (Predicatory p, Foldable f) => Int -> PT p a (f a)
+ PredicateTransformers: kth :: (Boolish p, Foldable f) => Int -> PT p a (f a)
- PredicateTransformers: match :: Predicatory p => Prism' s a -> PT p a s
+ PredicateTransformers: match :: (HasCallStack, Boolish p) => Getting [a] s a -> PT p a s
- PredicateTransformers: pair :: Predicatory p => Pred p a -> Pred p b -> Pred p (a, b)
+ PredicateTransformers: pair :: Boolish p => Pred p a -> Pred p b -> Pred p (a, b)
- PredicateTransformers: predful :: (HasCallStack, Predicatory p, Eq (f ()), Functor f, Foldable f) => f (Pred p a) -> Pred p (f a)
+ PredicateTransformers: predful :: (HasCallStack, Boolish p, Eq (f ()), Functor f, Foldable f) => f (Pred p a) -> Pred p (f a)
- PredicateTransformers: startingWith :: (HasCallStack, Predicatory p, Foldable f) => PT p a (f a)
+ PredicateTransformers: startingWith :: (HasCallStack, Boolish p, Foldable f) => PT p a (f a)
- PredicateTransformers: traceFail :: (Predicatory p, Exceptional p) => (a -> String) -> PT p a a
+ PredicateTransformers: traceFail :: Boolish p => (a -> String) -> PT p a a
- PredicateTransformers: traceFailShow :: (Exceptional p, Predicatory p, Show a) => PT p a a
+ PredicateTransformers: traceFailShow :: (Boolish p, Show a) => PT p a a
Files
- CHANGELOG.md +13/−0
- predicate-transformers.cabal +1/−3
- src/PredicateTransformers.hs +89/−119
CHANGELOG.md view
@@ -1,5 +1,18 @@ # Revision history for predicate-transformers +## 0.17.0.0 -- 2024-10-13+* Rename multiple functions to shorter names with the intent that predicate-transformers be imported qualified.+ In particular `also` renamed to `and`, `otherHand` renamed to `or`, etc.+* Delete `lens` and `mtl` dependencies.+* Delete `soleElementOf`, for lack of use in favor of `match`.+* Rename `Predicatory` to `Boolish` for ease of reading.+* Delete `Exceptional` class, folding it into the newly renamed `Boolish`.+* Delete `predJust`, `predLeft`, and `predRight` in favor of `match` uses.++## 0.16.0.0 -- 2024-10-13+* Add predicate failure error messages that include the actual value under test and a message explaining what was expected.+* Rename `just`, `left`, and `right` to `predJust`, `predLeft`, `predRight`+ ## 0.15.0.0 -- 2024-08-23 * Change `?` to right associative so that it works with `.`.
predicate-transformers.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: predicate-transformers-version: 0.16.0.0+version: 0.17.0.0 synopsis: A library for writing predicates and transformations over predicates in Haskell description: This package provides ways to write predicates such that they compose nicely and are easy to debug.@@ -22,8 +22,6 @@ base >=4.8 && <4.30 ,adjunctions >= 4.4.2 && < 5 ,deepseq >= 1.4.8 && < 2- ,mtl >= 2.3.1 && < 3- ,lens >= 5.3.2 && < 6 ,recover-rtti >= 0.3 && < 0.6 ,pretty-simple >= 1.1 && < 4.2 ,prettyprinter >= 1.6 && < 2.0
src/PredicateTransformers.hs view
@@ -21,45 +21,38 @@ -- `either` (base) -- `Control.Lens.allOf` (lens) module PredicateTransformers- ( Predicatory(..)- , Exceptional(..)+ ( Boolish(..) , PredicateFailed(..) , Pred , PT- , predJust- , predLeft- , predRight , endingWith , startingWith- , soleElementOf- , soleElement , match , kth- , predList+ , list , predful- , predCompose+ , compose , allTrue , allOf1 , pattern (:=>) , pair- , pt+ , fun , (?) , traced , tracedShow , traceFailShow , traceFail- , something , forced , equals- , satAll ) where +import Prelude hiding (and, fail, or) import Control.DeepSeq (NFData, force) import Control.Exception-import Control.Lens hiding (index)-import Control.Monad+import Control.Monad hiding (fail) import Data.Foldable (toList)+import Data.Functor.Const import Data.Functor.Rep (Representable (..)) import Data.Typeable import Debug.Trace@@ -73,33 +66,31 @@ import Debug.RecoverRTTI import qualified Data.Text.Lazy as TL --- | Class of possible predicate results.--- This is mostly a lattice with `otherHand` as disjunction, `also` as conjunction, `stop` as the falsy--- value, and `continue` as the truthy value. There may be multiple falsy values, however.-class Predicatory a where- otherHand :: a -> a -> a- also :: a -> a -> a- stop :: HasCallStack => v -> PP.Doc ann -> a- continue :: a- {-# MINIMAL otherHand, also, stop, continue #-}--instance Predicatory a => Predicatory (e -> a) where- (f `otherHand` f') e = f e `otherHand` f' e- (f `also` f') e = f e `also` f' e- stop v expected = withFrozenCallStack $ \_ -> stop v expected- continue = \_ -> continue--infixr 3 `also`-infixr 2 `otherHand`+type Getting r s a = (a -> Const r a) -> s -> Const r s --- | Class of predicate results which can be checked for failure,--- by triggering an action.-class Exceptional a where+-- | Class of possible predicate results.+-- This is almost a lattice with `or` as disjunction, `and` as conjunction, `fail` as the falsy+-- value, and `succeed` as the truthy value. However there may be multiple falsy values, and+-- `and` will pick the first one it's passed, whereas `or` will pick the second it's passed.+class Boolish a where+ or :: a -> a -> a+ and :: a -> a -> a+ fail :: HasCallStack => PP.Doc ann -> v -> a+ succeed :: a+ -- | Check and execute a callback on failure. assess :: a -> IO () -> a+ {-# MINIMAL or, and, fail, succeed, assess #-} -instance Exceptional a => Exceptional (e -> a) where+instance Boolish a => Boolish (e -> a) where+ (f `or` f') e = f e `or` f' e+ (f `and` f') e = f e `and` f' e+ fail expected actual = withFrozenCallStack $ \_ -> fail expected actual+ succeed = \_ -> succeed assess f act = \e -> assess (f e) act +infixr 3 `and`+infixr 2 `or`+ -- | The exception thrown by predicates of type `IO ()` by default. Other IOExceptions will work fine. data PredicateFailed = forall actual ann. PredicateFailed !CallStack (PP.Doc ann) actual deriving (Typeable)@@ -127,19 +118,17 @@ where prettyActual = anythingToStringPretty actual -instance Predicatory Bool where- otherHand = (||)- also = (&&)- stop _ _ = False- continue = True--instance Exceptional Bool where+instance Boolish Bool where+ or = (||)+ and = (&&)+ fail _ _ = False+ succeed = True assess b act | b = b | otherwise = unsafePerformIO act `pseq` b -instance a ~ () => Predicatory (IO a) where- otherHand x y = do+instance a ~ () => Boolish (IO a) where+ or x y = do catches x -- explicitly do not handle async exceptions. -- otherwise, a thread being killed may appear as a predicate failure.@@ -148,11 +137,9 @@ throwTo tid ex , Handler $ \(_ex :: SomeException) -> y ]- also = (>>)- stop v expected = throwIO (PredicateFailed (popCallStack callStack) expected v)- continue = return ()--instance a ~ () => Exceptional (IO a) where+ and = (>>)+ fail expected actual = throwIO (PredicateFailed (popCallStack callStack) expected actual)+ succeed = return () assess x act = catches x -- explicitly do not handle async exceptions.@@ -173,51 +160,37 @@ -- for instance `Data.Foldable.all` and `Data.Foldable.any`. type PT p a b = Pred p a -> Pred p b --- | Operate on the `Just` branch of a `Maybe`, or fail.-predJust :: Predicatory p => PT p a (Maybe a)-predJust = match _Just---- | Operate on the `Left` branch of an `Either`, or fail.-predLeft :: Predicatory p => PT p e (Either e a)-predLeft = match _Left---- | Operate on the `Right` branch of an `Either`, or fail.-predRight :: Predicatory p => PT p a (Either e a)-predRight = match _Right- -- | Operate on the last value in a foldable, or fail if it's not present.-endingWith :: (HasCallStack, Predicatory p, Foldable f) => PT p a (f a)-endingWith _ actual@(toList -> []) = stop actual "nonempty foldable"+endingWith :: (HasCallStack, Boolish p, Foldable f) => PT p a (f a)+endingWith _ actual@(toList -> []) = fail "nonempty foldable" actual endingWith p (toList -> xs) = p $ last xs -- | Operate on the first value in a foldable, or fail if it's not present.-startingWith :: (HasCallStack, Predicatory p, Foldable f) => PT p a (f a)-startingWith _ actual@(toList -> []) = stop actual "nonempty foldable"+startingWith :: (HasCallStack, Boolish p, Foldable f) => PT p a (f a)+startingWith _ actual@(toList -> []) = fail "nonempty foldable" actual startingWith p (toList -> (x : _)) = p x --- | Require that a @Fold@ has a single element, and operate on that element.-soleElementOf :: (HasCallStack, Predicatory p) => Fold s a -> PT p a s-soleElementOf f p (toListOf f -> [x]) = p x-soleElementOf _ _ actual = stop actual "only one element targeted by fold"---- | Require that a @Foldable@ has a single element, and operate on that element.-soleElement :: (Predicatory p, Foldable f) => PT p a (f a)-soleElement = soleElementOf folded- -- | Require that a @Prism@ matches, and apply the predicate to its contents.-match :: Predicatory p => Prism' s a -> PT p a s-match = soleElementOf+-- This works for folds, too.+match+ :: (HasCallStack, Boolish p)+ => Getting [a] s a+ -> PT p a s+match f p s =+ case f (Const . pure) s of+ Const [x] -> p x+ _ -> fail "fold yields exactly one element" s -- | Only test the @k@th element of a foldable.-kth :: (Predicatory p, Foldable f) => Int -> PT p a (f a)+kth :: (Boolish p, Foldable f) => Int -> PT p a (f a) kth k p = startingWith p . drop k . toList -- | Given a list of predicates and a list of values, ensure that each predicate holds for each respective value. -- Fails if the two lists have different lengths.-predList :: (HasCallStack, Predicatory p) => [Pred p a] -> [a] -> p-predList ps xs- | psl == length xs = foldr also continue (zipWith ($) ps xs)- | otherwise = stop xs $ "list with length " <> PP.pretty psl+list :: (HasCallStack, Boolish p) => [Pred p a] -> [a] -> p+list ps xs+ | psl == length xs = foldr and succeed (zipWith ($) ps xs)+ | otherwise = fail ("list with length " <> PP.pretty psl) xs where psl = length ps @@ -225,88 +198,85 @@ -- of the two functors match and apply all of the predicates to all of the values. -- Generalized version of `list`. predful ::- (HasCallStack, Predicatory p, Eq (f ()), Functor f, Foldable f) =>+ (HasCallStack, Boolish p, Eq (f ()), Functor f, Foldable f) => f (Pred p a) -> Pred p (f a) predful preds values | void preds == void values =- predList (toList preds) (toList values)+ list (toList preds) (toList values) | otherwise =- stop values ("shape equal to that of" <> PP.pretty (anythingToStringPretty preds))+ fail ("shape equal to that of" <> PP.pretty (anythingToStringPretty preds)) values -- | Given a representable functor-full of predicates, and a functor-full of values, -- yield a representable functor-full of booleans. Similar to `predful`.-predCompose ::+compose :: Representable f => f (Pred p a) -> f a -> f p-predCompose pr fa = tabulate (\r -> index pr r $ index fa r)+compose pr fa = tabulate (\r -> index pr r $ index fa r) -- | Test all predicates against one value.-allTrue :: (Predicatory p, Foldable f) => f (Pred p a) -> Pred p a-allTrue ps a = foldr (\p r -> p a `also` r) continue ps+allTrue :: (Boolish p, Foldable f) => f (Pred p a) -> Pred p a+allTrue ps a = foldr (\p r -> p a `and` r) succeed ps -- | Check that a predicate is true for all values behind a generalized getter -- and that there's at least one value for which it's true.-allOf1 :: (HasCallStack, Predicatory p) => Fold s a -> PT p a s+allOf1+ :: (HasCallStack, Boolish p)+ => Getting [a] s a+ -> PT p a s allOf1 g p vs- | notNullOf g vs =- foldrOf g (\x r -> p x `also` r) continue vs- | otherwise = stop vs "non-empty for fold"+ | [] <- vsList =+ foldr (\x r -> p x `and` r) succeed vsList+ | otherwise = fail "non-empty for fold" vs+ where+ Const vsList = g (Const . pure) vs -- | Sugar for tupling. pattern (:=>) :: a -> b -> (a, b) pattern a :=> b = (a, b) -- | A pair of predicates, made into a predicate on pairs.-pair :: Predicatory p => Pred p a -> Pred p b -> Pred p (a, b)-pair f s (a, b) = f a `also` s b+pair :: Boolish p => Pred p a -> Pred p b -> Pred p (a, b)+pair f s (a, b) = f a `and` s b -- | Flipped function composition; @pf f@ for a function @f@ is a predicate transformer -- such that @pf f p i == p (f i)@.-pt :: (a -> b) -> PT p b a-pt f p = p . f+fun :: (a -> b) -> PT p b a+fun f p = p . f -- | Higher precedence '$', to work well with '&'. (?) :: (a -> b) -> a -> b (?) = ($) infixr 8 ? --- | Prints the input of a predicate, for debugging.-traced :: Show a => (a -> String) -> PT c a a-traced s p a = trace (s a) (p a)---- | Prints the input of a predicate, for debugging.-tracedShow :: Show a => PT c a a-tracedShow = traced show- -- | Prints the input of a predicate, if the predicate fails, using `Show`. -- Requires that the predicate's output type can be checked for failure.-traceFailShow :: (Exceptional p, Predicatory p, Show a) => PT p a a+traceFailShow :: (Boolish p, Show a) => PT p a a traceFailShow = traceFail show -- | Prints the input of a predicate over functions, if the predicate fails. -- Requires that the predicate's output type can be checked for failure.-traceFail :: (Predicatory p, Exceptional p) => (a -> String) -> PT p a a+traceFail :: (Boolish p) => (a -> String) -> PT p a a traceFail s p a = assess (p a) $ traceIO (s a) --- | Predicate which always succeeds.-something :: Predicatory p => Pred p a-something = const continue+-- | Prints the input of a predicate, for debugging.+traced :: Show a => (a -> String) -> PT c a a+traced s p a = trace (s a) (p a) +-- | Prints the input of a predicate, for debugging.+tracedShow :: Show a => PT c a a+tracedShow = traced show+ -- | Predicate which triggers full evaluation of its input and succeeds. -- Useful for testing that an exception isn't thrown.-forced :: (Predicatory p, NFData a) => Pred p a-forced a = force a `seq` continue+forced :: (Boolish p, NFData a) => Pred p a+forced a = force a `seq` succeed -- | Predicate on equality.-equals :: (HasCallStack, Predicatory p, Eq a) => a -> Pred p a+equals :: (HasCallStack, Boolish p, Eq a) => a -> Pred p a equals expected actual- | expected == actual = continue- | otherwise = stop actual ("equal to" <> PP.softline <> PP.pretty (anythingToStringPretty expected))---- | Check that all of the input predicates are satisfied.-satAll :: Predicatory p => [Pred p a] -> Pred p a-satAll xs a = foldr also continue $ map ($ a) xs+ | expected == actual = succeed+ | otherwise = fail ("equal to" <> PP.softline <> PP.pretty (anythingToStringPretty expected)) actual