15-317 Constructive Logic
Recitation 10: Intrinsic Encodings

Today in recitation, we went over two Homework 6 problem solutions, one
an invertibility (∀R) and one a non-invertibility (∀L). The full
story can be found in the sample
solution, but the take-home point is the following: when asked to
prove "If J1, then J2", you must work parametrically
from an unknown proof of J1, taking all instances into account;
but when asked to refute such a statement, it suffices merely to
provide a counterexample, i.e. to choose an instance of J1
which you know to be derivable and argue that the corresponding
instance of J2 is not.

Then, we learned the story of intrinsic encodings. By systematically
transforming and reintepreting the code from lecture for representing
props and proofs, we can obtain a representation of
intrinsically well-formed proofs which obviates the need for a
proof-checking judgement.

Reviewing the code from lecture, we saw that the
judgements-as-types methodology represents on-paper judgements
using type families in the logical framework. So the on-paper
proof-checking judgement M : A becomes a type family

$ : proof -> prop -> type.

Now the type M $ A represents the judgement M : A,
and elements D of the type M $ A represent
derivations of the judgement M : A. Thus, inference rules for
deriving M : A become constructors for building elements of the
type M $ A. For instance the rule ∧I becomes:

/\I : pair M N $ A /\ B
<- M $ A
<- N $ B.

Early in the semester we had a natural deduction judgement A true
and various rules for constructing derivations of this judgement. The
rules were in one-to-one correspondence with the rules used for deriving
the judgement M : A, but without proof terms. We can
easily represent this in the logical framework LF by inventing a new type
family to represent the judgement:

true : prop -> type.

and by populating it by constructors corresponding to the rules. We can
follow our intuition regarding the correspondence between M : A
and A true: the rules populating true A are exactly
the rules populating M $ A with the proof terms erased and
the type family changed. For instance:

/\I : true (A /\ B)
<- true A
<- true B.

We can take this one step further, in fact, by realizing that proof terms
are also in one-to-one correspondence with derivations. Then instead of
reading the type true A as the judgement that a proposition
A is provable, we can read it as the type family of
well-formed proof terms. If we rename the rules using the proof term
constructors and write the arrows in the usual forward direction, we get:

pair : true A -> true B -> true (A /\ B).

To complete the interpretation, we can rename true to
proof and dispense with our earlier non-intrinsic proof
type.

proof : prop -> type.
pair : proof A -> proof B -> proof (A /\ B).

Now we have an intrinsic encoding of well-formed proofs: we cannot
even write down an ill-formed proof or it will not typecheck as an LF
term. In essence this is what you get if you interpret natural deduction
derivations as programs directly rather than taking the detour of
introducing proof terms and annotating the rules appropriately.

It is worth examining the steps of the above transformation in detail, so
I encourage you to look at and compare the three Twelf files listed below
in the references. We go from raw proof terms with a formation judgement
M $ A to bare natural deductions true A, and
then we reinterpret those deductions as intrinsically well-formed proof
terms. This is a highly general and elegant technique that is often used
when encoding logics in the logical framework: we leverage typechecking
in LF to rule out ill-formed proofs in our logic.