Is it possible to write a contract that checks whether a statement is true? For example, I want to define a contract
true :: Contract a
such that for all values x, the equation
assert true x == x
holds.
I have tried something like this:
true :: Contract a
true = Pred (\a -> True)
But when running assert true x == x
compiler says that x
is undefined.
When running assert true 5==6
the result is False
, and I have hoped for a Contract violation error
.
How should I change this true
contract? Would appreciate your help.
Here
data Contract :: * -> * where
Pred :: (a -> Bool) -> Contract a
Fun :: Contract a -> Contract b -> Contract (a -> b)
Assert will cause a run-time failure if a contract is violated, and otherwise return the original result:
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
You can see the problem very clearly if you consider your definitions of true
and assert
. Here are the relevant parts:
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"
...
Put them together and you see that assert true x
will test (\a -> True) x
and either produce x
or throw an error, depending on if it's True
or False
. And that this will always be True
, no matter what expression you use for x
, since the predicate by definition always returns True
, and ignores its argument.
The simple fix is to have the true
"contract" actually test its argument, like this:
true :: Contract Bool
true = Pred id
That is, this new true
can only apply to a value of type Bool
(because it really doesn't make sense for any others) and does nothing to it. It lets the value through unchanged if it's True
, and otherwise throws the contract violation error you want:
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
And note the parentheses in assert true (5==6)
, since assert true 5==6
is parsed as (assert true 5)==6
, due to function application being the most precedent "operator" in Haskell. assert true 5==6
leads to an error because this corrected version of true
only works on a Boolean value, and therefore not on 5
.