https://perry.alexander.name/eecs762
Formatted using Markdown and Marp for VS Code
Date: 2026-01-20
The most over-analyzed programming language in existence.
if and whileZ := X;
Y := 1;
while Z <> 0 do
Y := Y * Z;
Z := Z - 1
end
a := nat
| a + a
| a - a
| a * a
| id
b := true
| false
| a = a
| a <> a
| a <= a
| a > a
| ~b
| b && b
c := skip
| c ; c
| v := a
| if b then c else c end
| while b do c end
Inductive aexp : Type :=
| ANum (n : nat)
| AId (x : string)
| APlus (a1 a2 : aexp)
| AMinus (a1 a2 : aexp)
| AMult (a1 a2 : aexp).
Inductive bexp : Type :=
| BTrue
| BFalse
| BEq (a1 a2 : aexp)
| BNeq (a1 a2 : aexp)
| BLe (a1 a2 : aexp)
| BGt (a1 a2 : aexp)
| BNot (b : bexp)
| BAnd (b1 b2 : bexp).
Inductive com : Type :=
| CSkip
| CAsgn (x : string) (a : aexp)
| CSeq (c1 c2 : com)
| CIf (b : bexp) (c1 c2 : com)
| CWhile (b : bexp) (c : com).
Rocq provides nifty notations for parsing to abstract syntax.
<{ Z := X;
Y := 1;
while Z <> 0 do
Y := Y × Z;
Z := Z - 1
end }>
<{ concrete syntax }> -> abstract syntax
Fixpoint aeval (a : aexp) : nat :=
match a with
| ANum n => n
| APlus a1 a2 => (aeval a1) + (aeval a2)
| AMinus a1 a2 => (aeval a1) - (aeval a2)
| AMult a1 a2 => (aeval a1) * (aeval a2)
end.
Example test_aeval1:
aeval (APlus (ANum 2) (ANum 2)) = 4.
Proof. reflexivity. Qed.
Example test_aeval1:
aeval <{ 2 + 2 }> = 4.
Proof. reflexivity. Qed.
Fixpoint beval (b : bexp) : bool :=
match b with
| BTrue => true
| BFalse => false
| BEq a1 a2 => (aeval a1) =? (aeval a2)
| BNeq a1 a2 => negb ((aeval a1) =? (aeval a2))
| BLe a1 a2 => (aeval a1) <=? (aeval a2)
| BGt a1 a2 => negb ((aeval a1) <=? (aeval a2))
| BNot b1 => negb (beval b1)
| BAnd b1 b2 => andb (beval b1) (beval b2)
end.
beval calls aevalExample test_aeval1:
aeval <{ true && (1 < 3) }> = true.
Proof. reflexivity. Qed.
fact n = if n=0 then 1 else n * (fact n-1)
fact is simply a function that is equal to a mathematical expressionfact n to get a valuen-1 into fact<{ Z := X;
Y := 1;
while Z <> 0 do
Y := Y × Z;
Z := Z - 1
end }>
Z := X - replaces the value of Z in program state with the value of Xwhile Z<>0 do C - checks the value of Z in state and repeats or does not
repeat CC1 ; C2 - feeds the state resulting from C1 into C2The only reason for [state] is so that everything does not happen at once Buckaroo Banzai
ceval : State -> State is a next state function over StateDate: 2026-01-22
<{ Z := X; <st = ??>
Y := 1; <st = ??>
while Z <> 0 do
Y := Y × Z; <st = ??>
Z := Z - 1 <st = ??>
end }> <st = ??>
Maps.v given to usstring to some type ADefinition total_map (A:Type) := string -> A.`
AstringAn empty map returns the same default value for all inputs.
Definition t_empty {A:Type} (v : A) : total_map A := (fun _ => v)
v, the default valueA can be inferred from vUpdate a map by updating its function
Definition t_update {A:Type} (m : total_map A) (x : string) (v : A) :=
fun x' => if String.eqb x x' then v else (m x').
x:string and v:Av if x is the inputm x' if x is not the inputJust call the map as a function to access a value
(m x)
x is the string being accessedm is the map functionProgram state is a total map over nat
State := total_map natInitial state is the empty map over 0
t_empty 0 - initial stateUpdate state with t_update
t_update st "name" v - updating stateAccess with function call
st "name" - variable accessRocq provides some nifty notations:
__ !-> v - initial mapping with v as default value"name" !-> v ; m - update "name" with v in mMathematically $(x \mapsto v);m$ or $(x,v);m$
__|->0 - initial state over 0"x" !-> 3 ; __|->0 - assign 3 to “x”"y" !-> 4 ; "x" !-> 3 ; __|->0 - assign 3 to “x”, 4 to “y”"x" !-> 4 ; "y" !-> 4 ; "x" !-> 3 ; __|->0 - assign 3 to “x”, 4 to “y”, 4 to “x”X = (st "X")X := 3 = (X |-> 3 ; st)X := X+1 = (X |-> ((st "X") + 1) ; st)Watch for:
%string directivefunctional_extensionality theorem2026-01-27
ceval for commandsFixpoint ceval (st : state) (c : com) : state :=
match c with
| <{ skip }> =>
st
| <{ x := a }> =>
(x !-> aeval st a ; st)
| <{ c1 ; c2 }> =>
let st' := ceval st c1 in
ceval st' c2
| <{ if b then c1 else c2 end}> =>
if (beval st b)
then ceval st c1
else ceval st c2
| <{ while b do cb end }> =>
if (beval st b)
then st
else (ceval (ceval st cb) c)
end.
st we execute c resulting in st'falsefalse you can prove anythingceval on this program terminates<{ Z := X;
Y := 1;
while Z <> 0 do
Y := Y * Z;
Z := Z - 1
end }>
<{ Z := X;
Y := 1;
while Z <> 0 do
Y := Y * Z;
Z := Z + 1
end }>
while Badlyceval would seem to implement a recursive function just like beval and aeval | <{ while b do cb end }> =>
if (beval st b)
then st
else (ceval (ceval st cb) c)
ceval TerminateFixpoint ceval (fuel: nat) (st : state) (c : com) : state :=
match fuel with
| 0 => empty
| succ fuel' =>
match c with
| <{ skip }> =>
st
| <{ x := a }> =>
(x !-> aeval fuel st a ; st)
| <{ c1 ; c2 }> =>
let st' := ceval fuel' st c1 in
ceval fuel' st' c2
| <{ if b then c1 else c2 end}> =>
if (beval st b) then ceval fuel' st c1
else ceval fuel' st c2
| <{ while b do cb end }> =>
if (beval st b) then st
else (ceval fuel' (ceval fuel' st cb) c)
end
end.
fuel parameter gets smaller on every callfuel decreases on every callnat is a recursive type with a bottomfuel big enoughRelations have a ton of properties that we’ll talk about later
Three ways of saying “given A and B we know C”
$\frac{A,B}{C}$ - Tree style
$A,B \vdash C$ - Flat style
A -> B -> C - Rocq style
2026-01-27
forall v, aevalR(ANum(v),v)
forall t1 t2 v1 v2, aevalR(t1,v1) -> aevalR(t2,v2) -> aevalR(t1+t2,v1+v2)
forall t1 t2 v1 v2, aevalR(t1,v1) -> aevalR(t2,v2) -> bevalR(t1=t2,v1=?v2)
forall t1 t2 v1 v2,
bevalR(t1,v1) -> bevalR(t2,v2) -> bevalR(t1 && t2,(andb v1 v2))
forall c1 c2 st st' st'',
cevalR(st,c1,st') -> cevalR(st',c2,st'') -> cevalR(st,c1;c2,st'')
Inductive aevalR: aexp -> nat -> Prop :=
| aevalNum : forall v : nat, (aevalR (ANum v) v) -> True
| aevalPlus : forall t1 t2 v1 v2,
(aevalR t1 v1) -> (aevalR t2 v2) -> (aevalR (APlus t1 t2) (v1+v2)) -> True
| aevalTimes : forall t1 t2 v1 v2,
(aevalR t1 v1) -> (aevalR t2 v2) -> (aevalR (AMult t1 t2) (v1*v2)) -> True
| aevalMinus : forall t1 t2 v1 v2,
(aevalR t1 v1) -> (aevalR t2 v2) -> v1 <= v2 -> (aevalR (AMinus t1 t2) (v1-v2)) -> True.
aexp -> nat -> Prop is an evidence constructor defining when an ordered pair is in the relationaevalR is the smallest relation satisfying all evidence constructorsInductive aevalR: aexp -> nat -> Prop :=
| aevalNum : forall v : nat, (aevalR (ANum v) v)
| aevalPlus : forall t1 t2 v1 v2,
(aevalR t1 v1) -> (aevalR t2 v2) -> (aevalR (APlus t1 t2) (v1+v2))
| aevalTimes : forall t1 t2 v1 v2,
(aevalR t1 v1) -> (aevalR t2 v2) -> (aevalR (AMult t1 t2) (v1*v2))
| aevalMinus : forall t1 t2 v1 v2,
(aevalR t1 v1) -> (aevalR t2 v2) -> v1 <= v2 -> (aevalR (AMinus t1 t2) (v1-v2)).
True values are redundant.aevalFixpoint aeval (st : state) (* <--- NEW *)
(a : aexp) : nat :=
match a with
| ANum n => n
| AId x => st x (* <--- NEW *)
| <{a1 + a2}> => (aeval st a1) + (aeval st a2)
| <{a1 - a2}> => (aeval st a1) - (aeval st a2)
| <{a1 * a2}> => (aeval st a1) * (aeval st a2)
end.
aeval and beval simpleAId abstract syntax(AId x) dereferencingaeval StateDefinition init := (t_empty 0).
Definition st := (t_update init "X" 3).
Example ex4: aeval st (AId X) = 3.
Proof.
aevalRInductive aevalR: state -> aexp -> nat -> Prop :=
| aevalNum : forall st : state, v : nat, (aevalR st (ANum v) v)
| aevalId : forall st : state, x : string, (aevalR st (AId x) (st x))
| aevalPlus : forall st t1 t2 v1 v2,
(aevalR st t1 v1) -> (aevalR st t2 v2) -> (aevalR st (APlus t1 t2) (v1+v2))
| aevalTimes : forall st t1 t2 v1 v2,
(aevalR st t1 v1) -> (aevalR st t2 v2) -> (aevalR st (AMult t1 t2) (v1*v2))
| aevalMinus : forall st t1 t2 v1 v2,
(aevalR st t1 v1) -> (aevalR st t2 v2) -> v1 <= v2 -> (aevalR (AMinus st t1
t2) (v1-v2)).
aevalR and bevalRAId definition(AId x) dereferencing.aevalR and aeval with StateSwitch to Rocq
comInductive com : Type :=
| CSkip
| CAsgn (x : string) (a : aexp)
| CSeq (c1 c2 : com)
| CIf (b : bexp) (c1 c2 : com)
| CWhile (b : bexp) (c : com).
aexp and bexp are different from comcom instancesceval : s -> com -> s'
st=[c]=>st'
ceval st c st' == st=[c]=>st'
c transforms st into st'-----------
st =[SKIP]=> st
aevalR st t v
-------------------------
st =[ x:=t ]=> (x|->v);st
t to vx bound to v st =[c1]=> st'
st' =[c2]=> st''
--------------------------------
st =[c1;c2]=> st''
c1 in st to get st'c2 in st' to get st'' bevalR b true
st =[c1]=> st'
--------------------------------
st =[if b then c1 else c2]=> st'
bevalR b false
st =[c2]=> st'
--------------------------------
st =[if b then c1 else c2]=> st'
bb instructions cannot change statetrue then evaluate c1false then evaluate c2 bevalR b true
st =[c]=> st'
st' =[ while b do c end ]=> st''
--------------------------------
st =[while b do c end]=> st''
b to truec oncec can change statewhile again bevalR b false
-----------------------
st =[while b do c end]=> st
b to falseSKIPcevalc1;c2 evaluates in order?aeval and beval instead of aevalR and bevalR?
while iterate and terminate?if has two rules?“And if she weighs the same as a duck… she’s made of wood… and therefore… A WITCH!”. ~ Sir Bedevere from Monty Python and the Holy Grail
Why?
Definition aequiv p1 p2 : aexp :=
forall st : state, aexp st p1 = aexp st p2.
Definition cequiv (c1 c2 : com) : Prop :=
∀ (st st' : state),
(st =[ c1 ]=> st') <-> (st =[ c2 ]=> st').
c1 and c2 areTheorem skip_left : ∀ c,
cequiv
<{ skip; c }>
c.
Theorem skip_left : ∀ c,
cequiv
<{ skip; c }>
c.
Proof.
(* WORKED IN CLASS *)
intros c st st'.
split; intros H.
- (* -> *)
inversion H. subst.
inversion H2. subst.
assumption.
- (* <- *)
apply E_Seq with st.
+ apply E_Skip.
+ assumption.
Qed.
Theorem skip_right : forall c,
cequiv
<{ c; skip }>
c.
Theorem skip_right : forall c,
cequiv
<{ c; skip }>
c.
Proof.
intros c st st'.
split; intros H.
- inversion H. subst.
inversion H5. subst.
assumption.
- apply E_Seq with st'.
assumption.
apply E_Skip.
Qed.
Ltac H := inversion H; subst; remove H.
IF OptimizationTheorem if_true_simple : forall c1 c2,
cequiv
<{ if true then c1 else c2 end }>
c1.
ifif OptimizationTheorem if_true: ∀ b c1 c2,
bequiv b <{true}> →
cequiv
<{ if b then c1 else c2 end }>
c1.
if_true_simple?Theorem if_true: ∀ b c1 c2,
bequiv b <{true}> →
cequiv
<{ if b then c1 else c2 end }>
c1.
Proof.
intros b c1 c2 Hb.
split; intros H.
- (* -> *)
inversion H; subst.
+ (* b evaluates to true *)
assumption.
+ (* b evaluates to false (contradiction) *)
unfold bequiv in Hb. simpl in Hb.
rewrite Hb in H5.
discriminate.
- (* <- *)
apply E_IfTrue; try assumption.
unfold bequiv in Hb. simpl in Hb.
apply Hb. Qed.
Theorem if_false : ∀ b c1 c2,
bequiv b <{false}> →
cequiv
<{ if b then c1 else c2 end }>
c2.
Theorem if_false : forall b c1 c2,
bequiv b <{false}> ->
cequiv
<{ if b then c1 else c2 end }>
c2.
Proof.
intros b c1 c2. intros Hb.
split; intros H.
- inversion H; subst.
-- unfold bequiv in Hb. simpl in Hb.
rewrite Hb in H5.
inversion H5.
-- assumption.
- apply E_IfFalse.
-- unfold bequiv in Hb. simpl in Hb.
apply Hb.
-- assumption.
Qed.
if CasesTheorem swap_if_branches : forall b c1 c2,
if Cases (theorem)Theorem swap_if_branches : forall b c1 c2,
cequiv
<{ if b then c1 else c2 end }>
<{ if ~ b then c2 else c1 end }>.
if Cases (with the proof)Theorem swap_if_branches : forall b c1 c2,
cequiv
<{ if b then c1 else c2 end }>
<{ if ~ b then c2 else c1 end }>.
whilewhile falsewhile truewhile unfoldingwhile false<{while false do c end}>
while and Non-termination<{while true do c end}>
whileTheorem loop_unrolling : forall b c,
while TheoremTheorem loop_unrolling : forall b c,
cequiv
<{ while b do c end }>
<{ if b then c ; while b do c end else skip end }>.
aequiv a a'
-------------------------
cequiv (x := a) (x := a')
cequiv c1 c1'
cequiv c2 c2'
--------------------------
cequiv (c1;c2) (c1';c2')
Definition Assertion := state -> Prop.
fun st => st X = 3fun st => Truefun st => FalseDefinition assertion1 : Assertion := fun st ⇒ st X ≤ st Y.
Definition assertion2 : Assertion :=
fun st ⇒ st X = 3 ∨ st X ≤ st Y.
Definition assertion3 : Assertion :=
fun st ⇒ st Z × st Z ≤ st X ∧
¬ (((S (st Z)) × (S (st Z))) ≤ st X).
Definition assertion4 : Assertion :=
fun st ⇒ st Z = max (st X) (st Y).
fun st => st X = m
st is assumed to be defined as a function parameterDefinition assertion1 : Assertion := .
Definition assertion2 : Assertion := .
Definition assertion3 : Assertion := .
Definition assertion4 : Assertion := .
Definition assertion5 : Assertion := .
Definition assertion6 : Assertion := .
Definition assertion7 : Assertion := .
Definition assertion8 : Assertion := .
Definition assertion9 : Assertion := .
Definition assert_implies (P Q : Assertion) : Prop := forall st, P st -> Q st.
P ->> Q
P <<->> Q == P ->> Q /\ Q ->> P
{P} c {Q}
c
X := X + 1
forall m, X := X + 1
c
forall m, c
c
c
forall m, c
forall m, c
X := 5
X := X + 1
X := 5; Y := 0
X := 5
skip
skip
while true do skip end
while X = 0 do X := X + 1 end
while X ≠ 0 do X := X + 1 end
P and Q are assertionsst=[c]=>st' a command definitionP is true when c executesQ is true when it’s doneforall st st',
P st ->
st =[ c ]=> st' ->
Q st'
Definition valid_hoare_triple
(P : Assertion) (c : com) (Q : Assertion) : Prop :=
forall st st',
st =[ c ]=> st' ->
P st ->
Q st'.
c
Theorem hoare_post_true : forall (P Q : Assertion) c,
(forall st, Q st) ->
c .
Theorem hoare_post_false : forall (P Q : Assertion) c,
(forall st, not (P st)) ->
c .
-------------------- [hoare_SKIP]
SKIP
Theorem hoare_skip : forall P,
skip .
c2
c1
-----------------
c1;c2
X := Y
X=1 true after X:=Y???Y = 1
X:=Y
Y=1 mechanically?X=1X with YY=1
X:=X+Y
X=1X with X+Y X:=X+Y
X := a
X := a
(X + 1 <= 5)
X := X + 1
(3 = 3)
X := 3
(0 <= 3 /\ 3 <= 5)
X := 3
0
Definition assertion_sub X (a:aexp) (P:Assertion) : Assertion :=
fun (st : state) =>
(P%_assertion) (X !-> ((a:Aexp) st); st).
(X <= 5) [X !-> 3]
==
fun st =>
(fun st' => st' X <= 5)
(X !-> aeval st 3 ; st),
==
fun st =>
(fun st' => st' X <= 5)
(X !-> 3 ; st)
==
fun st =>
((X !-> 3 ; st) X) <= 5
==
fun st =>
3 <= 5.
Bounce over to Rocq for a couple of examples
P is weaker than Q if Q->P but ~P->QP is stronger than Q if P->Q but ~Q->PP is equivalent to Q if P->Q and Q->P
X := 3 ,
X := 3
True and (X = 3) [X |-> 3] are not syntactically equivalentTrue and (X = 3) [X !-> 3] are logically equivalent c
P <<->> P'
---------------------
c
P is equivalent to P' then P can replace P' c
P ->> P'
----------------------------- (hoare_consequence_pre)
c
c
Q' ->> Q
----------------------------- (hoare_consequence_post)
c
Theorem hoare_consequence_pre : forall (P P' Q : Assertion) c,
c ->
P ->> P' ->
c .
Theorem hoare_consequence_post : forall (P Q Q' : Assertion) c,
c ->
Q' ->> Q ->
c .
c
P ->> P'
Q' ->> Q
----------------------------- (hoare_consequence)
c
Theorem hoare_consequence : forall (P P' Q Q' : Assertion) c,
c ->
P ->> P' ->
Q' ->> Q ->
c .
eapply and eautoProof search attempts to build a proof automatically
auto and liaauto is a search tool over lemmas and introductions
A -> B style lemmaslia is a decision procedure for linear integer arithmetic (lia)ceval_deterministic proofapply and Specific RulesExample auto_example_1 : forall (P Q R: Prop),
(P -> Q) -> (Q -> R) -> P -> R.
Proof.
intros P Q R H1 H2 H3.
apply H2. apply H1. assumption.
Qed.
apply applicationsauto and DatabasesExample auto_example_1' : forall (P Q R: Prop),
(P -> Q) -> (Q -> R) -> P -> R.
Proof.
auto.
Qed.
intros and applyExample auto_example_2 : forall P Q R S T U : Prop,
(P -> Q) ->
(P -> R) ->
(T -> R) ->
(S -> T -> U) ->
((P -> Q) -> (P -> S)) ->
T ->
P ->
U.
Proof. auto. Qed.
autoautoauto using lemma_name.
lemma_name to the default hint databaseExample auto_example_3 : forall (P Q R S T U: Prop),
(P -> Q) ->
(Q -> R) ->
(R -> S) ->
(S -> T) ->
(T -> U) ->
P ->
U.
Proof.
auto will not solve this
auto 6 will solve this
auto_info to see what auto is up toautoHint Resolve T : core
core is the default hint databaseT to coreHint Constructors c : core.
c to coreHint Unfold d : core.
Hint Transparent d: core.
auto to unfold dauto to look inside d, but not unfold (rarely used)ceval_deterministic Againceval_deterministicapply and intros are Ltac primitivesLtac rwd H1 H2 := rewrite H1 in H2; discriminate
H1 and H2 are parameters; to sequence commandsLtac find_rwd :=
match goal with
H1: ?E = true, H2: ?E = false |- _
=>
rwd H1 H2
end.
match goal looks at the current Rocq proof goalH1 and H2 name antecentsA |- C
A matches antecedentC matches the consequent?E is a match variableLtac find_rwd :=
match goal with
H1: ?E = true, H2: ?E = false |- _
=>
rwd H1 H2
end.
?E that matches both antecedents
?E = true?E = falserwdfind_rwd does nothingfind_eqnLtac find_eqn :=
match goal with
H1: forall x, ?P x -> ?L = ?R,
H2: ?P ?X
|- _
=> rewrite (H1 X H2) in *
end.
?X defers knowing until later X := 1 ; Y := 2
P evaluatesExample ceval_example1:
empty_st =[
X := 2;
if (X <= 1)
then Y := 3
else Z := 4
end
]=> (Z !-> 4 ; X !-> 2).
Proof.
apply E_Seq with (X !-> 2).
- apply E_Asgn. reflexivity.
- apply E_IfFalse. reflexivity. apply E_Asgn. reflexivity.
Qed.
(X !-> 2) is the intermediate stateempty_st =[X:=2]=> (X !-> 2)
(X !-> 2) =[if command]=> (Z!->4;X!->2)
E_Seq is applied firstst =[c1]=> st' ->
st' =[c2]=> st'' ->
st =[c1;c2]=> st''
st and st'' by unification with the goalst' because X := 2 has not been processedst' if Rocq will be patienteapplyExample ceval'_example1:
empty_st =[
X := 2;
if (X <= 1)
then Y := 3
else Z := 4
end
]=> (Z !-> 4 ; X !-> 2).
Proof.
(* 1 *) eapply E_Seq.
- (* 2 *) apply E_Asgn.
(* 3 *) reflexivity.
- (* 4 *) apply E_IfFalse. reflexivity. apply E_Asgn. reflexivity.
Qed.
eapply E_Seq introduces ?X and instantiates E_Seq?Xempty_st =[X:=2]=> ?X
?X =[if command]=> (Z!->4;X!->2)
E_Asgn constrains ?X to be(X!->2) ; empty_st == (X!->2)
?X with the value we found:empty_st =[X:=2]=> (X!->2)
(X!->2) =[if command]=> (Z!->4;X!->2)
e Commandseexists - introduces a new existential variableeapply - we just saw thiseauto - uses eapply instead of applyinfo_eauto - uses eapply instead of applyeconsructor - instantiates a constructorStart with eapply and work from there