自动为SBV中的记录推导出可预测的Provable

时间:2016-10-11 11:25:58

标签: haskell sbv

我的情况是我有像

这样的数据类型
data X = X {foo :: SInteger, bar :: SInteger}

我希望证明这一点。

forAll_ $ \x -> foo x + bar x .== bar x + foo x

使用haskell的sbv。 这不会编译,因为X -> SBool不是Provable的实例。我可以用例如

作为实例
instance (Provable p) => Provable (X -> p) where
  forAll_ k = forAll_ $ \foo bar -> forAll_ $ k $ X foo bar
  forAll (s : ss) k =
    forAll ["foo " ++ s, "bar " ++ s] $ \foo bar -> forAll ss $ k $ X foo bar
  forAll [] k = forAll_ k
  -- and similarly `forSome_` and `forSome`

但这很乏味且容易出错(例如,当forSome应该被使用时使用forAll。有没有办法自动为我的类型派生Provable

2 个答案:

答案 0 :(得分:2)

至少可以减少错误:

onX :: (((SInteger, SInteger) -> a) -> b) -> ((X -> a) -> b)
onX f g = f (g . uncurry X)

instance Provable p => Provable (X -> p) where
    forAll_  = onX forAll_
    forSome_ = onX forSome_
    forAll   = onX . forAll
    forSome  = onX . forSome

如果SBV现有的多达7元组的实例不足够,那么这也是一种可推广的模式。

data Y = Y {a, b, c, d, e, f, g, h, i, j :: SInteger}
-- don't try to write the types of these, you will wear out your keyboard
fmap10 = fmap . fmap . fmap . fmap . fmap . fmap . fmap . fmap . fmap . fmap
onY f g = f (fmap10 g Y)

instance Provable p => Provable (Y -> p) where
    forAll_  = onY forAll_
    forSome_ = onY forSome_
    forAll   = onY . forAll
    forSome  = onY . forSome

但仍然很乏味。

答案 1 :(得分:0)

如果你真的想直接在你的lambda表达式中使用量词,那么Daniel的回答是“尽可能好”。但是,我强烈建议您为您的类型定义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

请注意,这可以在provesat上下文中正常运行,因为free知道在每种情况下如何正确行事。

我认为这更易读,更易于使用,即使它会强制您使用该功能。您还可以创建一个接受名称的版本,如下所示:

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

您还可以通过SBV使X“可解析”。在这种情况下,完整代码如下所示:

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

现在,您可以按照自己的意愿拍摄反例并进行后期处理。