「SF-LC」12 Imp
1 | Z ::= X;; |
A weird convention through out all IMP is:
a-
: arithb-
: boolc-
: command
Arithmetic and Boolean Expression
Abstract Syntax
1 | a ::= |
1 | Inductive aexp : Type := |
Evaluation
TODO: is this considered as “denotational semantics”?
1 | Fixpoint aeval (a : aexp) : nat := |
Supposed we have a Fixpoint optimize_0plus (a:aexp) : aexp
1 | Theorem optimize_0plus_sound: ∀a, |
During the proof, many cases of destruct aexp
are similar!
Recursive cases such as APlus, AMinus, AMult
all require duplicated IH
application.
From Coq Intensive:
when wesimpl
onAPlus
case. it’s not “simplified” but give us a pattern matching.
That’s a hint that we need to furthur case analysis bydestruct n
as0
case or_
case.
Coq Automation
Tacticals
“higher-order tactics”.
try T
and ;
tacticals
if
T
fail,try T
successfully does nothing at all
T;T'
: performsT'
on each subgoal generated byT
.
Super blindly but useful: (only leave the “interesting” one.)
1 | induction a; |
.
is the atomic;
cannot be stepped into…
T; [T1 | T2 | ... | Tn]
tacticals
general form or
;
T;T'
is shorthand for:T; [T' | T' | ... | T']
.
repeat
tacticals
1 | Theorem In10 : In 10 [1;2;3;4;5;6;7;8;9;10]. |
- stop when it fails
- always succeeds, then loop forever! e.g.
repeat simpl
This does not affect Coq’s logical consistency,
construction process diverges means we have failed to construct a proof, not that we have constructed a wrong one.
Defining New Tactic Notations
Tactic Notation
: syntax extension for tactics (good for simple macros)
1 | Tactic Notation "simpl_and_try" tactic(c) := |
Ltac
: scripting language for tactics (good for more sophisticated proof engineering)- OCaml tactic scripting API (for wizards)
The omega
Tactic
Presburger arithmetic
- arith, equality, ordering, logic connectives
O(doubly expontential)
A Few More Handy Tactics
clear H
subst x
,subst
rename ... into ...
(change auto-generated name that we don’t like…)
the below three are very useful in Coq Automation (w/ try T; T'
)
assumption
contradiction
constructor
(try toapply
all constructors.Problem: might have multiple constructors applicable but some fail)
Evaluation as a Relation
Defined as Binary relation on aexp × nat
.
Exactly Big Step / Structural Operational Semantics.
More flexible than Fixpoint
(computation, or Denotational).
…Since we can operate on Inductive
as data I guess?
…and we can also induction
on the relation.
…and when things getting more and more “un-computable” (see below).
译注:求值关系不满足对称性,因为它是有方向的。
1 | Inductive aevalR : aexp → nat → Prop := |
Noticed now we now define
inductive
in a mixed style:
some arg is before:
(named), some are after:
(anonymous).
We could do this as well
1 | | E_APlus (e1 e2: aexp) (n1 n2: nat) |
Reserved Notation
allow us using the notation during the definition!
1 | Reserved Notation "e '\\' n" (at level 90, left associativity). |
I hated this infix \\
notation…it tries to mimic ⇓
(double down arrow).
e1 \\ n1
e2 \\ n2
-------------------- (E_APlus)
APlus e1 e2 \\ n1+n2
is actually:
e1 ⇓ n1
e2 ⇓ n2
-------------------- (E_APlus)
APlus e1 e2 ⇓ n1+n2
Coq Intensive:
If you have two variables above the line. Think about if you needgeneralize dependent
.
Computational vs. Relational Definitions INTERESTING
In some cases, relational definition are much better than computational (a.k.a. functional).
for situations, where thing beingdefined is not easy to express as a function (or not a function at all)
case 1 - safe division
1 | Inductive aexp : Type := |
- functional: how to return
ADiv (ANum 5) (ANum 0)
? probably has to beoption
(Coq is total!) - relational:
(a1 \\ n1) → (a2 \\ n2) → (n2 > 0) → (mult n2 n3 = n1) → (ADiv a1 a2) \\ n3
.- we can add a constraint
(n2 > 0)
.
- we can add a constraint
case 2 - non-determinism
1 | Inductive aexp : Type := |
- functional: not a deterministic function…
- relational:
E_Any (n : nat) : AAny \\ n
… just say it’s the case.
Nonetheless, functional definition is good at:
- by definition deterministic (need proof in relational case)
- take advantage of Coq’s computation engine.
- function can be directly “extracted” from Gallina to OCaml/Haskell
In large Coq developments:
- given both styles
- a lemma stating they coincise (等价)
Expressions with Variables
State (Environment) 环境
A machine state (or just state) represents the current values of all variables at some point in the execution of a program.
1 | Definition state := total_map nat. |
Syntax
1 | Inductive aexp : Type := |
Notations & Coercisons — “meta-programming” and AST quasi-quotation
Quasi-quotation
OCaml PPX & AST quasi-quotation
quasi-quotation enables one to introduce symbols that stand for a linguistic expression in a given instance and are used as that linguistic expression in a different instance.
e.g. in above OCaml example, you wrote %expr 2 + 2
and you get [%expr [%e 2] + [%e 2]]
.
Coq’s Notation Scope + Coercision == built-in Quasi-quotation
1 | (** Coercision for constructors **) |
Evaluation w/ State (Environment)
Noticed that the st
has to be threaded all the way…
1 | Fixpoint aeval (st : state) (a : aexp) : nat := |
Commands (Statement)
1 | c ::= SKIP | x ::= a | c ;; c | TEST b THEN c ELSE c FI | WHILE b DO c END |
we use
TEST
to avoid conflicting with theif
andIF
notations from the standard library.
1 | Inductive com : Type := |
notation
magics:
1 | Bind Scope imp_scope with com. |
Unset Notations
1 | Unset Printing Notations. (** e1 + e2 -> APlus e1 e2 **) |
The Locate
command
1 | Locate "&&". |
Evaluating Commands
Noticed that to use quasi-quotation in pattern matching, we need
1 | Open Scope imp_scope. |
An infinite loop (the %imp
scope is inferred)
1 | Definition loop : com := |
The fact that
WHILE
loops don’t necessarily terminate makes defining an evaluation function tricky…
Evaluation as function (FAIL)
In OCaml/Haskell, we simply recurse, but In Coq
1 | | WHILE b DO c END => if (beval st b) |
Well, if Coq allowed (potentially) non-terminating, the logic would be inconsistent:
1 | Fixpoint loop_false (n : nat) : False := loop_false n. (** False is proved! **) |
Step-Indexed Evaluator (SUCC)
Chapter ImpCEvalFun
provide some workarounds to make functional evalution works:
- step-indexed evaluator, i.e. limit the recursion depth. (think about Depth-Limited Search).
- return
option
to tell if it’s a normal or abnormal termination. - use
LETOPT...IN...
to reduce the “optional unwrapping” (basicaly Monadic binding>>=
!)- this approach of
let-binding
became so popular in ML family.
- this approach of
Evaluation as Relation (SUCC)
Again, we are using some fancy notation st=[c]=>st'
to mimic ⇓
:
In both PLT and TaPL, we are almost exclusively use Small-Step, but in PLC, Big-Step were used:
beval st b1 = true
st =[ c1 ]=> st'
--------------------------------------- (E_IfTrue)
st =[ TEST b1 THEN c1 ELSE c2 FI ]=> st'
is really:
H; b1 ⇓ true
H; c1 ⇓ H'
---------------------------------- (E_IfTrue)
H; TEST b1 THEN c1 ELSE c2 FI ⇓ H'
1 | Reserved Notation "st '=[' c ']⇒' st'" (at level 40). |
By definition evaluation as relation (in Type
level),
we need to construct proofs (terms) to define example.
…noticed that in the definition of relaiton ceval
, we actually use the computational aevel
, beval
..
…noticed that we are using explicit ∀
style rather than constructor argument style (for IDK reason). They are the same!
Determinism of Evaluation
Changing from a computational to a relational definition of evaluation is a good move because it frees us from the artificial requirement that evaluation should be a total function
求值不再必须是全函数But it also raises a question: Is the second definition of evaluation really a partial function?
这个定义真的是偏函数吗?(这里的重点在于 偏函数 要求 right-unique 即 deterministic)
we can prove:
1 | Theorem ceval_deterministic: ∀c st st1 st2, |
Reasoning About Imp Programs
Case plus2_spec
1 | Theorem plus2_spec : ∀st n st', |
this looks much better as inference rules:
H(x) = n
H; x := x + 2 ⇓ H'
--------------------- (plus2_spec)
H'(x) = n + 2
By inversion
on the Big Step eval relation, we can expand one step of ceval
(对 derivation tree 的 expanding 过程其实就是展开我们所需的计算步骤的过程)
st : string -> nat
=================================
(X !-> st X + 2; st) X = st X + 2
In inference rule:
H : string → nat
================================
(x ↦ H(x) + 2); H)(x) = H(x) + 2
Case no_whiles_terminating
1 | Theorem no_whilesR_terminating_fail: |
If we intros st
before induction c
,
the IH would be for particular st
and too specific for E_Seq
(It’s actually okay for TEST
since both branch derive from the same st
)
1 | IHno_whilesR1 : exists st' : state, st =[ c1 ]=> st' |
So we’d love to
1 | generalize dependent st. |
Additional Exerciese
Stack Compiler
Things that evaluate arithmetic expressions using stack:
- Old HP Calculators
- Forth, Postscript
- Java Virtual Machine
1 | infix: |
Goal: compiler translates
aexp
into stack machine instructions.
1 | Inductive sinstr : Type := |
Correct Proof
1 | Theorem s_compile_correct : forall (st : state) (e : aexp), |
To prove this, we need a stronger induction hypothesis (i.e. more general), so we state:
1 | Theorem s_execute_theorem : forall (st : state) (e : aexp) (stack : list nat) (prog : list sinstr), |
and go through!
IMP Break/Continue
1 | Inductive result : Type := |
The idea is that we can add a signal to notify the loop!
Fun to go through!
Slide Q & A
st =[c1;;c2] => st'
there would be intermediate thing after inversion so… we need determinism to prove this!
- (It won’t be even true in undetermincy)
the
WHILE
one (would diverge)- true…how to prove?
- induction on derivation…!
- show contradiction for all cases
- to prove
¬(∃st', ...)
, we intro the existentials and prove theFalse
.
Auto
auto
includes try
Proof with auto.
Set Intro Auto