是否可以编写合同来检查陈述是否正确? 例如,我想定义一个合同
true :: Contract a
使得对于所有值x,方程式
assert true x == x
按住。
我尝试过这样的事情:
true :: Contract a
true = Pred (\a -> True)
但是在运行assert true x == x
时,编译器会指出x
是未定义的。
运行assert true 5==6
时,结果为False
,我希望有一个Contract violation error
。
我应该如何更改此true
合同?非常感谢您的帮助。
这里
data Contract :: * -> * where
Pred :: (a -> Bool) -> Contract a
Fun :: Contract a -> Contract b -> Contract (a -> b)
如果违反合同,Assert将导致运行时失败,否则将返回原始结果:
assert :: Contract a -> a -> a
assert (Pred p) x = if p x then x else error "contract violation"
assert (Fun pre post) f = assert post . f . assert pre
答案 0 :(得分:6)
如果您考虑true
和assert
的定义,则可以很清楚地看到问题。以下是相关部分:
true :: Contract a
true = Pred (\a -> True)
assert :: Contract a -> a -> a
assert (Pred p) x = if p x then x else error "contract violation"
...
将它们放在一起,您会发现assert true x
将测试(\a -> True) x
并产生x
或抛出错误,具体取决于它是True
还是False
。而且无论您为True
使用什么表达式,该值始终为x
,因为根据定义,谓词始终返回True
,而忽略其自变量。
简单的解决方法是让true
“合同”实际测试其参数,如下所示:
true :: Contract Bool
true = Pred id
也就是说,这个新的true
仅适用于类型Bool
的值(因为它对其他任何值实际上都没有意义),并且对此无任何作用。如果值为True
,它将使值保持不变,否则将引发您想要的违反合同错误:
Prelude> assert true (5==6)
*** Exception: contract violation
CallStack (from HasCallStack):
error, called at <interactive>:21:46 in interactive:Ghci2
Prelude> assert true (5==5)
True
请注意assert true (5==6)
中的括号,因为函数应用程序是Haskell中最先例的“运算符”,因此assert true 5==6
被解析为(assert true 5)==6
。 assert true 5==6
导致错误,因为true
的此更正版本仅适用于布尔值,因此不适用于5
。
答案 1 :(得分:0)
请注意,assert true x == x
将assert true x
与x
进行比较;因此assert true 5
为5
,当然5 == 6
为假,不是违反合同。
如果您打算assert true (x == x)
保留,那么看起来
true :: Contract Bool
true = Pred id
assert true (5==6) -- Contract violation
就是你想要的
答案 2 :(得分:0)
“在运行时断言true true x == x编译器说x是未定义的”在这里很关键。在我看来,您的assert
调用是一个表达式,其中包含对x
的两个引用,而最外面的函数是(==)
。没有约束x
的任何一方都无法评估。 assert true x
部分再也看不到==
,如果我们将其重写为assert true (x == x)
,我们仍然需要提供x :: Eq a
。我不知道如何检查这样的函数,但是当然有些选项我不太熟悉。