如果您真的想直接在 lambda 表达式中使用量词,丹尼尔的回答是“尽其所能”。Provable
但是,我强烈建议free
您为您的类型定义一个变体,而不是创建一个实例:
freeX :: Symbolic X
freeX = do f <- free_
b <- free_
return $ X f b
现在你可以像这样使用它:
test = prove $ do x <- freeX
return $ foo x + bar x .== bar x + foo x
这更容易使用,并且与约束很好地组合在一起。例如,如果您的数据类型具有两个组件都是正数的额外约束,并且第一个大于第二个,那么您可以这样编写freeX
:
freeX :: Symbolic X
freeX = do f <- free_
b <- free_
constrain $ f .> b
constrain $ b .> 0
return $ X f b
请注意,这将在prove
和sat
上下文中正常工作,因为free
知道如何在每种情况下正确表现。
我认为这更具可读性和更易于使用,即使它强制您使用 do-notation。您还可以创建一个接受名称的版本,如下所示:
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
现在我们得到:
*Main> test
Falsifiable. Counter-example:
x_foo = 3 :: Integer
x_bar = 1 :: Integer
您还可以X
通过 SBV 使“可解析”。在这种情况下,完整的代码如下所示:
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)
以下测试演示:
test :: IO (Maybe X)
test = extractModel `fmap` (prove $ do
x <- freeX
return $ foo x + bar x .== bar x * foo x)
我们有:
*Main> test >>= print
Just (X {foo = -4 :: SInteger, bar = -5 :: SInteger})
现在,您可以根据需要对反例进行后处理。