Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Basic Usage Tutorial

This tutorial teaches you to write coinductive proofs using paco. It assumes familiarity with the concepts in Theoretical Foundations.

Defining a Monotone Transformer

Every paco proof starts with a monotone relation transformer. The transformer specifies the one-step behavior of your coinductive relation.

import Paco

def EqF : Paco.MonoRel α :=
  ⟨fun R x y => x = y ∨ R x y, by
    intro R S hRS x y hxy
    cases hxy with
    | inl heq => exact Or.inl heq
    | inr hR => exact Or.inr (hRS x y hR)
  ⟩

This transformer relates x and y if they are equal or if R relates them. The monotonicity proof shows that enlarging R enlarges the output. The inl case handles equality. The inr case uses the hypothesis that R ≤ S.

Simple Coinduction

The paco_coind theorem is the main coinduction principle. It takes a witness relation R and shows paco F r x y when R is a post-fixpoint containing (x, y).

theorem paco_eq (x : α) : paco EqF ⊥ x x := by
  apply paco_coind EqF (fun a b => a = b) ⊥
  · intro a b hab
    subst hab
    simp only [Rel.sup_bot]
    exact Or.inl rfl
  · rfl

The first argument after EqF is the witness relation. Here it is equality. The second argument is the accumulator, which is for proofs without accumulation.

The first goal shows the witness is a post-fixpoint. The second goal shows the witness contains (x, x).

Using the Coind Wrapper

The coind wrapper from Paco.Coind simplifies the interface. It reorders arguments and handles the accumulator case.

theorem paco_eq' (x : α) : paco EqF ⊥ x x := by
  apply coind (R := fun a b => a = b)
  · rfl
  · intro a b hab
    subst hab
    exact Or.inl rfl

The witness comes first, then the membership proof, then the step function. The step function does not need the ⊔ r part since the accumulator is handled internally.

Working with the Accumulator

When you have facts to accumulate, use coind_acc. The step function receives R ⊔ r as the recursive relation.

theorem paco_with_acc (x : α) (r : Rel α) (hr : r x x) : paco EqF r x x := by
  apply coind_acc (R := fun a b => a = b)
  · rfl
  · intro a b hab
    subst hab
    exact Or.inl rfl

The accumulator r appears in the goal type. The step function can use facts from r by returning them on the right side of the disjunction.

Using Tactics

The tactic interface provides commands that mirror the Coq paco library.

pfold

The pfold tactic transforms a goal of paco F r x y into F (upaco F r) x y.

theorem test_pfold (x : α) : paco EqF ⊥ x x := by
  pfold
  exact Or.inl rfl

After pfold, the goal becomes EqF (upaco EqF ⊥) x x. The transformer expects either equality or a recursive call through upaco.

pstep

The pstep tactic moves into the paco side of upaco. It transforms upaco F r x y into F (upaco F r) x y.

theorem test_pstep (x : α) : upaco EqF ⊥ x x := by
  pstep
  exact Or.inl rfl

Use pstep when you need to make progress on a upaco goal.

pbase

The pbase tactic uses the accumulator side of upaco. It transforms upaco F r x y into r x y.

theorem test_pbase (x y : α) (hr : r x y) : upaco EqF r x y := by
  pbase
  exact hr

Use pbase when you can discharge the goal using accumulated facts.

pclearbot

The pclearbot tactic simplifies upaco F ⊥ to paco F ⊥.

theorem test_pclearbot (x y : α) (h : paco EqF ⊥ x y) : upaco EqF ⊥ x y := by
  pclearbot
  exact h

This is useful when the accumulator is empty and you have a paco hypothesis.

Unfolding Hypotheses

The paco_unfold theorem exposes the F-structure inside a paco hypothesis.

example (x y : α) (h : paco EqF ⊥ x y) : EqF (upaco EqF ⊥) x y :=
  paco_unfold EqF ⊥ x y h

After unfolding, you can case split on the disjunction to handle equality and recursive cases separately.

Example: Reflexivity

This complete example proves that paco EqF ⊥ is reflexive.

theorem paco_refl (x : α) : paco EqF ⊥ x x := by
  pfold
  left
  rfl

The proof folds into paco, then provides the left disjunct (equality) with rfl.

Additional Tactics

The library provides several convenience tactics for common patterns.

pmult

The pmult tactic absorbs nested paco into a single paco. It applies the accumulation lemma paco F (paco F r) ≤ paco F r.

theorem nested_paco (h : paco EqF (paco EqF r) x y) : paco EqF r x y := by
  pmult
  exact h

Use pmult when composing lemmas that each return paco results.

pdestruct

The pdestruct tactic combines unfold, destruct, and bot-clearing in one step.

example (h : paco EqF ⊥ x y) : x = y ∨ paco EqF ⊥ x y := by
  pdestruct h
  · left; assumption
  · right; assumption

This is equivalent to punfold h; cases h; pclearbot. The original hypothesis is preserved.

pinversion

The pinversion tactic is similar to pdestruct but explicitly preserves the original hypothesis.

example (h : paco EqF ⊥ x y) : x = y ∨ paco EqF ⊥ x y := by
  pinversion h
  · left; assumption
  · right; assumption

Use pinversion when you need to reference the original paco hypothesis after case analysis.

Choosing a Witness Relation

The witness relation R determines what you can prove. A good witness satisfies two properties. First, R must contain your target pair. Second, R must be a post-fixpoint of the parameterized transformer.

For reflexivity proofs, equality is often a good witness. For transitivity proofs, the transitive closure works well. For bisimulation, pairs of related states form the witness.

Start with the simplest relation that contains your target. If the post-fixpoint proof fails, expand the witness to include more pairs.

Troubleshooting

pfold does not apply

The pfold tactic expects a goal of the form paco F r x y. If your goal is upaco F r x y, use pstep instead. If your goal has a different form, you may need to unfold definitions first.

Witness is not a post-fixpoint

The step function must show R a b → F (R ⊔ r) a b. If this fails, your witness may be too small. Try expanding R to include more pairs that the F-step might produce.

Accumulator mismatch

When working with non-empty accumulators, ensure the accumulator in your goal matches the accumulator in your hypothesis. Use paco_mon to weaken from a smaller accumulator to a larger one.

Next Steps

See GPaco & Guards Tutorial for two-parameter coinduction. See Up-To Techniques Guide for enhanced reasoning with closures.