15-317 Constructive Logic
Recitation 11: The Identity Theorem
Today in recitation, we went over selected solutions from Midterm 2,
including the G4ip derivation of ¬¬(P ∨ ¬P) and
the time-saving/wasting derivations. The take-home point from the
problem about the memoizing rule is that remembering an atomic
proposition can only save time, but remembering a proposition
with structure opens us to the possibility of needlessly
decomposing that proposition.
We also went over the classical sequent calculus derivation of
P ∨ ¬ P, the cut admissibility case, and the invertibility
proof. See the complete
solution for details.
Then, we went over two cases of the proof of the Identity Theorem for the
intuitionistic sequent calculus, the case for atomic propositions and the
case for conjunctions. The end of Lecture 9 details the proof for all
connectives.
Finally, we saw how to represent the two cases of this proof in Twelf.
To help convey the mindset of a Twelf programmer, we reproduce them here
in a piecewise fashion. To see the steps animated, you may run
animate.pl on
identity-anim.txt.
First, we start from the specification of the theorem as a well-moded
total logic program. The program relates each proposition A
to a derivation that a hypothesis left A leads to a
conclusion right A :
identity : {A:prop} (left A -> right A) -> type.
%mode identity +A -D.
%worlds () (identity _ _).
%total A (identity A _).
The first and easiest case is the case when the given proposition is an
atomic proposition. We require a derivation of left (? P) -> right
(? P) , which we denote by leaving a "hole" _ with an
appropriate type annotation:
id? : identity (? P) (_ : left (? P) -> right (? P)).
(NB: each intermediate step will typecheck in Twelf: by writing a type
annotation, we can check that our intuitions are correct, but if we leave
off the type annotation, Twelf will tell us what type it expects the hole
to have. In this discussion, we will always write the type annotation
for clarity.)
The type we require is exactly the type of the init rule, so we can fill
in the underscore using init :
id? : identity (? P) (init : left (? P) -> right (? P)).
If we run the above code through Twelf with the totality check, we see
that although it typechecks, we get the following coverage error:
identity.elf:7.8-7.9 Error:
Coverage error --- missing cases:
{A1:prop} {A2:prop} {X1:left (A1 => A2) -> right (A1 => A2)}
|- identity (A1 => A2) ([x:left (A1 => A2)] X1 x),
{A1:prop} {A2:prop} {X1:left (A1 /\ A2) -> right (A1 /\ A2)}
|- identity (A1 /\ A2) ([x:left (A1 /\ A2)] X1 x).
This error expresses the fact that we are still missing clauses defining
the identity relation when the first argument is either an
implication or a conjunction.
Let's continue with the conjunction case. We can start off as before, by
leaving a "hole" for the output and observing what its type is:
id/\ : identity (A /\ B)
(_ : left (A /\ B) -> right (A /\ B)).
Since the output we require is of function type, and since we see no
obvious ways of filling it in, we can start by expanding the hole to be
an LF function. Its argument will have type left (A /\ B) ,
and its body will have type right (A /\ B) . We leave the
body as a hole for now:
id/\ : identity (A /\ B)
([u:left (A /\ B)]
_ : right (A /\ B)).
Now, we must find a way to make a right (A /\ B) . Taking a
cue from the paper proof, we know we'll want to start our derivation with
the /\R rule:
id/\ : identity (A /\ B)
([u:left (A /\ B)]
/\R (_ : right A)
(_ : right B)).
This leaves us with two holes: one of type right A and one
of type right B .
Taking another cue from the paper proof, we can continue by noting that
the first of these subderivations will proceed by the left rule
\/L1 , leaving two new holes:
id/\ : identity (A /\ B)
([u:left (A /\ B)]
/\R (/\L1 (_ : left A -> right A)
(_ : left (A /\ B)))
(_ : right B)).
How can we fill these in? Well, the left (A /\ B) that we
expect to apply the rule to is exactly the one that we hypothesized to
start our output derivation, namely u :
id/\ : identity (A /\ B)
([u:left (A /\ B)]
/\R (/\L1 (_ : left A -> right A)
(u : left (A /\ B)))
(_ : right B)).
What about the left A -> right A ? We don't have one handy,
but comparing with the paper proof, we realize that we should be able to
come up with one using our induction hypothesis on the proposition
A ! In Twelf, appeals to the induction hypothesis correspond
to recursive calls of the logic program, so we add a subgoal calling
identity at the proposition A and naming the
output IdA , with a type annotation on the output for
readability.
id/\ : identity (A /\ B)
([u:left (A /\ B)]
/\R (/\L1 (_ : left A -> right A)
(u : left (A /\ B)))
(_ : right B))
<- identity A (IdA : left A -> right A).
Now we can use the output IdA to satisfy our requirement of
a term of type left A -> right A .
id/\ : identity (A /\ B)
([u:left (A /\ B)]
/\R (/\L1 (IdA : left A -> right A)
(u : left (A /\ B)))
(_ : right B))
<- identity A (IdA : left A -> right A).
We can clean up a bit by deleting the extraneous type annotations from
our application of /\L1 :
id/\ : identity (A /\ B)
([u:left (A /\ B)]
/\R (/\L1 IdA u)
(_ : right B))
<- identity A (IdA : left A -> right A).
And now, we can use symmetric reasoning to satisfy our final remaining
hole of type right B :
id/\ : identity (A /\ B)
([u:left (A /\ B)]
/\R (/\L1 IdA u)
(/\L2 IdB u))
<- identity A (IdA : left A -> right A)
<- identity B (IdB : left B -> right B).
If we run this case through Twelf along with our previous case and the
totality check, we get a new, smaller coverage error:
identity.elf:14.8-14.9 Error:
Coverage error --- missing cases:
{A1:prop} {A2:prop} {X1:left (A1 => A2) -> right (A1 => A2)}
|- identity (A1 => A2) ([x:left (A1 => A2)] X1 x).
Note that Twelf no longer complains about a missing case for conjunction!
All that remains is the case for implication, and then the theorem will
check successfully. The file identity.elf
contains the entire proof, including the final case.
The two most important ideas to keep in mind while constructing a Twelf
proof are the following:
- A proof is just a well-moded total logic program taking the inputs
of the theorem to the outputs of the theorem.
- Let the types guide your development!
References:
[ Home
| Schedule
| Assignments
| Handouts
| Software
]
fp@cs
Frank Pfenning
|