Haskell SBV中记录上谓词的可证明性自动推导

Haskell SBV中记录上谓词的可证明性自动推导,haskell,sbv,Haskell,Sbv,我的情况是我有一个数据类型 data X = X {foo :: SInteger, bar :: SInteger} 我想证明一下 forAll_ $ \x -> foo x + bar x .== bar x + foo x 用哈斯克尔的。 这不会编译,因为X->SBool不是Provable的实例。我可以举例说明 instance (Provable p) => Provable (X -> p) where forAll_ k = forAll_ $ \foo

我的情况是我有一个数据类型

data X = X {foo :: SInteger, bar :: SInteger}
我想证明一下

forAll_ $ \x -> foo x + bar x .== bar x + foo x
用哈斯克尔的。 这不会编译,因为
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`

但这很繁琐且容易出错(例如,当应使用
forAll
时,使用
forSome
)。是否有一种方法可以自动为我的类型导出
可证明的

至少可以使其不易出错:

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
但是仍然很乏味。

如果你真的想在lambda表达式中直接使用量词,Daniel的答案是“尽可能好”。但是,我强烈建议为您的类型定义一个
free
的变体,而不是创建一个
可证明的
实例:

freeX :: Symbolic X
freeX = do f <- free_
           b <- free_
           return $ X f b
请注意,这将在
prove
sat
上下文中正常工作,因为
free
知道如何在每种情况下正确操作

我认为这更容易阅读和使用,即使它迫使您使用do符号。您还可以创建一个接受名称的版本,如下所示:

test = prove $ do x <- freeX
                  return $ foo x + bar x .== bar x + foo x
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
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)
您还可以通过SBV将
X
设置为“可解析”。在本例中,完整代码如下所示:

test = prove $ do x <- freeX
                  return $ foo x + bar x .== bar x + foo x
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
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)

现在,您可以选择反例,并根据需要对其进行后期处理。

您可以编写一个模板Haskell帮助程序。我以前只将模板Haskell用作客户机。我的问题基本上是,这是否已经内置到SBV中。搜索包的源代码,看起来不像您正在寻找的。也就是说,我不确定我是否理解为什么不使用元组。如果你真的想要类型区分,那就是
GeneralizedNewtypeDeriving
的目的。有一个
可证明((SBV a,SBV b)->p)
的例子,你的类型
X
(SBV Integer,SBV Integer)
是同构的。你想解释一下推广背后的直觉吗?顺便说一句,您错过了编写
fmap10=(.)的绝佳机会。
。@Alec可能不是直觉,但机制是这样的:因为有一个
可证明的(SInteger->p)
实例存在,这意味着已处理具有任意数量的
SInteger
参数的函数。要使用该实例生成一个
可证明的(Y->p)
实例,我们想用
SInteger->SInteger->组合我们的
Y->p
函数…->Y
函数(即构造函数
Y
)已用于制作
SInteger->SInteger->…->p
功能。
fmap
s链(或
(。
s,如您所观察)完成此合成。
*Main> test >>= print
Just (X {foo = -4 :: SInteger, bar = -5 :: SInteger})