Daniel's answer is "as good as it gets" if you really want to use quantifiers directly with your lambda-expressions. However, instead of creating a Provable
instance, I'd strongly recommend defining a variant of free
for your type:
freeX :: Symbolic X
freeX = do f <- free_
b <- free_
return $ X f b
Now you can use it like this:
test = prove $ do x <- freeX
return $ foo x + bar x .== bar x + foo x
This is much easier to use, and composes well with constraints. For instance, if your data type has the extra constraint that both components are positive, and the first one is larger than the second, then you can write freeX
thusly:
freeX :: Symbolic X
freeX = do f <- free_
b <- free_
constrain $ f .> b
constrain $ b .> 0
return $ X f b
Note that this will work correctly in both prove
and sat
contexts, since free
knows how to behave correctly in each case.
I think this is much more readable and easier to use, even though it forces you to use the do-notation. You can also create a version that accepts names, like this:
freeX :: String -> Symbolic X
freeX nm = do f <- free $ nm ++ "_foo"
b <- free $ nm ++ "_bar"
constrain $ f .> b
constrain $ b .> 0
return $ X f b
test = prove $ do x <- freeX "x"
return $ foo x + bar x .== bar x * foo x
Now we get:
*Main> test
Falsifiable. Counter-example:
x_foo = 3 :: Integer
x_bar = 1 :: Integer
You can also make X
"parseable" by SBV. In this case the full code looks like this:
data X = X {foo :: SInteger, bar :: SInteger} deriving Show
freeX :: Symbolic X
freeX = do f <- free_
b <- free_
return $ X f b
instance SatModel X where
parseCWs xs = do (x, ys) <- parseCWs xs
(y, zs) <- parseCWs ys
return $ (X (literal x) (literal y), zs)
The following test demonstrates:
test :: IO (Maybe X)
test = extractModel `fmap` (prove $ do
x <- freeX
return $ foo x + bar x .== bar x * foo x)
We have:
*Main> test >>= print
Just (X {foo = -4 :: SInteger, bar = -5 :: SInteger})
Now you can take your counter-examples and post-process them as you wish.