packages feed

quickspec 0.9.5 → 0.9.6

raw patch · 12 files changed

+540/−59 lines, 12 filesdep −mtl

Dependencies removed: mtl

Files

− README
@@ -1,1 +0,0 @@-cabal install and look at the examples directory.
+ README.asciidoc view
@@ -0,0 +1,410 @@+:replacements.DOCS: http://hackage.haskell.org/package/quickspec-0.9.5/docs/Test-QuickSpec.html+:replacements.PAPER: http://www.cse.chalmers.se/~nicsma/papers/quickspec.pdf+:replacements.FUN: http://hackage.haskell.org/package/quickspec-0.9.5/docs/Test-QuickSpec.html#v:+:replacements.TYPE: http://hackage.haskell.org/package/quickspec-0.9.5/docs/Test-QuickSpec.html#t:+:replacements.EXAMPLE: link:examples/++QuickSpec: equational laws for free!+====================================++Ever get that nagging feeling that your code must satisfy some+algebraic properties, but not sure what they are? Want to write some+QuickCheck properties, but not sure where to start? QuickSpec might be+for you! Give it your program -- QuickSpec will find the laws it obeys.++QuickSpec takes any hodgepodge of functions, and tests those functions+to work out the relationship between them. It then spits out what it+discovered as a list of equations.++Give QuickSpec `reverse`, `++` and `[]`, for example, and it will find+six laws:++------------------------------------------------+xs++[] == xs+[]++xs == xs+(xs++ys)++zs == xs++(ys++zs)+reverse [] == []+reverse (reverse xs) == xs+reverse xs++reverse ys == reverse (ys++xs)+------------------------------------------------++All the laws you would expect to hold, and nothing more -- and all+discovered automatically! Brill!++Where's the catch? While QuickSpec is pretty nifty, it isn't magic,+and has a number of limitations:++* QuickSpec can only discover _equations_, not other kinds of laws.+  Luckily, equations cover a lot of what you would normally want to+  say about Haskell programs. Often, even if a law you want isn't+  equational, QuickSpec will discover equational special cases of that+  law which suggest the general case.+* You have to tell QuickSpec exactly which functions and constants it+  should consider when generating laws. In the example above, we gave+  `reverse`, `++` and `[]`, and those are the _only_ functions that+  appear in the six equations. For example, we don't get the equation+  `(x:xs)++ys == x:(xs++ys)`, because we didn't include +:+ in the+  functions we gave to QuickSpec. A large part of using QuickSpec+  effectively is choosing which functions to consider in laws.+* QuickSpec exhaustively enumerates terms, so it will only discover+  equations about small(ish) terms -- in fact, terms up to a fixed+  depth. You can adjust the maximum depth but, as QuickSpec exhaustively+  enumerates terms, there is an exponential blowup as you increase the+  depth. Likewise, there is an exponential blowup as you give QuickSpec+  more functions to consider (though it doesn't blow up as badly as+  you might think!)+* QuickSpec only tests the laws, it doesn't try to prove them.+  So while the generated laws are very likely to be true, there is+  still a chance that they are false, especially if your test data+  generation is not up to scratch.++Despite these limitations, QuickSpec works well on many examples.++The rest of this +README+ introduces QuickSpec through a couple of short examples.+You can look at the bottom of this file for links to more examples, Haddock documentation and our paper about QuickSpec.++Installing+----------++Install QuickSpec in the usual way -- `cabal install quickspec`.++Booleans -- the basics+----------------------++Let's start by testing some boolean operators.++To run QuickSpec, we must define a _signature_, which specifies which+functions we want to test, together with the variables that can appear+in the generated equations. Here is our signature:++[source,haskell]+------------------------------------------------+bools = [+  ["x", "y", "z"] `vars` (undefined :: Bool),++  "||"    `fun2` (||),+  "&&"    `fun2` (&&),+  "not"   `fun1` not,+  "True"  `fun0` True,+  "False" `fun0` False]+------------------------------------------------++In the signature, we define three variables (+x+, +y+ and +z+) of type++Bool+, using the FUNvars[`vars`] combinator, which takes two+parameters: a list of variable names, and the type we want those+variables to have. We also give give QuickSpec the functions +||+,++&&+, +not+, +True+ and +False+, using the+FUNfun0[`fun0`]/FUNfun1[`fun1`]/FUNfun2[`fun2`] combinators. These+take two parameters: the name of the function, and the function+itself. The integer, +0+, +1+ or +2+ here, is the arity of the+function.++Having written this signature, we can invoke QuickSpec just by calling+the function FUNquickSpec[`quickSpec`]:++[source,haskell]+------------------------------------------------+import Test.QuickSpec hiding (bools)+main = quickSpec bools+------------------------------------------------++You can find this code in EXAMPLEBools.hs[examples/Bools.hs] in+the QuickSpec distribution. Go on, run it! (Compile it or else it'll go slow.)+You will see that QuickSpec prints out:++1. The signature it's testing, i.e. the types of all functions and+   variables. If something fishy is happening, check that the+   functions and types match up with what you expect! QuickSpec will+   also print a warning here if something seems fishy about the+   signature, e.g. if there are no variables of a certain type.+2. A summary of how much testing it did.+3. The equations it found -- the exciting bit!+   The equations are grouped according to which function they+   talk about, with equations that relate several functions at the end.++Peering through what QuickSpec found, you should see the familiar laws+of Boolean algebra. The only oddity is the equation +x||(y||z) ==+y||(x||z)+. This is QuickSpec's rather eccentric way of expressing+that +||+ is associative -- in the presence of the law +x||y == y||x+,+it's equivalent to associativity, and QuickSpec happens to choose this+formulation rather than the more traditional one. All the other laws+are just as we would expect, though. Not bad for 5 minutes' work!++Lists -- polymorphic functions and the prelude+----------------------------------------------++Now let's try testing some list functions -- perhaps just `reverse`,+`++` and `[]`. We might start by writing a signature by analogy with+the earlier booleans example:++[source,haskell]+----+lists = [+  ["xs", "ys", "zs"] `vars` (undefined :: [a]),++  "[]"      `fun0` [],+  "reverse" `fun1` reverse,+  "++"      `fun2` (++)]+----++Unfortunately, QuickSpec only supports _monomorphic_ functions. The+functions and variables in the `lists` signature are polymorphic,+and GHC complains:++----+No instance for (Arbitrary a0) arising from a use of `vars'+The type variable `a0' is ambiguous+----++The solution is to monomorphise the signature ourselves. QuickSpec+provides types called TYPEA[`A`], TYPEB[`B`] and TYPEC[`C`] for that+purpose, so we simply specialise all type variables to TYPEA[`A`]:++[source,haskell]+----+lists = [+  ["xs", "ys", "zs"] `vars` (undefined :: [A]),++  "[]"      `fun0` ([] :: [A]),+  "reverse" `fun1` (reverse :: [A] -> [A]),+  "++"      `fun2` ((++) :: [A] -> [A] -> [A])]+----++Having done that, we get the six laws from the beginning of this file.++Perhaps we now decide we want laws about `length` too. We want to keep+our existing list functions in the signature, so that we get laws+relating them to `length`, but on the other hand we only want to see+new laws, i.e. the ones that mention `length`. We can do this by+marking the existing functions as _background functions_, and the+resulting signature looks as follows:++[source,haskell]+----+lists = [+  ["xs", "ys", "zs"] `vars` (undefined :: [A]),++  background [+    "[]"      `fun0` ([] :: [A]),+    "reverse" `fun1` (reverse :: [A] -> [A]),+    "++"      `fun2` ((++) :: [A] -> [A] -> [A])],+  "length" `fun1` (length :: [A] -> Int)]+----++QuickSpec will only print an equation if it involves at least one+non-background function, in this case `length`. Running QuickSpec+again we get the following two laws:++----+length (reverse xs) == length xs+length (xs++ys) == length (ys++xs)+----++The first equation is all very well and good, but the second one is a+bit unsatisfying. Wouldn't we rather get+`length (xs++ys) = length xs + length ys`? To get that equation, we need to add+`(+) :: Int -> Int -> Int` to the signature. Adding it as a background+function gives us the law we want.++You often need a wide variety of background functions to get good+equations out of QuickSpec, and it gets a bit tedious declaring them+all by hand. To help you with this QuickSpec provides a _prelude_, a+predefined set of background functions which you can import into your+own signature. The prelude is very minimal, but includes basic boolean,+arithmetic and list functions. We can write our lists signature using+the prelude as follows:++[source,haskell]+----+lists = [+  prelude (undefined :: A) `without` ["[]", ":"],++  background [+    "reverse" `fun1` (reverse :: [A] -> [A])],+  "length" `fun1` (length :: [A] -> Int)]+----++A call to FUNprelude[`prelude`] +(undefined :: a)+ will declare the following+background functions:+  * The boolean connectives `||`, `&&`, `not`, `True` and `False`.+  * The arithmetic operations `0`, `1`, `+` and `*` over type `Int`.+  * The list operations `[]`, `:`, `++`, `head` and `tail` over type `[a]`.+  * Three variables each of type `Bool`, `Int`, `a` and `[a]`.++In the example above we used the FUNwithout[`without`] combinator to+leave out `[]` and `:` from the prelude, so as to get fewer laws.+QuickSpec also provides the combinators FUNbools[`bools`],+FUNarith[`arith`] and FUNlists[`lists`], which import only their+respective part of the prelude, for when you want more control -- see+the DOCS[documentation] for more information.++In EXAMPLELists.hs[Lists.hs] you can find an extended version+of the above example which also tests `map`.++Advanced: function composition -- testing types with no `Ord` instance+----------------------------------------------------------------------++WARNING: this section isn't finished.++IMPORTANT: You can skip this section unless you need to test a type+with no `Ord` instance.++Suppose we want to get QuickSpec to discover the laws of function+composition -- things like `id . f == f`.++If we just define a signature containing `id` and `(.)` (and suitable+variables), the output is rather disappointing:++----+(f . g) x == f (g x)+id x == x+----++This is because QuickSpec is giving us laws about _fully saturated_+applications of `(.)` and `id`, that is, `(.)` applied to three+arguments and `id` applied to one argument. In the laws we are after,+we only want to apply `(.)` to two arguments, and we don't want to+apply `id` to an argument at all. To fix this we can declare `(.)`+to have arity 2 and `id` to have arity 1, so that QuickSpec won't+fully apply them:++----+composition = [+  vars ["f", "g", "h"] (undefined :: A -> A),+  fun2 "."   ((.) :: (A -> A) -> (A -> A) -> (A -> A)),+  fun0 "id"  (id  :: A -> A),+  ]+----++Unfortunately, we get the following error message:++----+Could not deduce (Ord (A -> A)) arising from a use of `fun2'+----++To test a law like `id . f == f`, QuickSpec generates a random value+for `f` and then just evaluates the expression `id . f == f` to get+either `True` or `False`.++The error message complains that we are trying to generate laws about+terms of the type `A -> A` (i.e. functions), but as there is no `Ord`+instance for functions QuickSpec has no way of testing the laws.+QuickSpec tests a law like `id . f == f` by generating random values+for `f` and seeing if the resulting left-hand side and right-hand side+evaluate to the same value; it can only do this if it has an `Ord`+instance for the values in question. As there is no way to tell if+two functions are equal, it seems we are stuck!++Hang on, though. We can still _test_ if two functions are equal:+generate a random argument and apply the two functions to it, and see+if they both give the same result. If they don't, they're certainly+not equal. Repeat the process a few times, for several random+arguments, and if both functions always seem to give the same result+then they're probably equal.++++This is a common situation -- we have a type, we cannot directly+compare values of that type, but we can make random _observations_+and compare those. For our example, observing a function consists+of applying the function to a random argument. QuickSpec supports+finding equations over types that you can observe. The+observations must satisfy the following properties:++* The observation returns a value of a type that we can directly+  compare for equality.+* If two values are different, there is an observation that+  distinguishes them.+* If an observation distinguishes two values, they are not equal.++++Common pitfalls+---------------++WARNING: this section isn't finished.++*I get laws which seem to be false!*+If a law really is false, it means that QuickCheck didn't discover the+counterexample to it. Possible solutions include:++  * Improve the test data generation. If you can't change the+    Arbitrary` instance for your type, you can use the+    FUNgvars[`gvars`] combinator, which is like FUNvars[`vars`]+    but allows you to specify the generator.+  * If you are testing a polymorphic function, try instantiating it+    with the QuickSpec type TYPETwo[`Two`] instead of TYPEA[`A`].+    TYPETwo[`Two`] is a type that has only two elements, which may+    make it easier to hit counterexamples.+  * Use the FUNwithTests[`withTests`] combinator to increase the+    number of tests.++*QuickSpec runs for a very long time without terminating!*+QuickSpec works by enumerating all terms up to a certain depth,+and therefore suffers from exponential blowup. Check the output+where it reports how many terms it generated:++----+== Testing ==+Depth 1: 6 terms, 4 tests, 18 evaluations, 6 classes, 0 raw equations.+Depth 2: 61 terms, 500 tests, 28568 evaluations, 15 classes, 46 raw equations.+Depth 3: 412 terms, 500 tests, 205912 evaluations, 53 classes, 359 raw equations.+----++Here it's generated 412 terms. If the number gets much above 100,000+then you will probably run into trouble. This can be caused by one of+several things:+  * Too many functions in the signature.++*I only get ground instances of the laws I want!*++Perhaps you forgot to add++no variables++*Law not found*++Is it true? Is it provable? Are all necessary functions in the signature?+Do the types match up so that the term is well-typed?++*Get false laws*++Tweak test data generators++*Exponential blowup*++*I want to test a datatype with no `Ord` instance, such as functions*++see function composition+++++A common mistake when using QuickSpec is to forget to define any+variables of a certain type. In that case, you will typically get lots+of special cases instead of the law you really want. For example,++----+True||True == True+True||False == True+False||True == True+False||False == False+----++Where to go from here?+--------------------++Have a look at the examples that come with QuickSpec:++* link:examples/Bools.hs[Booleans]+* link:examples/Arith.hs[Arithmetic]+* link:examples/Lists.hs[List functions]+* link:examples/Heaps.hs[Binary heaps]+* link:examples/Composition.hs[Function composition]+* link:examples/Arrays.hs[Arrays]+* link:examples/TinyWM.hs[A tiny window manager]+* link:examples/PrettyPrinting.hs[Pretty-printing combinators]++Read our PAPER[paper].++Read the DOCS[Haddock documentation] for things to tweak.
+ examples/Arrays.hs view
@@ -0,0 +1,44 @@+-- Arrays.++{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, DeriveDataTypeable #-}+import Test.QuickCheck+import Test.QuickSpec+import Data.Typeable+import Data.Array++put :: Ix i => i -> a -> Array i a -> Array i a+put ix v arr = arr // [(ix, v)]++arrays :: forall a. (Typeable a, Ord a, Arbitrary a) => a -> [Sig]+arrays a = [+  -- Don't include head, or functions on natural numbers---they+  -- generate too many irrelevant terms.+  prelude (undefined :: a) `without` ["head", "*", "0", "1"],+  lists (undefined :: Int) `without` ["head"],++  ["x", "y", "z"] `vars` (undefined :: a),+  ["a"]           `vars` (undefined :: Array Int a),+  -- Generate ranges using a custom generator to improve test data+  -- distribution.+  ["r"]           `gvars` genRange,++  "!"             `fun2` ((!)       :: Array Int a -> Int -> a),+  "put"           `fun3` (put       :: Int -> a -> Array Int a -> Array Int a),+  "listArray"     `fun2` (listArray :: (Int, Int) -> [a] -> Array Int a),+  "elems"         `fun1` (elems     :: Array Int a -> [a]),+  "indices"       `fun1` (indices   :: Array Int a -> [Int])]++instance Arbitrary a => Arbitrary (Array Int a) where+  arbitrary = do+    (low, high) <- genRange+    elems <- arbitrary :: Gen (Int -> Maybe a)+    return (array (low, high) [(i, x) | i <- [low..high], Just x <- [elems i]])++genRange :: Gen (Int, Int)+genRange = do+  low <- choose (-2, 2)+  high <- fmap (low +) (choose (-1, 2))+  return (low, high)++-- Use Two instead of A to improve the chance of getting the right test data.+main = quickSpec (arrays (undefined :: Two))
examples/Lists.hs view
@@ -7,19 +7,11 @@ lists :: forall a. (Typeable a, Ord a, Arbitrary a, CoArbitrary a) =>          a -> [Sig] lists a = [-  arith (undefined :: Int),+  prelude (undefined :: a) `without` ["++"],   funs (undefined :: a), -  ["x", "y", "z"] `vars` (undefined :: a),-  ["xs", "ys", "zs"] `vars` (undefined :: [a]),--  background [-  "[]"      `fun0` ([]      :: [a]),-  ":"       `fun2` ((:)     :: a -> [a] -> [a])],--  "head"    `fun1` (head    :: [a] -> a),-  "tail"    `fun1` (tail    :: [a] -> [a]),   "unit"    `fun1` (return  :: a -> [a]),+  -- Don't take ++ from the prelude because we want to see laws about it   "++"      `fun2` ((++)    :: [a] -> [a] -> [a]),   "length"  `fun1` (length  :: [a] -> Int),   "reverse" `fun1` (reverse :: [a] -> [a]),
+ examples/PrettyPrinting.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables #-}+module Main where++import Control.Monad+import Data.Typeable+import Test.QuickCheck+import Test.QuickSpec++newtype Layout a = Layout [(Int, [a])] deriving (Typeable, Eq, Ord, Show)++instance Arbitrary a => Arbitrary (Layout a) where+  arbitrary = fmap Layout (liftM2 (:) arbitrary arbitrary)++text :: [a] -> Layout a+text s = Layout [(0, s)]++nest :: Int -> Layout a -> Layout a+nest k (Layout l) = Layout [(i+k, s) | (i, s) <- l]++($$) :: Layout a -> Layout a -> Layout a+Layout xs $$ Layout ys = Layout (xs ++ ys)++(<>) :: Layout a -> Layout a -> Layout a+Layout xs <> Layout ys = f (init xs) (last xs) (head ys) (tail ys)+  where f xs (i, s) (j, t) ys = Layout xs $$ Layout [(i, s ++ t)] $$ nest (i + length s - j) (Layout ys)++pretty :: forall a. (Typeable a, Ord a, Arbitrary a) => a -> [Sig]+pretty a = [+  ["d","e","f"] `vars` (undefined :: Layout a),+  ["s","t","u"] `vars` (undefined :: [a]),+  ["n","m","o"] `vars` (undefined :: Int),+  "text" `fun1` (text :: [a] -> Layout a),+  "nest" `fun2` (nest :: Int -> Layout a -> Layout a),+  "$$" `fun2` (($$) :: Layout a -> Layout a -> Layout a),+  "<>" `fun2` ((<>) :: Layout a -> Layout a -> Layout a),+  background [+    "[]" `fun0` ([] :: [a]),+    "++" `fun2` ((++) :: [a] -> [a] -> [a]),+    "0" `fun0` (0 :: Int),+    "length" `fun1` (length :: [a] -> Int),+    "+" `fun2` ((+) :: Int -> Int -> Int)]]++main = quickSpec (pretty (undefined :: Two))
examples/TinyWM.hs view
@@ -1,8 +1,3 @@--- This example requires QuickCheck >= 2.5. For older versions, you will--- have to define an Arbitrary Ordering instance, like so:---   instance Arbitrary Ordering where---     arbitrary = elements [LT, EQ, GT]- -- A window manager example, -- taken from http://donsbot.wordpress.com/2007/05/01/roll-your-own-window-manager-part-1-defining-and-testing-a-model 
quickspec.cabal view
@@ -1,6 +1,6 @@ Name:                quickspec-Version:             0.9.5-Cabal-version:       >=1.6+Version:             0.9.6+Cabal-version:       >= 1.6 Build-type:          Simple  Homepage:            https://github.com/nick8325/quickspec@@ -13,47 +13,37 @@  Category:            Testing -Synopsis:            Equational laws for free+Synopsis:            Equational laws for free! Description:-  QuickSpec automatically finds equational properties of your program.+  QuickSpec automatically finds equational laws about your program.   .   Give it an API, i.e. a collection of functions, and it will spit out   equations about those functions. For example, given @reverse@, @++@-  and @[]@, QuickSpec finds six laws:+  and @[]@, QuickSpec finds six laws, which are exactly the ones you+  might write by hand:   .   > xs++[] == xs   > []++xs == xs-  > reverse [] == []   > (xs++ys)++zs == xs++(ys++zs)+  > reverse [] == []   > reverse (reverse xs) == xs   > reverse xs++reverse ys == reverse (ys++xs)   .-  All you have to provide is:-  .-  * Some functions and constants to test. These are the /only/-    functions that will appear in the equations.-  .-  * A collection of variables that can appear in the equations-    (@xs@, @ys@ and @zs@ in the example above).-  .-  * 'Test.QuickCheck.Arbitrary' and 'Data.Typeable.Typeable' instances for the types you want to test.-  .-  Consider this a pre-release. Everything is complete but undocumented-  :) The best place to start is the examples at-  <http://github.com/nick8325/quickspec/tree/master/examples>. There-  is also a paper at-  <http://www.cse.chalmers.se/~nicsma/quickspec.pdf>.-  Everything you need should be in the module "Test.QuickSpec".+  The laws that QuickSpec generates are not proved correct, but have+  passed at least 200 QuickCheck tests.   .-  If you want help, email me!+  For more information, see the @README@ file at+  https://github.com/nick8325/quickspec/blob/master/README.asciidoc.  Extra-source-files:-  README+  README.asciidoc   examples/Arith.hs+  examples/Arrays.hs   examples/Bools.hs   examples/Composition.hs   examples/Heaps.hs   examples/Lists.hs+  examples/PrettyPrinting.hs   examples/TinyWM.hs   src/Test/QuickSpec/errors.h @@ -91,4 +81,4 @@    Build-depends:     base < 5, containers, transformers, QuickCheck >= 2.7,-    random, spoon >= 0.2, array, ghc-prim, mtl+    random, spoon >= 0.2, array, ghc-prim
src/Test/QuickSpec.hs view
@@ -1,8 +1,9 @@ -- | The main QuickSpec module. ----- This will not make sense if you haven't seen some examples!--- Look at <http://github.com/nick8325/quickspec/tree/master/examples>,--- or read the paper at <http://www.cse.chalmers.se/~nicsma/quickspec.pdf>.+-- Look at the introduction (<https://github.com/nick8325/quickspec/blob/master/README.asciidoc>),+-- read the examples (<http://github.com/nick8325/quickspec/tree/master/examples>),+-- or read the paper (<http://www.cse.chalmers.se/~nicsma/quickspec.pdf>)+-- before venturing in here.  module Test.QuickSpec   (-- * Running QuickSpec
src/Test/QuickSpec/Approximate.hs view
@@ -10,7 +10,7 @@ import Test.QuickSpec.Utils import Test.QuickSpec.Utils.Typeable import Control.Monad-import Control.Monad.Reader+import Control.Monad.Trans.Reader import Control.Spoon import System.Random import Data.Monoid@@ -61,7 +61,7 @@     plug x = frequency [(1, undefined), (3, x)]  pvars :: (Ord a, Partial a) => [String] -> a -> Sig-pvars xs w = pobserver w `mappend` primVars0 0 xs (PGen g g')+pvars xs w = pobserver w `mappend` primVars0 0 (zip xs (repeat (PGen g g')))   where     g = arbitrary `asTypeOf` return w     g' = g >>= genPartial
src/Test/QuickSpec/Prelude.hs view
@@ -86,6 +86,8 @@ -- Contains boolean, arithmetic and list functions, -- and some variables. -- Instantiate it as e.g. @prelude (undefined :: `A`)@.+-- For more precise control over what gets included,+-- see 'bools', 'arith', 'lists', 'funs' and 'without'. prelude :: (Typeable a, Ord a, Arbitrary a) => a -> Sig prelude a = background [   ["x", "y", "z"] `vars` a,
src/Test/QuickSpec/Reasoning/PartialEquationalReasoning.hs view
@@ -13,13 +13,14 @@ import Test.QuickSpec.Reasoning.NaiveEquationalReasoning(EQ, evalEQ, runEQ) import Data.IntMap(IntMap) import qualified Data.IntMap as IntMap-import Control.Monad.State-import qualified Control.Monad.State as S+import Control.Monad.Trans.State+import qualified Control.Monad.Trans.State as S import Data.List import Data.Ord import Test.QuickSpec.Utils import Test.QuickSpec.Signature hiding (vars) import Data.Monoid+import Control.Monad  data PEquation = Precondition :\/: Equation type Precondition = [Symbol]
src/Test/QuickSpec/Signature.hs view
@@ -372,19 +372,19 @@   where sig' = signature sig         silence1 x = x { silent = True } -primVars0 :: forall a. Typeable a => Int -> [String] -> PGen a -> Sig-primVars0 n xs g = variableSig [ Variable (Atom (symbol x n (undefined :: a)) g) | x <- xs ]-             `mappend` totalSig (totalGen g)-             `mappend` partialSig (partialGen g)+primVars0 :: forall a. Typeable a => Int -> [(String, PGen a)] -> Sig+primVars0 n xs = variableSig [ Variable (Atom (symbol x n (undefined :: a)) g) | (x, g) <- xs ]+             `mappend` mconcat [ totalSig (totalGen g) | (_, g) <- xs ]+             `mappend` mconcat [ partialSig (partialGen g) | (_, g) <- xs ]              `mappend` typeSig (undefined :: a) -primVars1 :: forall a b. (Typeable a, Typeable b) => Int -> [String] -> PGen (a -> b) -> Sig-primVars1 n xs g = primVars0 n xs g+primVars1 :: forall a b. (Typeable a, Typeable b) => Int -> [(String, PGen (a -> b))] -> Sig+primVars1 n xs = primVars0 n xs              `mappend` typeSig (undefined :: a)              `mappend` typeSig (undefined :: b) -primVars2 :: forall a b c. (Typeable a, Typeable b, Typeable c) => Int -> [String] -> PGen (a -> b -> c) -> Sig-primVars2 n xs g = primVars1 n xs g+primVars2 :: forall a b c. (Typeable a, Typeable b, Typeable c) => Int -> [(String, PGen (a -> b -> c))] -> Sig+primVars2 n xs = primVars1 n xs              `mappend` typeSig (undefined :: b)              `mappend` typeSig (undefined :: c) @@ -393,14 +393,18 @@ -- @gvars xs (arbitrary :: Gen a)@ is the same as -- @vars xs (undefined :: a)@. gvars, gvars0 :: forall a. Typeable a => [String] -> Gen a -> Sig-gvars xs g = primVars0 0 xs (pgen g)+gvars xs g = primVars0 0 (zip xs (repeat (pgen g))) gvars0 = gvars  gvars1 :: forall a b. (Typeable a, Typeable b) => [String] -> Gen (a -> b) -> Sig-gvars1 xs g = primVars1 1 xs (pgen g)+gvars1 xs g = primVars1 1 (zip xs (repeat (pgen g)))  gvars2 :: forall a b c. (Typeable a, Typeable b, Typeable c) => [String] -> Gen (a -> b -> c) -> Sig-gvars2 xs g = primVars2 2 xs (pgen g)+gvars2 xs g = primVars2 2 (zip xs (repeat (pgen g)))++-- | For Hipsters only :)+gvars' :: forall a. Typeable a => [(String, Gen a)] -> Sig+gvars' xs = primVars0 0 [ (x, pgen g) | (x, g) <- xs ]  -- | Declare a set of variables of a particular type. --