packages feed

quickcheck-higherorder (empty) → 0.1.0.0

raw patch · 12 files changed

+762/−0 lines, 12 filesdep +QuickCheckdep +basedep +quickcheck-higherordersetup-changed

Dependencies added: QuickCheck, base, quickcheck-higherorder, tasty, tasty-hunit, tasty-quickcheck, test-fun

Files

+ LICENSE view
@@ -0,0 +1,19 @@+Copyright Li-yao Xia (c) 2020++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the “Software”), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,186 @@+# Higher-order QuickCheck++A QuickCheck extension for properties of higher-order values.++## Summary++QuickCheck has a cute trick to implicitly convert functions+`Thing -> Bool` or `Thing -> Gen Bool` to testable properties,+provided `Thing` is an instance of `Arbitrary` and `Show`,+i.e., there is a random generator, a shrinker, and a printer for `Thing`.+Sadly, those constraints limit the range of types that `Thing` can be.+In particular, they rule out functions and other values of "infinite size".++This library, *quickcheck-higherorder*, lifts that limitation, generalizing+that technique to arbitrary types, and provides other related quality-of-life+improvements for property-based test suites.++The key idea is to separate the `Thing` manipulated by the application under+test, of arbitrary structure, from its *representation*, which is manipulated+by QuickCheck and needs to be "concrete" enough to be possible to generate,+shrink, and show.++### Constructible types++The `Constructible` type class relates types `a` to representations+`Repr a` from which their values can be constructed.+Constraints for `Arbitrary` and `Show` are thus attached to those+representations instead of the raw type that will be used in properties.++```haskell+class (Arbitrary (Repr a), Show (Repr a)) => Constructible a where+  type Repr a :: Type+  fromRepr :: Repr a -> a+```++To illustrate what it enables, here's an example of higher-order property:++```haskell+prop_bool :: (Bool -> Bool) -> Bool -> Property+prop_bool f x =+  f (f (f x)) === f x+```++In vanilla QuickCheck, it needs a little wrapping to actually run it:++```haskell+main :: IO ()+main = quickCheck (\(Fn f) x -> prop_bool f x)+```++The simpler expression `quickCheck prop_bool` would not typecheck+because `Bool -> Bool` is not an instance of `Arbitrary` nor `Show`.++With "higher-order" QuickCheck, that wrapping performed by `Fn` is instead+taken care of by the `Constructible` class, so we can write simply:++```haskell+main :: IO ()+main = quickCheck' prop_bool+```++This is especially convenient when the function type is not+directly exposed in the type of the property (as in `prop_bool`),+but may be hidden inside various data types or newtypes.++### Testable equality++In a similar vein, the `Eq` class is limited to types with+*decidable equality*,+which typically requires them to have values of "finite size".+Most notably, the type of functions `a -> b` cannot be an instance of `Eq` in+general.++But testing can still be effective with a weaker constraint, dubbed+*testable equality*. To compare two functions, we can generate some random+arguments and compare their images.+That is useful even if we can't cover the whole domain:++- if we find two inputs with distinct outputs, then the two functions+  are definitely not equal, and we now have a very concrete counterexample to+  contemplate;+- if we don't find any difference, then we can't conclude for sure, but:++    1. we can always try harder (more inputs, or rerun the whole property from+       scratch);+    2. in some situations, such as implementations of algebraic structures,+       bugs cause extremely obvious inequalities. If only we would look at them.+       The point of this new feature is to lower the bar for testing equations+       between higher-order values in the first place.++This package introduces a new type class `TestEq`, for testable equality.++```haskell+class TestEq a where+  (=?) :: a -> a -> Property+```++The codomain being `Property` offers some notable capabilities:++1. as explained earlier, we can use randomness to choose finite subsets+   of infinite values (such as functions) to compare;++2. we can also provide detailed context in the case of failure,+   by reporting the observations which lead to unequal outcomes.++For example, we can rewrite `prop_bool` as an algebraic property of functions+using `TestEq`:++```haskell+prop_bool :: (Bool -> Bool) -> Property+prop_bool f = (f . f . f) =? f+```++### More types of properties++Many common properties are quite simple, like `prop_bool`.+However, QuickCheck's way of declaring properties as functions with result type+`Property` introduces some unexpected complexity in the types.++For example, try generalizing the property `prop_bool` above to+arbitrary types instead of `Bool`+(so it's no longer valid as a property, of course).+Since we use testable equality of functions `a -> a`, we incur constraints that+the domain must be `Constructible`, and the codomain itself must have+testable equality.++```haskell+prop_fun :: (Constructible a, TestEq a) => (a -> a) -> Property+prop_fun f = (f . f . f) =? f+```++In my opinion, this type tells both too much and too little.+Too much, because the constraints leak details about the very specific+way in which the comparison is performed. Too little, because a `Property`+can do a lot of things besides testing the equality of two values;+in fact that is one cause for the previous concern.++A more precise formulation is the following:++```haskell+prop_fun :: (a -> a) -> Equation (a -> a)+prop_fun f = (f . f . f) :=: f+```++This does not actually do the comparison, but exposes just the necessary+amount of information to do it in whatever way one deems appropriate.+Indeed, `Equation` is simply a type of pairs:++```haskell+data Equation a = a :=: a+```++It is equipped with a `Testable` instance that will require a `TestEq`+constraint indirectly at call sites only.++## Example++```haskell+import Test.QuickCheck (quickCheck)+import Test.QuickCheck.HigherOrder (property', Equation((:=:)), CoArbitrary)++import Control.Monad.Cont (Cont, ContT(..), callCC)+++-- Example property+callCC_bind :: forall r a. Cont r a -> Equation (Cont r a)+callCC_bind m = callCC ((>>=) m) :=: m+++main :: IO ()+main = quickCheck' (callCC_bind @Int @Int)+++-- Newtype boilerplate++import Test.QuickCheck (Gen)+import Test.QuickCheck.HigherOrder (CoArbitrary, TestEq(..), Constructible(..))++-- Constructible instances+instance (CoArbitrary Gen (m r), Constructible a, Constructible (m r)) => Constructible (ContT r m a) where+  type Repr (ContT r m a) = Repr ((a -> m r) -> m r)+  fromRepr = ContT . fromRepr++instance (TestEq ((a -> m r) -> m r)) => TestEq (ContT r m a) where+  ContT f =? ContT g = f =? g+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ quickcheck-higherorder.cabal view
@@ -0,0 +1,77 @@+name:                quickcheck-higherorder+version:             0.1.0.0+synopsis:            QuickCheck extension for higher-order properties+description:+  Enhancements for property-based testing of higher-order properties.+  .+  * Associate types to their representations with the+    'Test.QuickCheck.HigherOrder.Constructible' class.+  * 'Test.QuickCheck.HigherOrder.Testable'' class,+    variant of 'Test.QuickCheck.Testable' with an improved instance for @(->)@.+  * Representation of higher-order functions (via test-fun).+  * Testable equality 'Test.QuickCheck.HigherOrder.TestEq'.+  * Explicit testable type of 'Test.QuickCheck.HigherOrder.Equation'.+  .+  See also README.+homepage:            https://github.com/Lysxia/quickcheck-higherorder#readme+license:             MIT+license-file:        LICENSE+author:              Li-yao Xia+maintainer:          lysxia@gmail.com+copyright:           2020 Li-yao Xia+category:            Testing+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10+tested-with:         GHC == 8.0.2 || == 8.2.2 || == 8.4.4 || == 8.6.5 || == 8.8.2++library+  hs-source-dirs:      src+  exposed-modules:+    Test.QuickCheck.HigherOrder+    Test.QuickCheck.HigherOrder.Internal.Constructible+    Test.QuickCheck.HigherOrder.Internal.Function+    Test.QuickCheck.HigherOrder.Internal.Testable+    Test.QuickCheck.HigherOrder.Internal.Testable.Class+    Test.QuickCheck.HigherOrder.Internal.TestEq+  build-depends:+    QuickCheck >= 2.12,+    test-fun,+    base >= 4.9 && < 5+  ghc-options:         -Wall+  default-language:    Haskell2010++test-suite qcho-test+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: test.hs+  build-depends:+    quickcheck-higherorder,+    tasty,+    tasty-hunit,+    tasty-quickcheck,+    base+  ghc-options: -Wall+  default-language: Haskell2010++test-suite qcho-sample+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: sample.hs+  build-depends:+    QuickCheck,+    quickcheck-higherorder,+    test-fun,+    base+  ghc-options: -Wall+  default-language: Haskell2010+  if !flag(sample)+    buildable: False++flag sample+  default: False+  manual: True++source-repository head+  type:     git+  location: https://github.com/Lysxia/quickcheck-higherorder
+ src/Test/QuickCheck/HigherOrder.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE PatternSynonyms #-}++-- | QuickCheck extension for properties of higher-order values.+--+-- See the README for an introduction.++module Test.QuickCheck.HigherOrder+  ( -- * Constructible values+    Constructible(..)++    -- * Runners+  , quickCheck'+  , quickCheckWith'++    -- * Testable properties+  , Testable'(..)++    -- ** Types of testable properties+  , Equation(..)+  , Implication(..)+  , EqImpl++    -- ** Decidable properties+  , Decidable(..)++    -- * Testable equality+  , TestEq(..)+  , decEq++    -- * Helpers+  , ok+  , ko+  , quickChecks++    -- ** @Constructible@ wrappers+  , forAll_+  , Constructed()+  , pattern Construct++    -- * CoArbitrary+    -- | See also the documentation of "Test.Fun".+  , (:->)+  , applyFun+  , CoArbitrary(..)+  , cogenEmbed+  , cogenIntegral+  , coarbitraryGeneric+  ) where++import Test.Fun++import Test.QuickCheck.HigherOrder.Internal.Testable+import Test.QuickCheck.HigherOrder.Internal.Testable.Class+import Test.QuickCheck.HigherOrder.Internal.TestEq+import Test.QuickCheck.HigherOrder.Internal.Constructible+import Test.QuickCheck.HigherOrder.Internal.Function ()
+ src/Test/QuickCheck/HigherOrder/Internal/Constructible.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++module Test.QuickCheck.HigherOrder.Internal.Constructible where++import Data.Functor.Identity+import qualified Data.Monoid as Monoid++import Test.QuickCheck (Arbitrary(..), Function, CoArbitrary, Fun)++-- * The 'Constructible' class++-- | A 'Constructible' type is associated with a type of "finite descriptions"+-- that can be generated, shown (e.g., as counterexamples in QuickCheck), and+-- interpreted as values.+-- This enhances 'Arbitrary' and 'Show' used by vanilla QuickCheck.+--+-- The main motivating example is the type of functions, which can be+-- finitely represented by the type @('Test.QuickCheck.HigherOrder.:->')@+-- (see also "Test.Fun").+--+-- It turns out we can define 'Constructible' for just about anything+-- except 'IO' (for now...).+class (Arbitrary (Repr a), Show (Repr a)) => Constructible a where+  -- | The observable representation of a value.+  type Repr a+  type instance Repr a = a+  -- | Interpret a representation as a value.+  fromRepr :: Repr a -> a++-- * The 'Constructed' modifier++-- | 'Constructible' wrapper with 'Show' and 'Arbitrary' instances+-- that operate on the representation of the argument type.+--+-- Deconstruct with the 'Construct' pattern.+--+-- This is only useful for property combinators from vanilla QuickCheck, that+-- use the original 'Testable' class instead of+-- 'Test.QuickCheck.HigherOrder.Testable'' from this library.+data Constructed a = Constructed (Repr a) a++-- | A unidirectional pattern to deconstruct 'Constructed' values.+pattern Construct :: a -> Constructed a+pattern Construct a <- Constructed _ a++-- | A smart constructor for constructible values.+mkConstructed :: Constructible a => Repr a -> Constructed a+mkConstructed r = Constructed r (fromRepr r)++instance Constructible a => Arbitrary (Constructed a) where+  arbitrary = fmap mkConstructed arbitrary+  shrink (Constructed r _) = fmap mkConstructed (shrink r)++instance Constructible a => Show (Constructed a) where+  showsPrec n (Constructed r _) = showParen (n > 10) $+    showString "Constructed " . showsPrec 11 r . showString " _"+++-- 'Constructible' instances++instance (CoArbitrary a, Function a, Show a, Constructible b) => Constructible (Fun a b) where+  type Repr (Fun a b) = Fun a (Repr b)+  fromRepr = fmap fromRepr++instance Constructible a => Constructible (Identity a) where+  type Repr (Identity a) = Repr a+  fromRepr = Identity . fromRepr++instance (Constructible a, Constructible b) => Constructible (a, b) where+  type Repr (a, b) = (Repr a, Repr b)+  fromRepr (a, b) = (fromRepr a, fromRepr b)++instance (Constructible a, Constructible b) => Constructible (Either a b) where+  type Repr (Either a b) = Either (Repr a) (Repr b)+  fromRepr (Left a) = Left (fromRepr a)+  fromRepr (Right b) = Right (fromRepr b)++instance Constructible a => Constructible (Maybe a) where+  type Repr (Maybe a) = Maybe (Repr a)+  fromRepr = fmap fromRepr++instance Constructible a => Constructible [a] where+  type Repr [a] = [Repr a]+  fromRepr = fmap fromRepr++instance Constructible Integer where fromRepr = id+instance Constructible Int where fromRepr = id+instance Constructible Word where fromRepr = id+instance Constructible Double where fromRepr = id+instance Constructible Char where fromRepr = id+instance Constructible () where fromRepr = id+instance Constructible Bool where fromRepr = id+instance Constructible Ordering where fromRepr = id++instance Constructible a => Constructible (Monoid.Sum a) where+  type Repr (Monoid.Sum a) = Monoid.Sum (Repr a)+  fromRepr = Monoid.Sum . fromRepr . Monoid.getSum
+ src/Test/QuickCheck/HigherOrder/Internal/Function.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE+    FlexibleContexts,+    MultiParamTypeClasses,+    ScopedTypeVariables,+    TypeFamilies,+    TypeOperators #-}++{-# OPTIONS_GHC -Wno-orphans #-}++-- | Representation of (higher-order) functions.++module Test.QuickCheck.HigherOrder.Internal.Function where++import Test.Fun ((:->), applyFun, shrinkFun, cogenFun, CoArbitrary(..), Concrete(..))+import Test.QuickCheck (Arbitrary(..), Gen, choose)++import Test.QuickCheck.HigherOrder.Internal.Constructible++-- * Instances for @(:->)@++concrete :: (Arbitrary a, Show a) => Concrete a+concrete = Concrete shrink showsPrec++instance (CoArbitrary Gen a, Arbitrary r) => Arbitrary (a :-> r) where+  arbitrary = coarbitrary arbitrary+  shrink = shrinkFun shrink++instance (Constructible a, CoArbitrary Gen b) => CoArbitrary Gen (a -> b) where+  coarbitrary = cogenFun concrete ga fromRepr coarbitrary where+    ga = do+      x <- choose (0, 4 :: Int)+      if x == 0 then+        pure Nothing+      else+        Just <$> arbitrary++-- * 'Constructible' instance for @(->)@++instance (CoArbitrary Gen a, Constructible b) => Constructible (a -> b) where+  type Repr (a -> b) = a :-> Repr b+  fromRepr h = fromRepr . applyFun h
+ src/Test/QuickCheck/HigherOrder/Internal/TestEq.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE StandaloneDeriving #-}++module Test.QuickCheck.HigherOrder.Internal.TestEq where++import Data.Functor.Identity+import qualified Data.Monoid as Monoid+import Test.QuickCheck++import Test.QuickCheck.HigherOrder.Internal.Constructible+import Test.QuickCheck.HigherOrder.Internal.Testable.Class++-- | Testable equality+class TestEq a where+  -- | A property that /probably/ fails if the two values are not equal.+  --+  -- @+  -- (a '=?' a)  =  'property' 'True'+  -- @+  (=?) :: a -> a -> Property++-- | Default method to convert 'Eq' (decidable equality) into 'TestEq'.+decEq :: (Eq a, Show a) => a -> a -> Property+decEq a b = a === b++infix 4 =?+++-- 'TestEq' instances++instance (Constructible a, TestEq b) => TestEq (a -> b) where+  f =? g = property' (\a -> f a =? g a)++deriving instance TestEq a => TestEq (Identity a)++instance (TestEq a, TestEq b) => TestEq (a, b) where+  (a1, b1) =? (a2, b2) =+    counterexample "(_, _) =? (_, _)   (1) ..." (a1 =? a2) .&&.+    counterexample "(_, _) =? (_, _)   (2) ..." (b1 =? b2)++instance (TestEq a, TestEq b) => TestEq (Either a b) where+  Left a1  =? Left a2  = counterexample "Left _ =? Left _   ..."   $ a1 =? a2+  Right b1 =? Right b2 = counterexample "Right _ =? Right _   ..." $ b1 =? b2+  Left _ =? Right _ = counterexample "Left _ /= Right _" $ property False+  Right _ =? Left _ = counterexample "Right _ /= Left _" $ property False++instance TestEq a => TestEq (Maybe a) where+  Just a =? Just b = counterexample "Just _ =? Just _   ..." $ a =? b+  Nothing =? Nothing = property True+  Just _ =? Nothing = counterexample "Just _ /= Nothing" $ property False+  Nothing =? Just _ = counterexample "Nothing /= Just _" $ property False++instance TestEq a => TestEq [a] where+  [] =? [] = property True+  a : as =? b : bs =+    counterexample "(_ : _) =? (_ : _)   (1) ..." (a =? b) .&&.+    counterexample "(_ : _) =? (_ : _)   (2) ..." (as =? bs)+  [] =? _ : _ = counterexample "[] /= (_ : _)" $ property False+  _ : _ =? [] = counterexample "(_ : _) /= []" $ property False++instance TestEq Integer where (=?) = decEq+instance TestEq Int where (=?) = decEq+instance TestEq Word where (=?) = decEq+instance TestEq Double where (=?) = decEq+instance TestEq Char where (=?) = decEq+instance TestEq () where (=?) = decEq+instance TestEq Bool where (=?) = decEq+instance TestEq Ordering where (=?) = decEq++deriving instance TestEq a => TestEq (Monoid.Sum a)
+ src/Test/QuickCheck/HigherOrder/Internal/Testable.hs view
@@ -0,0 +1,95 @@+-- | An alternative to Testable++module Test.QuickCheck.HigherOrder.Internal.Testable where++import Data.Traversable (for)+import Test.QuickCheck++import Test.QuickCheck.HigherOrder.Internal.Testable.Class+import Test.QuickCheck.HigherOrder.Internal.TestEq+++-- * Syntax for properties+++-- | Equation: an equals sign between two values.+data Equation a = (:=:) a a+  deriving (Eq, Ord, Show)++infix 5 :=:++instance TestEq a => Testable (Equation a) where+  property (a :=: b) = a =? b++instance TestEq a => Testable' (Equation a) where+  property' = property  -- the one defined just up there++instance Eq a => Decidable (Equation a) where+  decide (a :=: b) = a == b+++-- | Expressions denoting a logical implication.+data Implication a b = (:==>) a b++infixr 2 :==>++-- | Implication between two equations.+type EqImpl a b = Implication (Equation a) (Equation b)++-- | Just use @('==>')@.+instance (Decidable a, Testable b) => Testable (Implication a b) where+  property (a :==> b) = decide a ==> b++-- | Just use @('==>')@.+instance (Decidable a, Testable' b) => Testable' (Implication a b) where+  property' (a :==> b) = decide a ==> property' b+++-- | Decidable property.+class Decidable a where+  -- | The definition of decidability: we can compute whether a property is+  -- true.+  decide :: a -> Bool+++-- * Auxiliary functions++-- | Variant of 'quickCheck' using the alternative 'Testable''.+quickCheck' :: Testable' prop => prop -> IO ()+quickCheck' = quickCheck . property'++-- | Variant of 'quickCheckWith' using the alternative 'Testable''.+quickCheckWith' :: Testable' prop => Args -> prop -> IO ()+quickCheckWith' args = quickCheckWith args . property'++-- | A named property that should __pass__.+--+-- Use 'ok' and 'ko' to construct lists of named properties+-- @[('String', 'Property')]@, which can be run using 'quickChecks',+-- or @testProperties@ from tasty-quickcheck.+ok :: Testable' prop => String -> prop -> (String, Property)+ok s prop = (s, property' prop)++-- | A named property that should __fail__.+--+-- See also 'ok'.+ko :: Testable' prop => String -> prop -> (String, Property)+ko s = ok s . expectFailure . property'++-- | Execute a list of named properties.+quickChecks :: [(String, Property)] -> IO Bool+quickChecks ps =+  fmap and . for ps $ \(name, p) -> do+    putStrLn ("=== " ++ name ++ " ===")+    r <- quickCheckResult p+    putStrLn ""+    return $ case r of+      Success{} -> True+      Failure{} -> False+      NoExpectedFailure{} -> False+      GaveUp{} -> False++-- Decidable instances++instance Decidable Bool where+  decide = id
+ src/Test/QuickCheck/HigherOrder/Internal/Testable/Class.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++module Test.QuickCheck.HigherOrder.Internal.Testable.Class where++import Test.QuickCheck++import Test.QuickCheck.HigherOrder.Internal.Constructible++-- * An alternative Testable++-- | Types that represent testable properties.+--+-- This is a clone of the 'Testable' class with an improved function instance.+class Testable' prop where+  property' :: prop -> Property++-- * Helpers++-- | Equivalent to 'property'' specialized to functions:+-- convert a function to a 'Property'.+forAll_+  :: forall a prop+  .  (Constructible a, Testable' prop)+  => (a -> prop) -> Property+forAll_ f =+  forAllShrinkShow+    (arbitrary @(Repr a))+    (shrink    @(Repr a))+    (show      @(Repr a))+    (property' . f . fromRepr)++-- | A 'Property' is the canonical type of testable properties.+--+-- > property' @Property = property @Property = id+instance Testable' Property where+  property' = id++instance Testable' Bool where+  property' = property++-- | A generator represents a universally quantified property.+instance Testable' a => Testable' (Gen a) where+  property' = property . fmap property'++-- | A function represents a universally quantified property.+instance (Constructible a, Testable' b) => Testable' (a -> b) where+  property' = forAll_+
+ test/sample.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE+    FlexibleContexts,+    TypeApplications,+    TypeOperators #-}++module Main where++import Data.Foldable (for_)++import Test.QuickCheck (Gen, Arbitrary, arbitrary, sample')++import Test.Fun+import Test.Fun.Internal.Types (truncateFun)++import Test.QuickCheck.HigherOrder ()++genFun :: (CoArbitrary Gen a, Arbitrary b) => Gen (a :-> b)+genFun = coarbitrary arbitrary++sample_ :: Show a => Gen a -> IO ()+sample_ g = do+  xs <- sample' g+  for_ xs $ \x -> putStrLn (indent (show x))++main :: IO ()+main = do+  sample_ (truncateFun 4 id 33 <$> genFun @(Int -> Int) @Int)+  sample_ (truncateFun 4 id 33 <$> genFun @(Int -> Either () ()) @Int)+  -- sample_ (truncateFun 10 id undefined <$> genFun @Bool @Bool)+  -- sample_ (truncateFun 100 id undefined <$> genFun @(Bool -> Bool) @Bool)
+ test/test.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE TypeApplications, TypeOperators #-}++module Main where++import Test.Tasty+import Test.Tasty.QuickCheck (Property, (===), testProperty, expectFailure)++import Test.QuickCheck.HigherOrder++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "tests"+  [ testFunction+  ]++testFunction :: TestTree+testFunction = testGroup "Function"+  [ testFunctionQC+  ]++trivialProperty :: (Eq a, Show a) => ((a -> a) -> a) -> Property+trivialProperty f = f id === f id++badProperty :: (Eq a, Show a) => ((a -> a) -> a) -> (a -> a) -> Property+badProperty f g = expectFailure (f id === f g)++testFunctionQC :: TestTree+testFunctionQC = testGroup "qc"+  [ testProperty "trivial-Int"    (property' (trivialProperty @Int))+  , testProperty "trivial-Bool"   (property' (trivialProperty @Bool))+  , testProperty "trivial-Either" (property' (trivialProperty @(Either () ())))+  , testProperty "bad-Int"    (property' (badProperty @Int))+  , testProperty "bad-Bool"   (property' (badProperty @Bool))+  , testProperty "bad-Either" (property' (badProperty @(Either () ())))+  ]