Theorems
General Form
A theorem has the general form
all(a:A, b:B, ...)
require
pre_1
pre_2
...
ensure
goal
... -- optional proof
end
If a theorem has no variables then the variable list can be ommitted with the
parentheses, however the keyword all
is still necessary. The block of
preconditions is optional. A theorem must have a goal.
In an implementation file a theorem can have an optional proof but only one goal, in an interface file proofs are not allowed (proofs are considered as implementations) but there can be many goals (ensure clauses).
A theorem states that for all values of the variables a,b,...
the conditions
pre_1
, pre_2
, ... imply the goal goal
.
Each proof consists of an optional assertion block and an optional proof expression.
Assertion Block
An assertion block within a proof has the form
assert
assertion_1
assertion_2
...
The assertions in the assertion block have to be provable by the proof
engine by using the assumptions pre_1
, pre_2
, ... and
all previous assertions of the assertion block.
An assertion in the assertion block can be a complete theorem, i.e. theorems can be nested arbitrarily. If an assertion of an assertion block is a theorem it can have its own proof.
In an inner theorem the variables and the require clauses are optional. I.e. the most degenerate form of an inner theorem looks like
ensure
exp
-- optional proof of 'exp'
end
If an inner theorem has no variables then the variable list together with the
keyword all
has to be dropped. An inner theorem without variables but with
preconditions has the form
require
r1
r2
...
ensure
exp
-- optinal proof of 'exp' assuming 'r1', 'r2', ...
end
The purpose of an assertion block is to enrich the context with assertions. Without an assertion block the goal must be provable by just using the preconditions. With an assertion block all assertions in the block can be used to prove the goal.
Proof by Case Split
A case split is a proof expression of the following form.
if a
-- optional proof of the goal under the assumption 'a'
orif b
-- optional proof of the goal under the assumption 'b'
The case split expression instructs the compiler to do the following steps:
Prove
a or b
Shift condition
a
into the context and prove the goalShift condition
b
into the context and prove the goal
Since a or b
is valid and the goal is provable assuming a
and assuming
b
, the goal is provable assuming a or b
which is the case because of the
first step.
There is a variant of a case split expression where b
is not a
.
if a
-- optional proof of the goal under the assumption 'a'
else
-- optional proof of the goal under the assumption 'not a'
This proof expression looks like an ordinary if-expression. However there is
no keyword then
and instead of an expression an optional proof
(i.e. optional assertion block and optional proof expression) can be used.
Proof by Contradiction
Sometimes we want to proof a proposition a
by assuming the opposite i.e.not
a
and deriving a contradiction i.e. false
from it. A proof by contradiction
uses the general theorem all(a:BOOLEAN) (not a ==> false) ==> a
.
The Albatross programming language has syntax support for proofs by contradiction. A proof by contradiction has the following form.
via require
exp
-- optional proof of 'false'
The clause via require exp
instructs the compiler to assume exp
and derive
false
from it. After having done this successfully the assertion exp ==>
false
has been proved. With the help of this assertion the original goal must
be provable.
Usually exp
is the negation of the original goal.
Transitivity Proof
If the goal has the form r(a,z)
where r
is a transitive relation like =
or <=
the transitivity law all(x.y,z) r(x,y) ==> r(y,z) ==> r(x,z)
of the
relation can be used to prove r(a,z)
by using some intermediate expressions
b
, c
, ... and prove r(a,b)
, r(b,c), ...,
r(y,z)`.
A transitivity proof has the following form
via [ b
, c
, d
...
]
A transitivity proof instructs the compiler to do the following steps.
Verify that the relation
r
in the goalr(a,z)
is transitiveVerify
r(a,b)
Verify
r(b,c)
...
Verify
r(y,z)
If the relation r
is not only transitive but also reflexive then the
endpoints a
and z
can be included optionally in the list because r(a,a)
and r(z,z)
are valid as well. If the relation is not reflexive this is not
possible.
Existential Proof
An existential proof has the form
via some(a,b,...)
exp
An existential proof instructs the compiler to do the following.
Verify
some(a,b,...) exp
Add the variables
a,b,...
and shiftexp
into the contextProve the original goal under the assumption that there are variables
a,b,...
which satisfyexp
Induction Proof
There are induction proofs for inductive types and inductively defined sets and relations.
The first one has the form
inspect
x -- 'x' must be a variable of an inductive type
case constructor_1
... -- optional proof for the case that 'x' has been constructed
-- by the constructor expression 'constructor_1'
... -- other cases
The cases are optional. If no cases are present the compiler tries to prove the goal for all cases implicitely. Therefore explicit cases are usually only mentioned if an explicit proof is necessary for the specific case.
The second one has the form
inspect
x in set
case rule_1
... -- optional proof for the case that 'x' is in the set because
-- of 'rule_1
... -- other cases
x
must be a variable and set
must be an expression which evaluates to an
inductively defined set and x
must be in this set.
Chaining and Nesting of Proof Expressions
Proof expressions can be chained and nested.
all(a,b,...)
require
pre_1
pre_2
ensure
goal
assert
ass_1 -- derive 'ass_1', 'ass_2', ... from the preconditions
...
via some(x) -- prove 'some(x) exp' and use it to prove the goal
exp
assert
...
via require -- assume 'cond' and derive 'false' from it and afterwards
cond -- prove the goal assuming 'cond ==> false'
if cond_a -- prove 'false' assuming 'cond_a'
assert
...
inspect n
orif cond_b -- prove 'false' assuming 'cond_b
assert
...
end
Realistic Theorem
We demonstrate some of the proof expressions with a more realistic example. Suppose we have the following definitions.
A: ANY
B: ANY
(<=) (p,q:{A}): ghost BOOLEAN
-- Is 'p' a subset of 'q'?
-> all(x) x in p ==> x in q
domain (r:{A,B}): ghost {A}
-- The domain of the relation 'r'.
-> {a: some(b) r(a,b)}
range (r:{A,B}): ghost {B}
-- The range of the relation 'r'.
-> {b: some(a) r(a,b)}
carrier (r:{A,A}): ghost {A}
-- The carrier of the relation 'r'.
-> r.domain + r.range
Obviously if a relation r
is a subset (subrelation) of the relation s
then
the carrier of r
has to be a subset of the carrier of s
.
The following proof expands first the definition of r.carrier <= s.carrier
,
Then it makes a case split and uses an existential proof within each case with
some assertion block.
all(r,s:{A,A})
require
r <= s
ensure
r.carrier <= s.carrier
assert
all(x)
require
x in r.carrier
ensure
x in s.carrier
-- 'x' is in 'r.carrier', therefore it has to be in the domain
-- or in the range of 'r'. I.e. we can prove by case split.
if x in r.domain
-- if 'x' is in the domain of 'r' then there is some
-- 'y' with 'r(x,y)
via some(y)
r(x,y)
assert
s(x,y) -- because of 'r is a subset of 's'
x in s.domain
x in s.carrier
orif x in r.range
-- if 'x' is in the range of 'r' then there is some
-- 'y' with 'r(y,x)
via some(y)
r(y,x)
assert
s(y,x) -- because of 'r is a subset of 's'
x in s.range
x in s.carrier
end
end