You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
semantics-2023/theories/type_systems/stlc/lecture2.v

300 lines
9.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

(** * This file was shown during the lecture on 26.10.2023.
* It contrains duplicated code that is copy-pasted from e.g. lang.v
*)
From stdpp Require Export binders strings.
From stdpp Require Import options.
From semantics.lib Require Import maps.
(** * Simply Typed Lambda Calculus *)
(** ** Expressions and values. *)
(** [Z] is Coq's version of the integers.
All the standard operations, like [+], are defined on it.
The type [binder] is defined as [x ::= BNamed (s: string) | BAnon]
where BAnon can be used if we don't want to use the variable in
the function.
*)
Inductive expr :=
(* Base lambda calculus *)
| Var (x : string)
| Lam (x : binder) (e : expr)
| App (e1 e2 : expr)
(* Base types and their operations *)
| LitInt (n: Z)
| Plus (e1 e2 : expr).
Inductive val :=
| LitIntV (n: Z)
| LamV (x : binder) (e : expr).
(* Injections into expr *)
Definition of_val (v : val) : expr :=
match v with
| LitIntV n => LitInt n
| LamV x e => Lam x e
end.
(* try to make an expr into a val *)
Definition to_val (e : expr) : option val :=
match e with
| LitInt n => Some (LitIntV n)
| Lam x e => Some (LamV x e)
| _ => None
end.
Lemma to_of_val v : to_val (of_val v) = Some v.
Proof.
destruct v; simpl; reflexivity.
Qed.
Lemma of_to_val e v : to_val e = Some v -> of_val v = e.
Proof.
destruct e; simpl; try congruence.
all: injection 1 as <-; simpl; reflexivity.
Qed.
(* Inj is a type class for injective functions.
It is defined as:
[Inj R S f := ∀ x y, S (f x) (f y) -> R x y]
*)
#[export] Instance of_val_inj : Inj (=) (=) of_val.
Proof. by intros ?? Hv; apply (inj Some); rewrite <-!to_of_val, Hv. Qed.
(* A predicate which holds true whenever an
expression is a value. *)
Definition is_val (e : expr) : Prop :=
match e with
| LitInt n => True
| Lam x e => True
| _ => False
end.
Lemma is_val_spec e : is_val e v, to_val e = Some v.
Proof.
destruct e; simpl.
(* naive_solver is an automation tactic like intuition, firstorder, auto, ...
It is provided by the stdpp library. *)
all: naive_solver.
Qed.
Lemma is_val_of_val v : is_val (of_val v).
Proof.
apply is_val_spec. rewrite to_of_val. eauto.
Qed.
(* A small tactic that simplifies handling values. *)
Ltac simplify_val :=
repeat match goal with
| H: to_val (of_val ?v) = ?o |- _ => rewrite to_of_val in H
| H: is_val ?e |- _ => destruct (proj1 (is_val_spec e) H) as (? & ?); clear H
end.
(* values are values *)
Lemma is_val_val (v: val): is_val (of_val v).
Proof.
destruct v; simpl; done.
Qed.
(* we tell eauto to use the lemma is_val_val *)
#[global]
Hint Immediate is_val_val : core.
(** ** Operational Semantics *)
(** *** Substitution *)
Fixpoint subst (x : string) (es : expr) (e : expr) : expr :=
match e with
| LitInt n => LitInt n
(* The function [decide] can be used to decide propositions.
[decide P] is of type {P} + {¬ P}.
It can only be applied to propositions for which, by type class inference,
it can be determined that the proposition is decidable. *)
| Var y => if decide (x = y) then es else Var y
| Lam y e =>
Lam y $ if decide (BNamed x = y) then e else subst x es e
| App e1 e2 => App (subst x es e1) (subst x es e2)
| Plus e1 e2 => Plus (subst x es e1) (subst x es e2)
end.
(* We lift substitution to binders. *)
Definition subst' (mx : binder) (es : expr) : expr -> expr :=
match mx with BNamed x => subst x es | BAnon => id end.
(** *** Small-Step Semantics *)
(* We use right-to-left evaluation order,
which means in a binary term (e.g., e1 + e2),
the left side can only be reduced once the right
side is fully evaluated (i.e., is a value). *)
Inductive step : expr -> expr -> Prop :=
| StepBeta x e e' :
is_val e' ->
step (App (Lam x e) e') (subst' x e' e)
| StepAppL e1 e1' e2 :
is_val e2 ->
step e1 e1' ->
step (App e1 e2) (App e1' e2)
| StepAppR e1 e2 e2' :
step e2 e2' ->
step (App e1 e2) (App e1 e2')
| StepPlusRed (n1 n2 n3: Z) :
(n1 + n2)%Z = n3 ->
step (Plus (LitInt n1) (LitInt n2)) (LitInt n3)
| StepPlusL e1 e1' e2 :
is_val e2 ->
step e1 e1' ->
step (Plus e1 e2) (Plus e1' e2)
| StepPlusR e1 e2 e2' :
step e2 e2' ->
step (Plus e1 e2) (Plus e1 e2').
(* We make the tactic eauto aware of the constructors of [step].
Then it can automatically solve goals where we want to prove a step. *)
#[global] Hint Constructors step : core.
(* A term is reducible, if it can take a step. *)
Definition reducible (e : expr) :=
e', step e e'.
(** *** Big-Step Semantics *)
Inductive big_step : expr -> val -> Prop :=
| bs_lit (n : Z) :
big_step (LitInt n) (LitIntV n)
| bs_lam (x : binder) (e : expr) :
big_step (Lam x e) (LamV x e)
| bs_add e1 e2 (z1 z2 : Z) :
big_step e1 (LitIntV z1) ->
big_step e2 (LitIntV z2) ->
big_step (Plus e1 e2) (LitIntV (z1 + z2))%Z
| bs_app e1 e2 x e v2 v :
big_step e1 (@LamV x e) ->
big_step e2 v2 ->
big_step (subst' x (of_val v2) e) v ->
big_step (App e1 e2) v
.
#[export] Hint Constructors big_step : core.
Lemma big_step_vals (v: val): big_step (of_val v) v.
Proof.
induction v; econstructor.
Qed.
(*** Inversion ***)
(* We might want to show the following lemma about small step semantics: *)
Lemma val_no_step (v : val) (e : expr) :
step (of_val v) e -> False.
Proof.
(* destructing doesn't work: the different cases don't have enough information *)
destruct 1.
- admit.
- admit.
Restart.
remember (of_val v) as e' eqn:Heq.
destruct 1.
- (* Aha! Coq remembered that we had a value on the left-hand sidde of the
step *)
destruct v; discriminate.
- destruct v; discriminate.
- destruct v; discriminate.
- destruct v; discriminate.
- destruct v; discriminate.
- destruct v; discriminate.
Restart.
(* there's a tactic that does this for us: inversion *)
inversion 1.
all: destruct v; discriminate.
Qed.
(* The (non-recursive) eliminator for step in Coq *)
Lemma step_elim (P : expr -> expr -> Prop) :
( (x : binder) (e1 e2 : expr), is_val e2 ->
P (App (Lam x e1) e2) (subst' x e2 e1))
-> ( e1 e1' e2 : expr, is_val e2 -> step e1 e1' ->
P (App e1 e2) (App e1' e2))
-> ( e1 e2 e2' : expr, step e2 e2' ->
P (App e1 e2) (App e1 e2'))
-> ( n1 n2 n3 : Z, (n1 + n2)%Z = n3 ->
P (Plus (LitInt n1) (LitInt n2)) (LitInt n3))
-> ( e1 e1' e2 : expr, is_val e2 -> step e1 e1' ->
P (Plus e1 e2) (Plus e1' e2))
-> ( e1 e2 e2' : expr, step e2 e2' -> P (Plus e1 e2) (Plus e1 e2'))
-> e e' : expr, step e e' -> P e e'.
Proof.
destruct 7; eauto.
Qed.
(* An inversion operator for step in Coq *)
Lemma step_inversion (P : expr -> expr -> Prop) (e e' : expr) :
( (x : binder) (e1 e2 : expr), e = App (Lam x e1) e2 -> e' = subst' x e2 e1 ->
is_val e2 ->
P (App (Lam x e1) e2) (subst' x e2 e1))
-> ( e1 e1' e2 : expr, e = App e1 e2 -> e' = App e1' e2 ->
is_val e2 -> step e1 e1' ->
P (App e1 e2) (App e1' e2))
-> ( e1 e2 e2' : expr, e = App e1 e2 -> e' = App e1 e2' ->
step e2 e2' ->
P (App e1 e2) (App e1 e2'))
-> ( n1 n2 n3 : Z, e = Plus (LitInt n1) (LitInt n2) -> e' = LitInt n3 ->
(n1 + n2)%Z = n3 ->
P (Plus (LitInt n1) (LitInt n2)) (LitInt n3))
-> ( e1 e1' e2 : expr, e = Plus e1 e2 -> e' = Plus e1' e2 ->
is_val e2 -> step e1 e1' ->
P (Plus e1 e2) (Plus e1' e2))
-> ( e1 e2 e2' : expr, e = Plus e1 e2 -> e' = Plus e1 e2' ->
step e2 e2' ->
P (Plus e1 e2) (Plus e1 e2'))
-> step e e' -> P e e'.
Proof.
intros H1 H2 H3 H4 H5 H6.
destruct 1.
{ eapply H1. all: easy. }
{ eapply H2. all: easy. }
(* All the other cases are similar. *)
all: eauto.
Qed.
(* We can use the inversion operator to show that values cannot step. *)
Lemma val_no_step' (v : val) (e : expr) :
step (of_val v) e -> False.
Proof.
intros H. eapply step_inversion. 7: eauto.
all: intros; destruct v; discriminate.
Qed.
(* The following is another kind of inversion operator, as taught in the ICL lecture. *)
Definition step_inv (e e' : expr) : step e e' ->
(match e with
| App e1 e2 =>
( x f, e1 = (Lam x f) e' = subst' x e2 f)
( e1', is_val e2 step e1 e1')
( e2', step e2 e2')
| Plus e1 e2 =>
( n1 n2, e1 = LitInt n1 e2 = LitInt n2 LitInt (n1 + n2) = e')
( e1', is_val e2 step e1 e1')
( e2', step e2 e2')
| _ => False
end).
Proof.
intros H. destruct H; naive_solver.
Qed.