exercises.basics
From iris.base_logic Require Import iprop.
From iris.proofmode Require Import proofmode.
(* ################################ *)
From iris.proofmode Require Import proofmode.
(* ################################ *)
(* ================================================================= *)
Introduction
(* ================================================================= *)
Iris in Coq
Iris defines two Coq propositions for proving Iris propositions:
Iris is built on top of Coq, so to smoothen the experience, we will be
working with the Iris Proof Mode (IPM). The practical implication of
this is that we get a new context, called the spatial context, in
addition to the usual context, now called the non-spatial context.
Hypotheses from both contexts can be used to prove the goal.
The regular Coq tactics can still be used when we work within the
non-spatial context, but, in general, we shall use new tactics that
work natively with the spatial context. These new tactics start with
'i', and since many of them simply 'lift' the regular tactics to also
work with the spatial context, they borrow the regular names but with
the 'i' prefixed. For instance, instead of intros H we use
iIntros "H", and instead of apply H we use iApply "H". Note that
identifiers for hypotheses in the spatial context are strings, instead
of the usual Coq identifiers.
To see this in action we will prove the statement P ⊢ P, for all
P.
- ⊢ P asks whether P holds with no assumptions
- P ⊢ Q asks whether Q holds assuming P
Lemma asmb (P : iProp Σ) : P -∗ P.
Proof.
iIntros "H".
iApply "H".
Qed.
(*to Mansky: What is the difference between P ⊢ Q and ⊢ P -* Q?
We are using iIntros "H" to get the same context *)
(* WM: Technically, ⊢ P -* Q is syntactic sugar for emp ⊢ P -* Q.
But the tactics are often smart enough to drop the emp automatically, so in effect they're the same. *)
Lemma asm (P : iProp Σ) : P ⊢ P.
Proof.
To enter the Iris Proof Mode, we can use the tactic iStartProof.
However, most Iris tactics will automatically start the Iris Proof
Mode for you, so we can directly introduce P.
iIntros "H".
This adds P to the spatial context with the identifier "H". To
finish the proof, one would normally use either exact or apply.
So in Iris, we use either iExact or iApply.
iApply "H".
Qed.
(* ----------------------------------------------------------------- *)
Qed.
(* ----------------------------------------------------------------- *)
Technical Details
Fail Definition and_fail (P Q : iProp Σ) := P ∧ Q.
Definition and_success (P Q : iProp Σ) := (P ∧ Q)%I.
Definition and_success (P Q : iProp Σ) := (P ∧ Q)%I.
Iris uses ssreflect, but we will not assume knowledge of ssreflect
tactics. As such we will limit the use of ssreflect tactics whenever
possible. However, ssreflect overrides the definition of rewrite
changing its syntax and behaviour slightly. Notably:
- No commas between arguments, meaning you have to write rewrite H1 H2 instead of rewrite H1, H2.
- rewrite -H instead of rewrite <-H
- Occurrences are written in front of the argument rewrite {1 2 3}H instead of rewrite H at 1 2 3
- Rewrite can unfold definitions as rewrite /def which does the same as unfold def
(* ================================================================= *)
Basic Separation Logic
P1 ⊢ Q1⠀⠀⠀⠀⠀⠀P2 ⊢ Q2
----------------------
P1 ∗ P2 ⊢ Q1 ∗ Q2
To eliminate a separating conjunction we can use the tactic
iDestruct with the usual introduction pattern. However, like
with intros, we can use iIntros to eliminate directly.
iIntros "[HP HQ]". (* or iDestruct "H" as "HP HQ". *)
Unlike ∧, ∗ is not idempotent. Specifically, there are Iris
propositions for which ¬(P ⊢ P ∗ P). Because of this, it is
generally not possible to use iSplit to introduce ∗. The
iSplit tactic would duplicate the spatial context and is therefore
not available when the context is non-empty.
Fail iSplit.
Instead, Iris introduces the tactics iSplitL and iSplitR. These
allow you to specify how you want to separate your resources to
prove each subgoal. The hypotheses mentioned by iSplitL are given
to the left subgoal, and the remaining to the right. Conversely for
iSplitR.
iSplitL "HQ".
- iApply "HQ".
- iApply "HP".
Qed.
- iApply "HQ".
- iApply "HP".
Qed.
Separating conjunction has an analogue to implication which, instead
of introducing the antecedent to the assumptions with conjunction,
introduces it with separating conjunction. This connective is written
as P -∗ Q and pronounced `magic wand' or simply `wand'. Separation
is so widely used that P -∗ Q is treated specially; instead of
writing P ⊢ Q, we can write P -∗ Q, with the ⊢ being implicit.
That is, ⊢ P -∗ Q is notationally equivalent to P -∗ Q.
Writing a wand instead of entailment makes currying more natural. Here
is the Iris version of modus ponens. It is provable using only
iIntros and iApply.
Lemma modus_ponens (P Q : iProp Σ) : P -∗ (P -∗ Q) -∗ Q.
Proof.
(* exercise *)
iIntros "HP HQ".
iApply "HQ".
iApply "HP".
Qed.
Proof.
(* exercise *)
iIntros "HP HQ".
iApply "HQ".
iApply "HP".
Qed.
Just as with Coq tactics, Iris allows nesting of introduction
patterns. In fact, like Coq, Iris supports patterns of the form
(H1 & .. & H2 & H3) as a shorthand for [H1 .. [H2 H3] ..].
Exercise: try to use an introduction with a pattern of parentheses to
prove associativity for ∗. Note that ∗ is right-associative, so
P ∗ Q ∗ R is parsed as P ∗ (Q ∗ R).
Lemma sep_assoc_1 (P Q R : iProp Σ) : P ∗ Q ∗ R ⊢ (P ∗ Q) ∗ R.
Proof.
(* exercise *)
iIntros "(HP & HQ & HR)".
iSplitR "HR".
- iSplitL "HP".
+ iApply "HP".
+ iApply "HQ".
- iApply "HR".
Qed.
Proof.
(* exercise *)
iIntros "(HP & HQ & HR)".
iSplitR "HR".
- iSplitL "HP".
+ iApply "HP".
+ iApply "HQ".
- iApply "HR".
Qed.
Manually splitting a separation can become tedious. To alleviate this,
we can use the iFrame tactic. This tactic pairs up hypotheses with
pieces of a separation sequence. Its full use is described in
https://gitlab.mpi-sws.org/iris/iris/-/blob/master/docs/proof_mode.md?ref_type=heads#separation-logic-specific-tactics
Lemma sep_comm_v2 (P Q : iProp Σ) : P ∗ Q ⊢ Q ∗ P.
Proof.
iIntros "[HP HQ]".
iFrame.
Qed.
Fact asdfd (P : iProp Σ): P ⊣⊢ P.
Proof.
iSplit.
- iIntros "H".
iApply "H".
- iIntros "H".
iApply "H".
Qed.
Check asdfd.
Proof.
iIntros "[HP HQ]".
iFrame.
Qed.
Fact asdfd (P : iProp Σ): P ⊣⊢ P.
Proof.
iSplit.
- iIntros "H".
iApply "H".
- iIntros "H".
iApply "H".
Qed.
Check asdfd.
Bi-entailment of Iris propositions is denoted P ⊣⊢ Q. It is an
equivalence relation, and most connectives preserve this relation. It
is encoded using the setoid library with the typeclass Proper. It is
therefore possible to use the rewrite tactic inside the Iris Proof
Mode. Bi-entailment is defined as (P -∗ Q) ∧ (Q -∗ P), so it can be
decomposed using the iSplit tactic.
For hypotheses with multiple curried wands, it is necessary to specify
how to split the Iris context during application. This can be done as
iApply ("H" with "[H1 H2 H3] [H4 H5]"). Each set of square brackets
specifies the part of the context going to that argument. Let us
consider a specific example.
When applying "H", we get the subgoals P and Q. To specify that
we want to use "HP" to prove the first subgoal, and "HQ" the second,
we add "HP" in the first square bracket, and "HQ" in the second.
iApply ("H" with "[HP] [HQ]").
- iApply "HP".
- iApply "HQ".
Qed.
- iApply "HP".
- iApply "HQ".
Qed.
Hypotheses that fit arguments exactly can be supplied directly without
a square bracket to avoid trivial subgoals, as in the above. Try this
in the following exercise.
Lemma wand_adj (P Q R : iProp Σ) : (P -∗ Q -∗ R) ⊣⊢ (P ∗ Q -∗ R).
Proof.
(* exercise *)
iSplit.
- iIntros "H1".
iIntros "(HP & HQ)".
iApply ("H1" with "HP HQ").
- iIntros "H2".
iIntros "HP HQ".
iApply "H2".
iFrame.
Qed.
Proof.
(* exercise *)
iSplit.
- iIntros "H1".
iIntros "(HP & HQ)".
iApply ("H1" with "HP HQ").
- iIntros "H2".
iIntros "HP HQ".
iApply "H2".
iFrame.
Qed.
Disjunctions ∨ are treated just like disjunctions in Coq. The
introduction pattern [ _ | _ ] allows us to eliminate a disjunction,
while the tactics iLeft and iRight let us introduce them.
Prove that disjunction commutes.
Lemma or_comm (P Q : iProp Σ) : Q ∨ P ⊢ P ∨ Q.
Proof.
(* exercise *)
iIntros "[HQ | HP]".
(*to Mansky: for the frist branch, the solution is "by iRight", without "iApply "HQ""
Why is it not needed? *)
(* WM: "by" applies the "done" tactic after its argument, and "done" does simple things like iAssumption. *)
- iRight. iApply "HQ".
- iLeft. iApply "HP".
Qed.
Proof.
(* exercise *)
iIntros "[HQ | HP]".
(*to Mansky: for the frist branch, the solution is "by iRight", without "iApply "HQ""
Why is it not needed? *)
(* WM: "by" applies the "done" tactic after its argument, and "done" does simple things like iAssumption. *)
- iRight. iApply "HQ".
- iLeft. iApply "HP".
Qed.
We can even prove the usual elimination rule for or-elimination
written with separation. This version is, however, not very useful, as
it does not allow the two cases to share resources.
Lemma or_elim (P Q R : iProp Σ) : (P -∗ R) -∗ (Q -∗ R) -∗ P ∨ Q -∗ R.
Proof.
iIntros "HP HQ [HORP | HORQ]".
- iApply "HP". iApply "HORP".
- iApply "HQ". iApply "HORQ".
Qed.
Proof.
iIntros "HP HQ [HORP | HORQ]".
- iApply "HP". iApply "HORP".
- iApply "HQ". iApply "HORQ".
Qed.
Separating conjunction distributes over ordinary conjunction. This is
because separating conjunction is a form of conjunction, and the
introduction rule for conjunction is the same as for separating
conjunction.
Separating conjunction distributes over disjunction (for the same
reason as ordinary conjunction).
Lemma sep_or_distr (P Q R : iProp Σ) : P ∗ (Q ∨ R) ⊣⊢ P ∗ Q ∨ P ∗ R.
Proof.
(* exercise *)
iSplit.
- iIntros "[HP [HQ | HR]]".
+ iLeft. iFrame.
+ iRight. iFrame.
- iIntros "[[HP HQ]|[HP HR]]".
+ iFrame.
+ iFrame.
Qed.
Proof.
(* exercise *)
iSplit.
- iIntros "[HP [HQ | HR]]".
+ iLeft. iFrame.
+ iRight. iFrame.
- iIntros "[[HP HQ]|[HP HR]]".
+ iFrame.
+ iFrame.
Qed.
Iris has existential and universal quantifiers over any Coq type.
Existential quantifiers are proved using the iExists tactic, using
the same syntax as for exists. Elimination of existentials is done
through the pattern "[%_ _]" or as part of a "(_&..&_)" with a %
in front of the existential variable.
Lemma sep_ex_distr {A} (P : iProp Σ) (Φ : A → iProp Σ) :
(P ∗ ∃ x, Φ x) ⊣⊢ ∃ x, P ∗ Φ x.
Proof.
iSplit.
- iIntros "(HP & %x & HΦ)".
iExists x.
iFrame.
- (* exercise *)
(*
iIntros "H".
iDestruct "H" as "%x Hx" .
iDestruct "Hx" as "HP Hxx".
*)
iIntros "(%x & HP & Hxx)".
iSplitL "HP".
+ iApply "HP".
+ iExists x.
iApply "Hxx".
Qed.
(P ∗ ∃ x, Φ x) ⊣⊢ ∃ x, P ∗ Φ x.
Proof.
iSplit.
- iIntros "(HP & %x & HΦ)".
iExists x.
iFrame.
- (* exercise *)
(*
iIntros "H".
iDestruct "H" as "%x Hx" .
iDestruct "Hx" as "HP Hxx".
*)
iIntros "(%x & HP & Hxx)".
iSplitL "HP".
+ iApply "HP".
+ iExists x.
iApply "Hxx".
Qed.
Likewise, forall quantification works almost as in Coq. To introduce
universally quantified variables, you can either use iIntros (x y z)
or iIntros "%x %y %z". These patterns are interchangeable. To
specify the parameters of hypotheses, we write
iApply ("H" $! x y z).