This part is meant as a brief, but complete reference to Tutch.
The proof checker Tutch is invoked by
$ /afs/andrew/scs/cs/15-399/bin/tutch [options] [files]
The files are extended by `.tut' if they do not have an extension already. Options are:
Assignments submission is possible via
$ /afs/andrew/scs/cs/15-399/bin/submit -r file [options] [files]
Options are the same as for tutch, but -r is mandatory. The status of a submission can be checked via
$ /afs/andrew/scs/cs/15-399/bin/status file
This retrieves the status of the submission file handed in via submit -r file ....
Tutch recognizes a set of special symbols. They do not have to be separated by spaces from the remaining code, but serve as separators themselves. These symbols are
( ) [ ] ; : = ~ & | => <=>
Furthermore there are reserved words which cannot be used as identifiers:
T F proof begin end
Identifiers are made up of letters `a-zA-Z', digits `0-9' underscores `_' and primes `''.
Atoms @math{Q} are identifiers that start with capital letter. Propositions @math{A}, @math{B} have the following grammar
@math{A}, @math{B} ::= T % Truth | F % Falsehood | @math{Q} % Atom | ~@math{A} % Negation | @math{A} & @math{B} % Conjunction | @math{A} | @math{B} % Disjunction | @math{A} => @math{B} % Implication | @math{A} <=> @math{B} % Equivalence | (@math{A}) % Parentheses
The binary operators `&', `|' and `=>' are right associative, `<=>' is non-associative. Binding strength decreases in this order:
~ & | => <=>
A hypothesis @math{H} is just a proposition. A proof entry @math{E} is either a line or a frame. A proof @math{P} is a non-empty list of entries.
@math{H} ::= @math{A} % Hypothesis @math{E} ::= @math{A} % Line | [ @math{H}; @math{P} ] % Frame @math{P} ::= @math{E} % Final step | @math{E}; @math{P} % Step and remaining proof
A proof name x is a non-capital identifier. A Tutch file @math{F} is a sequence of proof declarations. A proof declaration @math{D} has the following syntax:
@math{D} ::= proof x: @math{A} = begin @math{P} end % @r{Proof of @math{A} with name x} @math{F} ::= % Empty file | @math{D}; @math{F} % Declaration and remaining file
We extend our list of special symbols and reserved words by the following:
, annotated term fst snd inl inr case of fn abort
Proof terms for propositional logic are formed according to the following grammar:
@math{M}, @math{N} ::= x % Variable | (@math{M}, @math{N}) % Pair | fst @math{M} % First projection | snd @math{M} % Second projection | inl @math{M} % Left injection | inr @math{M} % Right injection | case @math{M} of inl x => @math{N} | inr y => @math{O} end % Case analysis | fn x => @math{M} % Abstraction | @math{M} @math{N} % Application | () % Empty tuple (proof of truth) | abort @math{M} % Falsehood elimination | @math{M} : A % Annotation
Application is a "invisible" left-associative infix operator. It has maximal binding strength, along with the prefix operators `fst', `snd', `inl', `inr', `abort'. This enforces use of parentheses in most cases. E.g.,
(fst x) ((snd x) y)
has already a minimum amount of parentheses. Abstraction `fn x =>' binds less than application and annotation `:' least. The following term is syntactically correct.
fn y => fn x => x y : A => (A => B) => B
Annotated proofs @math{P} are proofs as defined above annotated with proof terms. This changes the syntax of hypotheses @math{H} and proof entries @math{E}.
@math{H} ::= @math{x : A} % Hypothesis @math{E} ::= @math{M : A} % Line | [ @math{H}; @math{P} ] % Frame @math{P} ::= @math{E} % Final step | @math{E}; @math{P} % Step and remaining proof
A Tutch file @math{F} now can contain proof, term and annotated proof declarations @math{D}:
@math{D} ::= proof ... | annotated proof x: @math{A} = begin @math{P} end % @r{Annotated proof of @math{A} with name x} | term x: @math{A} = @math{M} % @r{Proof of @math{A} with name x} @math{F} ::= % Empty file | @math{D}; @math{F} % Declaration and remaining file
New special symbols and keywords are:
* + -> :: nat bool list 0 s rec true false if then else nil val
Types @math{T}, @math{T'} have the following grammar:
@math{T}, @math{T'} ::= 1 % Unit type | 0 % Empty type | @math{a} % Atom | nat % Natural numbers | bool % Booleans | @math{T} list % @r{Lists of element type @math{T}} | @math{T} * @math{T'} % Product | @math{T} + @math{T'} % Disjoint sum | @math{T} -> @math{T'} % Function space | (@math{T}) % Parentheses
`list' is a postfix operator.
The binary operators `*', `+' and `=>' are right associative. Binding strength decreases in this order:
list * + ->
We extend the grammar for terms by the following constructs:
@math{M}, @math{N} ::= ... | 0 % Zero | s @math{M} % Successor | rec @math{M} of f 0 => @math{N} | f (s x) => @math{O} end % Recursion over nat | true % True | false % False | if @math{M} then @math{N} else @math{O} % Boolean case distinction | nil % Empty list | @math{M} :: @math{N} % List construction | rec @math{M} of f nil => @math{N} | f (x :: xs) => @math{O} end % Recursion over list
`0', `true', `false' and `nil' are constants, `s' and `if r then s else' are prefix operators and `::' is an infix operator with lower precedence than the prefix operators or application.
We add one new declaration to the syntax of tutch files:
@math{D} ::= ... | val x: @math{T} = @math{M} % @r{Program of type @math{T} with name x} @math{F} ::= % Empty file | @math{D}; @math{F} % Declaration and remaining file
Reasoning in First-Order Logic (FOL) requires handling of universally and existentially quantified propositions. New special symbols are
! ? .
We extend our definition of propositions by
@math{A}, @math{B} ::= ... | @math{R} @math{M1}...@math{Mn} % Instantiation | !x:@math{T}. @math{A} % Universal quantification | ?x:@math{T}. @math{A} % Existential quantification
Relation symbols @math{R} are capital identifiers. Only they can be instantiated by terms @math{Mi}, e.g. `A(x)', which is the same as `A x'. Instantiation binds strongest, as strong as application and the prefix operators for terms @math{M}.
Quantification `!x:@math{T'} resp. `?x:@math{T'. @math{A}} is treated as a prefix operator with minimal precedence (like lambda abstraction). Ordered by binding strength, the operators that appear in propositions are:
!x:T. resp. ?x:T., <=>, =>, |, &, ~, instantiation
The `proof' declaration supports now also proofs in FOL. Two judgments can form a statement of the proof: an assertion @math{A} (representing `A true' and a term declaration @math{M : T} expressing "@math{M} has type @math{T}". In the same way there are now two forms of hypotheses: @math{x : T}, which introduces a new parameter @math{x} into the proof, and @math{A} which assumes that @math{A} is true. Furthermore one frame can introduce several hypotheses, separated by commas. The grammar for proofs is the following:
@math{H} ::= @math{A} % Hypothesis introduction | @math{x : T} % Parameter introduction @math{Hs} ::= @math{H} % Last hypothesis | @math{H}, @math{Hs} % Several hypotheses @math{E} ::= @math{A} % Line: Assertion | @math{M : T} % Line: Term declaration | [ @math{Hs}; @math{P} ] % Frame @math{P} ::= @math{E} % Final step | @math{E}; @math{P} % Step and remaining proof
All variables in a proposition that appears in a proof must be bound by quantifiers or be (visible) parameters introduced by frames.
We add the two binary relations "less than" and "equal to" to our definition of propositions
@math{A}, @math{B} ::= ... | @math{M} < @math{N} % @r{@math{M} less than @math{N}} | @math{M} = @math{N} % @r{@math{M} equal to @math{N}}
These relations allow us to prove properties about them and defined functions by induction.
Unfortunately, `=' introduces a ambiguity into our syntax, e.g. in
proof Ex2 : !x:nat. 0 < x => ?y:nat. s(y) = x = ...
While parsing the first `=', it is not clear whether it marks the end of the proposition or stands for the equality relation. This ambiguity is resolved by the following rule:
Whenever the expression before `=' is definitively a term, then `=' is parsed as equality. In all other cases it is parsed as the end of the declaration.
In our case `s(y)' is definitively a term. At the next `=', the expression on the left of it is `s(y)=x', which is a proposition, not a term. Thus the end of the declaration is correctly recognized. The same happens in the following example:
val nth : nat -> tau list -> tau -> tau = ...
The expression `tau' left of the equality symbol is a variable. It could be a term or a type variable. Thus it is not definitely a term, and the parser finished parsing the type of `nth' here.
Not correctly resolved is the ambiguity in this case:
proof refl : !x:nat. x = x = ...
Since `x' is either a term variable or a type variable from the perspective of mere syntax, it is not definitively a term. Thus the parser detects falsely the end of the declaration of `refl'. The parser will then try to interpret `!x:nat.x' as a proposition, and fail with the following error message:
Category mismatch: x is a variable, but a proposition is expected in this place
To work around this bug, insert parentheses somewhere around the equality expression, e.g.
proof refl : !x:nat. (x = x) = ...
The introduction and elimination rules for equality and less-than give rise to the following proof terms. All are reserved words:
eq0 eqS eqE0S eqES0 eqESS less0 lessS lessE0 lessES
Of these, two are constants: eq0 and less0. All other are prefix operators. For induction we reuse the `rec' construct for primitive recursion.
We reuse `=' for equality on lists. The following proof terms represent the introduction and elimination rules for equality on lists. These are new reserved words:
eqN eqC eqENC eqECN eqECC
Except eqN, which is a constants, all are prefix operators. The rule for eqC is:
If @math{M : xs = ys}, then @math{eqC M : x::xs = x::ys} for an arbitrary @math{x}.
Here we assume that all these lists @math{xs, ys, x::xs, x::ys} are well-formed and of the same type.
This sections summarizes the syntax specification given in the previous sections.
Special Symbols:
( ) [ ] ; : = ~ & | => <=> , * + -> :: ! ? . <
Reserved words:
annotated proof term val begin end T F nat bool list inl inr case of fst snd fn abort 0 s rec true false if then else nil eq0 eqS eqE0S eqES0 eqESS less0 lessS lessE0 lessES eqN eqC eqENC eqECN eqECC
Proposition expressions:
@math{A}, @math{B} ::= T % Truth | F % Falsehood | @math{Q} % Atom | ~@math{A} % Negation | @math{A} & @math{B} % Conjunction | @math{A} | @math{B} % Disjunction | @math{A} => @math{B} % Implication | @math{A} <=> @math{B} % Equivalence | @math{R} @math{M1}...@math{Mn} % Instantiation | !x:@math{T}. @math{A} % Universal quantification | ?x:@math{T}. @math{A} % Existential quantification | @math{M} < @math{N} % @r{@math{M} less than @math{N}} | @math{M} = @math{N} % @r{@math{M} equal to @math{N}}
Type expressions:
@math{T}, @math{T'}::= 1 % Unit type | 0 % Empty type | @math{a} % Atom | nat % Natural numbers | bool % Booleans | @math{T} list % @r{Lists of element type @math{T}} | @math{T} * @math{T'} % Product | @math{T} + @math{T'} % Disjoint sum | @math{T} -> @math{T'} % Function space
Terms:
@math{M}, @math{N} ::= x % Variable | (@math{M}, @math{N}) % Pair | fst @math{M} % First projection | snd @math{M} % Second projection | inl @math{M} % Left injection | inr @math{M} % Right injection | case @math{M} of inl x => @math{N} | inr y => @math{O} end % Case analysis | fn x => @math{M} % Abstraction | @math{M} @math{N} % Application | () % Empty tuple (proof of truth) | abort @math{M} % Falsehood elimination | @math{M} : A % Annotation | 0 % Zero | s @math{M} % Successor | rec @math{M} of f 0 => @math{N} | f (s x) => @math{O} end % Recursion over nat | true % True | false % False | if @math{M} then @math{N} else @math{O} % Boolean case distinction | nil % Empty list | @math{M} :: @math{N} % List construction | rec @math{M} of f nil => @math{N} | f (x :: xs) => @math{O} end % Recursion over list | eq0 % Proof of @math{0 = 0} | eqS @math{M} % @r{Proof of @math{M = N} |- @math{s M = s N}} | eqE0S @math{M} % @r{Elimination of @math{0 = s N}} | eqES0 @math{M} % @r{Elimination of @math{s M = 0}} | eqESS @math{M} % @r{Proof of @math{s M = s N} |- @math{M = N}} | less0 @math{M} % @r{Proof of @math{0 < s M}} | lessS @math{M} % @r{Proof of @math{M < N} |- @math{s M < s N}} | lessE0 @math{M} % @r{Elimination of @math{s M = 0}} | lessES @math{M} % @r{Proof of @math{s M = s N} |- @math{M = N}} | eqN % Proof of @math{nil = nil} | eqC @math{M} % @r{Proof of @math{Ms = Ns} |- @math{M::Ms = M::Ns}} | eqENC @math{M} % @r{Elimination of @math{nil = M::Ms}} | eqECN @math{M} % @r{Elimination of @math{M::Ms = nil}} | eqECC @math{M} % @r{Proof of @math{M::Ms = N::Ns} |- @math{Ms = Ns}}
Operator precedence:
_ _ (application) list inl inr fst ... (all atomar prefix ops) :: if M then N else let (x,u) = M in fn x => < = ~ & * | + => -> <=> !x:t. ?x:t.
Proofs:
@math{H} ::= @math{A} % Hypothesis introduction | @math{x : T} % Parameter introduction @math{Hs} ::= @math{H} % Last hypothesis | @math{H}, @math{Hs} % Several hypotheses @math{E} ::= @math{A} % Line: Assertion | @math{M : T} % Line: Term declaration | [ @math{Hs}; @math{P} ] % Frame @math{P} ::= @math{E} % Final step | @math{E}; @math{P} % Step and remaining proof
Annotated Proofs:
@math{H} ::= @math{x : A} % Hypothesis @math{E} ::= @math{M : A} % Line | [ @math{H}; @math{P} ] % Frame @math{P} ::= @math{E} % Final step | @math{E}; @math{P} % Step and remaining proof
Declarations:
@math{D} ::= proof x: @math{A} = begin @math{P} end % @r{Proof of @math{A} with name x} | annotated proof x: @math{A} = begin @math{P} end % @r{Annotated proof of @math{A} with name x} | term x: @math{A} = @math{M} % @r{Proof of @math{A} with name x} | val x: @math{T} = @math{M} % @r{Program of type @math{T} with name x} @math{F} ::= % Empty file | @math{D}; @math{F} % Declaration and remaining file
A requirements file @math{F} specifies proof and program tasks, but does not give any proofs or implementations. Grammar:
@math{S} ::= proof x: @math{A} % Proof specification | annotated proof x: @math{A} % Proof specification | term x: @math{A} % Term specification | val x: @math{T} % Program specification @math{F} ::= % Empty file | @math{S}; @math{F} % Specification and remaining file
We give an inductive definition of the proof checking algorithm implemented in Tutch via two judgments `step' and `valid'. The definition is given as in Twelf syntax.
% Tutch proof checker for propositional logic % any infinite datatype: nat : type. z : nat. s : nat -> nat. % Propositions prop : type. %name prop A. % Formation rules true : prop. false : prop. atom : nat -> prop. & : prop -> prop -> prop. %infix right 14 &. | : prop -> prop -> prop. %infix right 13 |. => : prop -> prop -> prop. %infix right 12 =>. % Notational definitions ~ : prop -> prop = [A:prop] (A => false). %prefix 15 ~. <=> : prop -> prop -> prop = [A:prop][B:prop] (A => B) & (B => A) . %infix none 11 <=>. % One-step inference algorithm step : prop -> type. nonhyp : prop -> type. % available non-hypothetical judgment hyp : prop -> prop -> type. % available hypothetical judgment % immediate tactic imm : nonhyp A -> step A. % introduction tactics trueI : step true. &I : nonhyp A -> nonhyp B -> step (A & B). |IL : nonhyp A -> step (A | B). |IR : nonhyp B -> step (A | B). =>I : hyp A B -> step (A => B). % elimination tactics falseE : nonhyp false -> step A. &EL : nonhyp (A & B) -> step A. &ER : nonhyp (A & B) -> step B. |E : nonhyp (A | B) -> hyp A C -> hyp B C -> step C. =>E : nonhyp (A => C) -> nonhyp A -> step C. % Proofs proof : type. %name proof P. final : prop -> proof. % P, Q ::= A line : prop -> proof -> proof. % | A; P frame : prop -> proof -> proof -> proof. % | [H; P]; Q % Proof checking valid : proof -> prop -> type. vfinal : step A -> valid (final A) A. vline : step A -> (nonhyp A -> valid P B) -> valid (line A P) B. vframe : (nonhyp H -> valid P A) -> (hyp H A -> valid Q B) -> valid (frame H P Q) B.
Here we give a new Twelf implementation of Tutch that includes proof terms. The definition and the typing rules are:
% Tutch proof checker for propositional logic % Version 0.2 proof terms % any infinite datatype: nat : type. z : nat. s : nat -> nat. % Propositions prop : type. %name prop A. % Formation rules true : prop. false : prop. atom : nat -> prop. & : prop -> prop -> prop. %infix right 14 &. | : prop -> prop -> prop. %infix right 13 |. => : prop -> prop -> prop. %infix right 12 =>. % Notational definitions ~ : prop -> prop = [A:prop] (A => false). %prefix 15 ~. <=> : prop -> prop -> prop = [A:prop][B:prop] (A => B) & (B => A) . %infix none 11 <=>. % Proof terms term : type. %name term M. fst : term -> term. snd : term -> term. , : term -> term -> term. %infix right 14 ,. inl : term -> term. inr : term -> term. case : term -> (term -> term) -> (term -> term) -> term. \ : (term -> term) -> term. %prefix 11 \. : term -> term -> term. %infix left 20 . <> : term. abort: term -> term. % Typing judgement in : term -> prop -> type. %infix none 0 in. % Typing rules &I : M in A -> N in B -> (M , N) in A & B. &EL : M in A & B -> fst M in A. &ER : M in A & B -> snd M in B. |IL : M in A -> inl M in A | B. |IR : M in B -> inr M in A | B. |E : M in A | B -> ({x: term} x in A -> N x in C) -> ({y: term} y in B -> L y in C) -> case M N L in C. =>I : ({x: term} x in A -> M x in B) -> \ M in A => B. =>E : M in A => B -> N in A -> M N in B. trueI : <> in true. falseE: M in false -> abort M in C.
We add annotated proofs, which need an inference algorithm that respects proof terms:
% One-step inference algorithm step : term -> prop -> type. % %mode step +A. j0hyp : term -> prop -> type. % available non-hypothetical judgment j1hyp : (term -> term) -> prop -> prop -> type. % available hypothetical judgment % immediate tactic imm : j0hyp M A -> step M A. % introduction tactics bytrueI : step <> true. by&I : j0hyp M A -> j0hyp N B -> step (M , N) (A & B). by|IL : j0hyp M A -> step (inl M) (A | B). by|IR : j0hyp M B -> step (inr M) (A | B). by=>I : j1hyp M A B -> step (\ M) (A => B). % elimination tactics byfalseE : j0hyp M false -> step (abort M) A. by&EL : j0hyp M (A & B) -> step (fst M) A. by&ER : j0hyp M (A & B) -> step (snd M) B. by|E : j0hyp M (A | B) -> j1hyp N A C -> j1hyp L B C -> step (case M N L) C. by=>E : j0hyp M (A => C) -> j0hyp N A -> step (M N) C.
The checking of annotated proofs requires two judgments `avalid1' and `avalid2': The first returns a proof term and the second the proven proposition.
% Annotated proofs aproof : type. %name aproof P. afinal : term -> prop -> aproof. % P ::= M : A aline : term -> prop -> aproof -> aproof. % | M : A; P aframe : prop -> (term -> aproof) -> aproof -> aproof. % | [x: H; P]; P' % Annotated proof checking avalid1 : aproof -> term -> type. % %mode avalid1 +P -M. avalid2 : aproof -> prop -> type. % %mode avalid2 +P -A. avfinal1 : step M A -> avalid1 (afinal M A) M. avfinal2 : step M A -> avalid2 (afinal M A) A. avline1 : step M A -> (j0hyp M A -> avalid1 P N) -> avalid1 (aline M A P) N. avline2 : step M A -> (j0hyp M A -> avalid2 P B) -> avalid2 (aline M A P) B. avframe1 : ({x:term} j0hyp x H -> avalid1 (P x) (M x)) -> ({x:term} j0hyp x H -> avalid2 (P x) A) -> (j1hyp M H A -> avalid1 Q N) -> avalid1 (aframe H P Q) N. avframe2 : ({x:term} j0hyp x H -> avalid1 (P x) (M x)) -> ({x:term} j0hyp x H -> avalid2 (P x) A) -> (j1hyp M H A -> avalid2 Q B) -> avalid2 (aframe H P Q) B.
Go to the first, previous, next, last section, table of contents.