packages feed

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 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