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.

340 lines
9.9 KiB

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.
Lemma big_step_inv_vals (v w: val): big_step (of_val v) w v = w.
Proof.
destruct v; inversion 1; eauto.
Qed.
(** *** Contextual Semantics *)
(** Base reduction *)
Inductive base_step : expr expr Prop :=
| BetaS x e1 e2 e' :
is_val e2
e' = subst' x e2 e1
base_step (App (Lam x e1) e2) e'
| PlusS e1 e2 (n1 n2 n3 : Z):
e1 = (LitInt n1)
e2 = (LitInt n2)
(n1 + n2)%Z = n3
base_step (Plus e1 e2) (LitInt n3).
Inductive ectx :=
| HoleCtx
| AppLCtx (K: ectx) (v2 : val)
| AppRCtx (e1 : expr) (K: ectx)
| PlusLCtx (K: ectx) (v2 : val)
| PlusRCtx (e1 : expr) (K: ectx).
Fixpoint fill (K : ectx) (e : expr) : expr :=
match K with
| HoleCtx => e
| AppLCtx K v2 => App (fill K e) (of_val v2)
| AppRCtx e1 K => App e1 (fill K e)
| PlusLCtx K v2 => Plus (fill K e) (of_val v2)
| PlusRCtx e1 K => Plus e1 (fill K e)
end.
(* filling a context with another context *)
Fixpoint comp_ectx (Ko Ki: ectx) :=
match Ko with
| HoleCtx => Ki
| AppLCtx K v2 => AppLCtx (comp_ectx K Ki) v2
| AppRCtx e1 K => AppRCtx e1 (comp_ectx K Ki)
| PlusLCtx K v2 => PlusLCtx (comp_ectx K Ki) v2
| PlusRCtx e1 K => PlusRCtx e1 (comp_ectx K Ki)
end.
Inductive contextual_step (e1 : expr) (e2 : expr) : Prop :=
Ectx_step K e1' e2' :
e1 = fill K e1'
e2 = fill K e2'
base_step e1' e2'
contextual_step e1 e2.
Definition contextual_reducible (e : expr) :=
e', contextual_step e e'.
#[export] Hint Constructors base_step : core.
#[export] Hint Constructors contextual_step : core.
(* Lemmas about the contextual semantics *)
Definition empty_ectx := HoleCtx.
Lemma fill_empty e : fill empty_ectx e = e.
Proof. done. Qed.
Lemma base_contextual_step e1 e2 :
base_step e1 e2 contextual_step e1 e2.
Proof. apply Ectx_step with empty_ectx; by rewrite ?fill_empty. Qed.
Lemma fill_comp (K1 K2 : ectx) e : fill K1 (fill K2 e) = fill (comp_ectx K1 K2) e.
Proof. induction K1; simpl; congruence. Qed.
Lemma fill_contextual_step K e1 e2 :
contextual_step e1 e2 contextual_step (fill K e1) (fill K e2).
Proof.
destruct 1 as [K' e1' e2' -> ->].
rewrite !fill_comp. by econstructor.
Qed.
(** Open and closed expressions *)
Fixpoint is_closed (X : list string) (e : expr) : bool :=
match e with
| Var x => bool_decide (x X)
| Lam x e => is_closed (x :b: X) e
| LitInt _ => true
| App e1 e2
| Plus e1 e2 => is_closed X e1 && is_closed X e2
end.
Notation closed X e := (Is_true (is_closed X e)).
#[export] Instance closed_proof_irrel X e : ProofIrrel (closed X e).
Proof. unfold closed. apply _. Qed.
#[export] Instance closed_dec X e : Decision (closed X e).
Proof. unfold closed. apply _. Defined.
Lemma closed_weaken X Y e : closed X e X Y closed Y e.
Proof. revert X Y; induction e; naive_solver (eauto; set_solver). Qed.
Lemma closed_weaken_nil X e : closed [] e closed X e.
Proof. intros. by apply closed_weaken with [], list_subseteq_nil. Qed.
Lemma closed_subst X Y e x es :
closed Y es closed (x :: X) e closed (X ++ Y) (subst x es e).
Proof.
induction e as [y|y e IH|e1 e2|n|e1 e2]in X |-*; simpl; intros Hc1 Hc2; eauto.
- eapply bool_decide_unpack, elem_of_cons in Hc2 as [->|Hc2].
+ destruct decide; try congruence. eapply closed_weaken; eauto with set_solver.
+ destruct decide.
* eapply closed_weaken; eauto with set_solver.
* simpl. eapply bool_decide_pack. set_solver.
- destruct y as [|y]; simpl in *; eauto.
destruct decide as [Heq|].
+ injection Heq as ->. eapply closed_weaken; eauto. set_solver.
+ rewrite app_comm_cons. eapply IH; eauto.
eapply closed_weaken; eauto. set_solver.
- eapply andb_True. eapply andb_True in Hc2 as [H1 H2].
split; eauto.
- eapply andb_True. eapply andb_True in Hc2 as [H1 H2].
split; eauto.
Qed.
Lemma closed_subst_nil X e x es :
closed [] es closed (x :: X) e closed X (subst x es e).
Proof.
intros Hc1 Hc2. eapply closed_subst in Hc1; eauto.
revert Hc1. rewrite right_id; [done|apply _].
Qed.
Lemma closed_do_subst' X e x es :
closed [] es closed (x :b: X) e closed X (subst' x es e).
Proof. destruct x; eauto using closed_subst_nil. Qed.
Lemma subst_closed X e x es : closed X e x X subst x es e = e.
Proof.
induction e in X |-*; simpl; rewrite ?bool_decide_spec, ?andb_True; intros ??;
repeat case_decide; simplify_eq; simpl; f_equal; intuition eauto with set_solver.
Qed.
Lemma subst_closed_nil e x es : closed [] e subst x es e = e.
Proof. intros. apply subst_closed with []; set_solver. Qed.
Lemma val_no_step e e':
step e e' is_val e False.
Proof.
by destruct 1.
Qed.
Lemma val_no_step' (v : val) (e : expr) :
step (of_val v) e -> False.
Proof.
intros H. eapply (val_no_step _ _ H).
apply is_val_val.
Qed.
Ltac val_no_step :=
match goal with
| [H: step ?e1 ?e2 |- _] =>
solve [exfalso; eapply (val_no_step _ _ H); done]
end.