Library liblayers.lib.LogicalRelations
Require Import Coq.Program.Basics.
Require Export Coq.Classes.Morphisms.
Require Import Coq.Setoids.Setoid.
Require Import Coq.Lists.List.
Local Open Scope type.
Global Open Scope signature.
Require Export Coq.Classes.Morphisms.
Require Import Coq.Setoids.Setoid.
Require Import Coq.Lists.List.
Local Open Scope type.
Global Open Scope signature.
Prerequisites
Class NotEvar {A} (x: A).
Hint Extern 1 (NotEvar ?x) ⇒
not_evar x; constructor : typeclass_instances.
Sometimes we may want to introduce an auxiliary variable to help
with unification.
Class Convertible {A} (x y: A) :=
convertible: x = y.
Hint Extern 1 (Convertible ?x ?y) ⇒
eapply eq_refl : typeclass_instances.
Relations
Proper elements
Class ProperDef {A} (m: A) (R: rel A A) := proper_prf : R m m.
Notation "´@´ ´Proper´ T R m" := (@ProperDef T m R)
(at level 10, T at next level, R at next level, m at next level).
Notation Proper R m := (ProperDef m R).
Order on relations
Class subrel {A B} (R1 R2: rel A B) :=
subrel_at x y: R1 x y → R2 x y.
Global Instance subrel_preorder A B:
@PreOrder (rel A B) subrel.
Proof.
split; firstorder.
Qed.
Instance subrel_refl {A B} (R: rel A B):
subrel R R.
Proof.
firstorder.
Qed.
Definition rel_union {A B} (R1 R2: rel A B): rel A B :=
fun x y ⇒ R1 x y ∨ R2 x y.
Infix "∪" := rel_union (at level 50) : signature_scope.
Lemma rel_union_introl {A B} (R1 R2: rel A B):
subrel R1 (R1 ∪ R2).
Proof.
firstorder.
Qed.
Hint Extern 0 (subrel _ (_ ∪ _)) ⇒
eapply rel_union_introl : typeclass_instances.
Lemma rel_union_intror {A B} (R1 R2: rel A B):
subrel R2 (R1 ∪ R2).
Proof.
firstorder.
Qed.
Hint Extern 0 (subrel _ (_ ∪ _)) ⇒
eapply rel_union_introl : typeclass_instances.
Lemma rel_union_lub {A B} (R1 R2 R: rel A B):
subrel R1 R →
subrel R2 R →
subrel (R1 ∪ R2)%signature R.
Proof.
firstorder.
Qed.
Hint Extern 2 (subrel (_ ∪ _) _) ⇒
eapply rel_union_lub : typeclass_instances.
Definition rel_inter {A B} (R1 R2: rel A B): rel A B :=
fun x y ⇒ R1 x y ∧ R2 x y.
Infix "∩" := rel_inter (at level 40) : signature_scope.
Lemma rel_inter_eliml {A B} (R1 R2: rel A B):
subrel (R1 ∩ R2) R1.
Proof.
firstorder.
Qed.
Hint Extern 0 (subrel (_ ∩ _) _) ⇒
eapply rel_inter_eliml : typeclass_instances.
Lemma rel_inter_elimr {A B} (R1 R2: rel A B):
subrel (R1 ∩ R2) R2.
Proof.
firstorder.
Qed.
Hint Extern 0 (subrel (_ ∩ _) _) ⇒
eapply rel_inter_elimr : typeclass_instances.
Lemma rel_inter_glb {A B} (R R1 R2: rel A B):
subrel R R1 →
subrel R R2 →
subrel R (R1 ∩ R2).
Proof.
firstorder.
Qed.
Hint Extern 2 (subrel _ (_ ∩ _)) ⇒
eapply rel_inter_glb : typeclass_instances.
Lemma rel_inter_refl {A} (R1 R2: rel A A):
Reflexive R1 →
Reflexive R2 →
Reflexive (R1 ∩ R2).
Proof.
intros H1 H2.
split; reflexivity.
Qed.
Hint Extern 2 (Reflexive (_ ∩ _)) ⇒
eapply rel_inter_refl : typeclass_instances.
Lemma rel_inter_trans {A} (R1 R2: rel A A):
Transitive R1 →
Transitive R2 →
Transitive (R1 ∩ R2).
Proof.
intros H1 H2 x y z [Hxy1 Hxy2] [Hyz1 Hyz2].
split; etransitivity; eassumption.
Qed.
Hint Extern 2 (Transitive (_ ∩ _)) ⇒
eapply rel_inter_trans : typeclass_instances.
Lemma rel_inter_sym {A} (R1 R2: rel A A):
Symmetric R1 →
Symmetric R2 →
Symmetric (R1 ∩ R2).
Proof.
intros H1 H2 x y [Hxy1 Hxy2].
split; symmetry; assumption.
Qed.
Hint Extern 2 (Symmetric (_ ∩ _)) ⇒
eapply rel_inter_sym : typeclass_instances.
Global Instance rel_inter_flip_sym {A} (R: rel A A):
Symmetric (R ∩ flip R).
Proof.
intros x y [Hxy Hyx].
split; assumption.
Qed.
Definition rel_bot {A B}: rel A B :=
fun x y ⇒ False.
Notation "⊥" := rel_bot : signature_scope.
Definition rel_top {A B}: rel A B :=
fun x y ⇒ True.
Notation "⊤" := rel_top : signature_scope.
Hint Resolve (fun A B (x:A) (y:B) ⇒ I : rel_top x y).
Definition eqrel {A B}: rel (rel A B) (rel A B) :=
(subrel ∩ flip subrel).
Global Instance eqrel_equivalence A B:
Equivalence (@eqrel A B).
Proof.
unfold eqrel.
split; typeclasses eauto.
Qed.
Relators for function types
Non-dependent function types
Definition arrow_rel {A1 A2 B1 B2}:
rel A1 A2 → rel B1 B2 → rel (A1 → B1) (A2 → B2) :=
fun RA RB f g ⇒ ∀ x y, RA x y → RB (f x) (g y).
Notation "RA ==> RB" := (arrow_rel RA RB)
(at level 55, right associativity) : signature_scope.
Notation "RA ++> RB" := (arrow_rel RA RB)
(at level 55, right associativity) : signature_scope.
Notation "RA --> RB" := (arrow_rel (flip RA) RB)
(at level 55, right associativity) : signature_scope.
Global Instance arrow_subrel {A1 A2 B1 B2}:
Proper (subrel --> subrel ++> subrel) (@arrow_rel A1 A2 B1 B2).
Proof.
firstorder.
Qed.
Pointwise extension of a relation
Definition arrow_pointwise_rel A {B1 B2}:
rel B1 B2 → rel (A → B1) (A → B2) :=
fun RB f g ⇒ ∀ a, RB (f a) (g a).
Notation "- ==> R" := (arrow_pointwise_rel _ R)
(at level 55, right associativity) : signature_scope.
Global Instance arrow_pointwise_subrel {A B1 B2}:
Proper (subrel ++> subrel) (@arrow_pointwise_rel A B1 B2).
Proof.
firstorder.
Qed.
Global Instance arrow_pointwise_eq_subrel {A B1 B2} (RB1 RB2: rel B1 B2):
subrel RB1 RB2 →
subrel (- ==> RB1) (@eq A ==> RB2).
Proof.
intros HRB f g Hfg x y Hxy.
subst.
apply HRB.
apply Hfg.
Qed.
Dependent products
Definition forall_rel {V1 V2} {E: V1→V2→Type} {FV1: V1→Type} {FV2: V2→Type}:
(∀ v1 v2, E v1 v2 → rel (FV1 v1) (FV2 v2)) →
rel (∀ v1, FV1 v1) (∀ v2, FV2 v2) :=
fun FE f g ⇒
∀ v1 v2 (e: E v1 v2), FE v1 v2 e (f v1) (g v2).
Notation "∀ α : E v1 v2 , R" := (forall_rel (E := E) (fun v1 v2 α ⇒ R))
(at level 200, α ident, E at level 7, v1 ident, v2 ident, right associativity)
: signature_scope.
Notation "∀ α : E , R" := (forall_rel (E := E) (fun _ _ α ⇒ R))
(at level 200, α ident, E at level 7, right associativity)
: signature_scope.
Notation "∀ α , R" := (forall_rel (fun _ _ α ⇒ R))
(at level 200, α ident, right associativity)
: signature_scope.
Dependent pointwise extension
Definition forall_pointwise_rel {V: Type} {FV1 FV2: V → Type}:
(∀ v, rel (FV1 v) (FV2 v)) →
rel (∀ v, FV1 v) (∀ v, FV2 v) :=
fun FE f g ⇒
∀ v, FE v (f v) (g v).
Notation "∀ - , FE" := (forall_pointwise_rel (fun _ ⇒ FE))
(at level 200).
Notation "∀ - : ´rel´ , FE" := (forall_pointwise_rel (fun _ ⇒ FE))
(at level 200).
Notation "∀ - : ´rel´ v , FE" := (forall_pointwise_rel (fun v ⇒ FE))
(at level 200, a at level 0).
Tactics
Resolution process
- First, choose an orientation. Since R m m ↔ (flip R) m m, we need to consider both of those goals. Furthermore, we need to normalize flip R so that flip is pushed inward and any occurence of flip (flip Q) is reduced to Q.
- Then, if m is an applied function, we may want to look for a more general Proper theorem. So for instance, assuming that Q x x, we can use a theorem of the form Proper (Q ++> R) f to solve a goal of the form R (f x) (f x).
- Once we've chosen an orientation and a degree of partial application, we can finally look for a corresponding Proper instance.
Inductive processing_phase :=
| proper_orientation
| proper_partial_app
| proper_partial_arg
| proper_subrel.
Class ProperQuery (φs: list processing_phase) {A} (R: rel A A) (m: A) :=
proper_query_outcome: Proper R m.
The different processing_phases will peel themselves off the
list and generate subgoals to be handled by the next phase.
Ultimately the list becomes empty, and we look for a regular
instance of Proper.
Global Instance proper_query_finalize {A} (R: rel A A) (m: A):
Proper R m →
ProperQuery nil R m.
Proof.
tauto.
Qed.
Flipping Proper goals
Catch-all, default instance.
Flipping twice. This instance is also used when the first argument
is an existential variable, so that the resulting relation is itself
as general as possible.
Symmetric relations flip to themselves.
Instances for basic relators.
Instance arrow_flipsto {A1 A2 B1 B2} (RA: rel A1 A2) (RB: rel B1 B2) RA´ RB´:
FlipsTo RA RA´ →
FlipsTo RB RB´ →
FlipsTo (RA ++> RB) (RA´ ++> RB´).
Proof.
unfold FlipsTo, flip.
firstorder.
Qed.
The proper_orientation phase causes both orientations to be tried.
Global Instance proper_orientation_direct φs {A} (R: rel A A) (m: A):
ProperQuery φs R m →
ProperQuery (proper_orientation::φs) R m.
Proof.
tauto.
Qed.
Lemma proper_orientation_flip φs {A} (R R´: rel A A) (m: A):
FlipsTo R R´ →
ProperQuery φs R´ m →
ProperQuery (proper_orientation::φs) R m.
Proof.
firstorder.
Qed.
For proper_orientation_flip above, we only want to use the
first instance of FlipsTo found.
Ltac proper_orientation_flip :=
lazymatch goal with
| |- @ProperQuery _ ?A ?R ?m ⇒
let Rv := fresh in evar (Rv: rel A A);
let R´ := eval red in Rv in clear Rv;
let H := fresh in
assert (H: FlipsTo R R´) by typeclasses eauto;
eapply (@proper_orientation_flip _ A R R´ m H);
clear H
end.
Hint Extern 2 (ProperQuery (proper_orientation::_) _ _) ⇒
proper_orientation_flip : typeclass_instances.
Partial applications
Class ProperApplies A (B: A → Type) R (a: A) R´ (m: ∀ a, B a) :=
proper_applies : Proper R m → Proper R´ (m a).
Ltac proper_applies :=
let H := fresh in
unfold ProperApplies, ProperDef;
intro H;
eapply H;
eapply (@proper_query_outcome (proper_partial_arg::nil)).
Hint Extern 1 (ProperApplies _ _ _ _ _ _) ⇒
proper_applies : typeclass_instances.
The processing phase proper_partial_arg is used for proving that
a given argument can be applied. It is our version of
Morphisms.ProperProxy.
Instance proper_partial_arg_eq φs {A} (m: A):
ProperQuery (proper_partial_arg::φs) eq m.
Proof.
firstorder.
Qed.
Instance proper_partial_arg_reflexive φs {A} (R: rel A A) (m: A):
Reflexive R →
ProperQuery (proper_partial_arg::φs) R m.
Proof.
firstorder.
Qed.
Instance proper_partial_arg_default φs {A} (R: rel A A) (m: A):
NotEvar R →
ProperQuery φs R m →
ProperQuery (proper_partial_arg::φs) R m.
Proof.
firstorder.
Qed.
The proper_partial_app processing phase consists in using
proper_applies an arbitrary number of times. TODO: we may want to
use the Params class to limit the resulting search space to only
one possibility instead.
Global Instance proper_partial_app_bail φs {A} (R: rel A A) (m: A):
ProperQuery φs R m →
ProperQuery (proper_partial_app::φs) R m.
Proof.
tauto.
Qed.
Lemma proper_partial_app_arg φs {A} (B: A → Type) R a R´ m:
@ProperQuery (proper_partial_app::φs) (∀ a, B a) R m →
ProperApplies A B R a R´ m →
@ProperQuery (proper_partial_app::φs) (B a) R´ (m a).
Proof.
firstorder.
Qed.
When using proper_partial_app_arg, the unification of the goal
against the subterm (B a) is problematic, because usually the type
will be an arbitrary expression where a appears freely. Therefore,
we first need to put the goal in the right form. The tactic
dependent_type_of recovers the type of a term that can be applied,
as a function of its first argument.
Ltac dependent_type_of f arg T :=
let A := type of arg in
let x := fresh "x" in evar (x : A);
let fx := fresh "fx" in set (fx := f x);
pattern x in fx;
lazymatch type of fx with ?TT _ ⇒ set (T := TT) end;
let y := eval red in x in unify y arg;
subst x fx.
Ltac proper_partial_app_arg :=
lazymatch goal with
| |- ProperQuery (proper_partial_app::?φs) ?R (?op ?arg) ⇒
let T := fresh "T" in dependent_type_of op arg T;
eapply (proper_partial_app_arg φs T _ arg R op);
subst T
end.
Hint Extern 2 (ProperQuery (proper_partial_app::_) _ _) ⇒
proper_partial_app_arg : typeclass_instances.
Using subrel
Global Instance do_proper_subrel {A} φs (R1 R2: rel A A) (m: A):
ProperQuery φs R1 m →
subrel R1 R2 →
ProperQuery (proper_subrel::φs) R2 m.
Proof.
firstorder.
Qed.
Compatibility with setoid rewriting
Notation rewrite_proper_query :=
(proper_orientation :: proper_partial_app :: proper_subrel :: nil)
(only parsing).
Ltac convert_proper :=
repeat
match goal with
| |- appcontext C[respectful ?R1 ?R2] ⇒
let T´ := context C[R1 ==> R2] in change T´
| |- appcontext C[forall_relation ?R] ⇒
let T´ := context C[∀ -, R] in change T´
| |- Morphisms.Proper ?R ?m ⇒
change (ProperQuery rewrite_proper_query R m)
end.
We want convert_proper to be used for the initial Proper goal,
but we're not really interested in having it applied to the subgoals
generated by the original process in Coq.Classes.Morphisms, since
we have our own. The following tactic attempts to detect and reject
cases where some work has already been done.
Ltac use_convert_proper :=
match goal with
| _ : normalization_done |- _ ⇒
fail 1
| H : apply_subrelation |- _ ⇒
clear H;
progress convert_proper
end.
Hint Extern 1 (Morphisms.Proper _ _) ⇒
use_convert_proper : typeclass_instances.
The monotonicity of transitive relations is sometimes needed to
solve the goals generated by setoid rewriting.
Instance transitive_proper {A} (R: rel A A):
Transitive R →
Proper (R --> R ++> impl) R.
Proof.
firstorder.
Qed.
The monotonicity tactic
Ltac monotonicity :=
let rec apply_proper_prf m1 m2 :=
match m1 with
| m2 ⇒
let A := type of m1 in
let Rv := fresh "R" in evar (Rv: rel A A);
let Re := eval red in Rv in clear Rv;
let H := fresh in
assert (H: ProperQuery nil Re m1) by typeclasses eauto;
eapply H;
clear H
| ?f ?x1 ⇒
match m2 with
| ?g ?x2 ⇒ apply_proper_prf f g
end
end in
lazymatch goal with
| |- ?R ?m1 ?m2 ⇒
apply_proper_prf m1 m2
end.
Our version of Morphisms.f_equiv.
Ltac f_equiv :=
repeat monotonicity.
Our version of Morphisms.solve_proper. Note that we are somewhat
parcimonious with introductions because we don't want to cause undue
unfoldings. For instance, if we define the relation R1 from R2
as R1 x y := ∀ i, R2 (get i x) (get i y), we may create a
situation where applying the monotonicity theorem for get on a
goal of the form R2 (get i x) (get i y) produces a subgoal of the
form R1 x y, but then an introduction would get us back to where
we started. So we limit them to well-defined cases.
Ltac solve_monotonic_tac t :=
repeat
match goal with
| |- Proper _ _ ⇒ red
| |- flip _ _ _ ⇒ red
| |- _ ⇒ progress t
| |- _ _ _ ⇒ monotonicity
| |- (_ --> _) _ _ ⇒ let H := fresh in intros ? ? H; red in H
| |- (_ ++> _) _ _ ⇒ intros ? ? ?
| |- (- ==> _) _ _ ⇒ intro
| |- (∀ _, _) _ _ ⇒ intros ? ? ?
| |- (∀ -, _) _ _ ⇒ intro
| |- ∀ _, _ ⇒ intro
end.
Tactic Notation "solve_monotonic" :=
solve_monotonic_tac ltac:(eassumption || congruence || (now econstructor)).
Tactic Notation "solve_monotonic" tactic(t) :=
solve_monotonic_tac ltac:(eassumption || congruence || (now econstructor)|| t).
Exploiting foo_subrel instances
Furthermore, the following instance of subrel enables the use of
foo_subrel instances for rewriting along within applied relations.
So that for instance, a hypothesis H: subrel R1 R2 can be used for
rewriting in a goal of the form (R1 × R1´ ++> R) x y.
Instance subrel_pointwise_subrel {A B}:
subrel (@subrel A B) (eq ==> eq ==> impl).
Proof.
intros R1 R2 HR x1 x2 Hx y1 y2 Hy H; subst.
eauto.
Qed.
More relators for basic types
Inductive types
Nullary type constructors
Inductive Empty_set_rel: rel Empty_set Empty_set := .
Inductive unit_rel: rel unit unit :=
tt_rel: Proper unit_rel tt.
Global Existing Instance tt_rel.
Sum types
Inductive sum_rel: forall {A1 A2 B1 B2}, rel A1 A2 -> rel B1 B2 -> rel (A1+B1) (A2+B2):= | inl_rel: Proper (∀ RA : rel, ∀ RB : rel, RA ++> sum_rel RA RB) (@inl) | inr_rel: Proper (∀ RA : rel, ∀ RB : rel, RB ++> sum_rel RA RB) (@inr).However, to minimize the need for inversions we want to keep as many arguments as possible as parameters of the inductive type.
Inductive sum_rel {A1 A2} RA {B1 B2} RB: rel (A1 + B1) (A2 + B2) :=
| inl_rel_def: (RA ++> sum_rel RA RB) (@inl A1 B1) (@inl A2 B2)
| inr_rel_def: (RB ++> sum_rel RA RB) (@inr A1 B1) (@inr A2 B2).
Infix "+" := sum_rel : signature_scope.
Since it is not possible to retype the constructors after the
fact, we use the _def suffix when defining them, then redeclare
a corresponding, full-blown Proper instance.
Local Instance inl_rel:
Proper (∀ RA, ∀ RB, RA ++> RA + RB) (@inl).
Proof.
exact @inl_rel_def.
Qed.
Local Instance inr_rel:
Proper (∀ RA, ∀ RB, RB ++> RA + RB) (@inr).
Proof.
exact @inr_rel_def.
Qed.
Global Instance sum_subrel:
Proper (∀ -, ∀ -, subrel ++> ∀ -, ∀ -, subrel ++> subrel) (@sum_rel).
Proof.
intros A1 A2 RA1 RA2 HRA B1 B2 RB1 RB2 HRB.
intros x1 x2 Hx.
destruct Hx; constructor; eauto.
Qed.
Global Instance sum_rel_refl {A B} (R1: rel A A) (R2: rel B B):
Reflexive R1 → Reflexive R2 → Reflexive (R1 + R2).
Proof.
intros H1 H2 x.
destruct x; constructor; reflexivity.
Qed.
Global Instance sum_rel_trans {A B} (R1: rel A A) (R2: rel B B):
Transitive R1 → Transitive R2 → Transitive (R1 + R2).
Proof.
intros H1 H2 x y z Hxy Hyz.
destruct Hxy; inversion Hyz; constructor; etransitivity; eassumption.
Qed.
Global Instance sum_rel_sym {A B} (R1: rel A A) (R2: rel B B):
Symmetric R1 → Symmetric R2 → Symmetric (R1 + R2).
Proof.
intros H1 H2 x y Hxy.
destruct Hxy; constructor; symmetry; eassumption.
Qed.
Global Instance sum_rel_preorder {A B} (R1: rel A A) (R2: rel B B):
PreOrder R1 → PreOrder R2 → PreOrder (R1 + R2).
Proof.
split; typeclasses eauto.
Qed.
Hint Extern 0 (Proper _ (@inl)) ⇒ exact inl_rel : typeclass_instances.
Hint Extern 0 (Proper _ (@inr)) ⇒ exact inr_rel : typeclass_instances.
Inductive prod_rel {A1 A2} RA {B1 B2} RB: rel (A1 × B1) (A2 × B2):=
| pair_rel_def: (RA ++> RB ++> prod_rel RA RB) (@pair A1 B1) (@pair A2 B2).
Infix "×" := prod_rel : signature_scope.
Local Instance pair_rel:
Proper (∀ RA, ∀ RB, RA ++> RB ++> RA × RB) (@pair).
Proof.
exact @pair_rel_def.
Qed.
Local Instance fst_rel:
Proper (∀ RA, ∀ RB, RA × RB ==> RA) (@fst).
Proof.
intros A1 A2 RA B1 B2 RB.
intros _ _ [a1 a2 Ha b1 b2 Hb].
assumption.
Qed.
Local Instance snd_rel:
Proper (∀ RA, ∀ RB, RA × RB ==> RB) (@snd).
Proof.
intros A1 A2 RA B1 B2 RB.
intros _ _ [a1 a2 Ha b1 b2 Hb].
assumption.
Qed.
Global Instance prod_rel_refl {A B} (R1: rel A A) (R2: rel B B):
Reflexive R1 → Reflexive R2 → Reflexive (R1 × R2).
Proof.
intros H1 H2 x.
destruct x; constructor; reflexivity.
Qed.
Global Instance prod_rel_trans {A B} (R1: rel A A) (R2: rel B B):
Transitive R1 → Transitive R2 → Transitive (R1 × R2).
Proof.
intros H1 H2 x y z Hxy Hyz.
destruct Hxy; inversion Hyz; constructor; etransitivity; eassumption.
Qed.
Global Instance prod_rel_sym {A B} (R1: rel A A) (R2: rel B B):
Symmetric R1 → Symmetric R2 → Symmetric (R1 × R2).
Proof.
intros H1 H2 x y Hxy.
destruct Hxy; constructor; symmetry; eassumption.
Qed.
Global Instance prod_rel_preorder {A B} (R1: rel A A) (R2: rel B B):
PreOrder R1 → PreOrder R2 → PreOrder (R1 × R2).
Proof.
split; typeclasses eauto.
Qed.
Hint Extern 0 (Proper _ (@pair)) ⇒ exact pair_rel : typeclass_instances.
Hint Extern 0 (Proper _ (@fst)) ⇒ exact fst_rel : typeclass_instances.
Hint Extern 0 (Proper _ (@snd)) ⇒ exact snd_rel : typeclass_instances.
Inductive option_rel {A1 A2} (RA: rel A1 A2): rel (option A1) (option A2) :=
| Some_rel_def: (RA ++> option_rel RA) (@Some A1) (@Some A2)
| None_rel_def: option_rel RA (@None A1) (@None A2).
Local Instance Some_rel:
Proper (∀ R : rel A1 A2, R ++> option_rel R) (@Some).
Proof.
exact @Some_rel_def.
Qed.
Local Instance None_rel:
Proper (∀ R, option_rel R) (@None).
Proof.
exact @None_rel_def.
Qed.
Global Instance option_subrel:
Proper (∀ -, ∀ -, subrel ++> subrel) (@option_rel).
Proof.
intros A1 A2 RA1 RA2 HRA.
intros x1 x2 Hx.
destruct Hx; constructor; eauto.
Qed.
Hint Extern 0 (Proper _ (@Some)) ⇒ exact Some_rel : typeclass_instances.
Hint Extern 0 (Proper _ (@None)) ⇒ exact None_rel : typeclass_instances.
Inductive list_rel {A1 A2} (R: rel A1 A2): rel (list A1) (list A2) :=
| nil_rel_def: (list_rel R) (@nil A1) (@nil A2)
| cons_rel_def: (R ++> list_rel R ++> list_rel R) (@cons A1) (@cons A2).
Local Instance nil_rel:
Proper (∀ R, list_rel R) (@nil).
Proof.
exact @nil_rel_def.
Qed.
Local Instance cons_rel:
Proper (∀ R, R ++> list_rel R ++> list_rel R) (@cons).
Proof.
exact @cons_rel_def.
Qed.
Hint Extern 0 (Proper _ (@nil)) ⇒ exact nil_rel : typeclass_instances.
Hint Extern 0 (Proper _ (@cons)) ⇒ exact cons_rel : typeclass_instances.
Global Instance app_rel:
Proper (∀ R : rel, list_rel R ++> list_rel R ++> list_rel R) (@app).
Proof.
intros A1 A2 R l1 l2 Hl.
induction Hl as [ | x1 x2 Hx l1 l2 Hl IHl]; simpl.
× firstorder.
× solve_monotonic.
apply IHl.
assumption.
Qed.
Goal ∀ A (a1 a2: A) B (b1 b2: B) (RA: rel A A), True.
Proof.
intros.
evar (T: Type); evar (R: rel T T); subst T;
assert (H1: ProperQuery (proper_partial_app::nil) R (@pair A A a1)); subst R.
typeclasses eauto.
instantiate (1 := RA) in H1.
evar (T: Type); evar (R: rel T T); subst T;
assert (H2: ProperQuery (proper_partial_app::nil) R (@pair A)); subst R.
typeclasses eauto.
instantiate (1 := RA) in H2.
evar (T: Type); evar (R: rel T T); subst T;
assert (H3: ProperQuery (proper_partial_app::nil) R (@inl A B a2)); subst R.
typeclasses eauto.
instantiate (1 := eq) in H3.
exact I.
Qed.
Goal
∀ A (a b: A) `(HR: Equivalence A) (H: R a b),
sum_rel R R (inl a) (inl b).
Proof.
intros.
rewrite H.
rewrite <- H.
reflexivity.
Qed.
This test checks that transitive_proper is used as expected.
Goal
∀ A (op: A → A → A) (R: rel A A) (x y z: A),
Proper (R ++> R ++> R) op →
PreOrder R →
R (op y x) (op x y) →
R (op (op z y) x) (op z (op x y)).
Proof.
intros A op R x y z Hop HR H.
rewrite <- H.
For your debugging convenience, here are the goals generated by
the rewrite above.
evar (RE: rel A A);
assert (Morphisms.Proper (RE ==> flip impl) (R (op (op z y) x))
∧ Morphisms.Proper (flip R ==> RE) (op z)); subst RE.
{
split.
× convert_proper.
proper_orientation_flip.
proper_partial_app_arg.
eapply proper_partial_app_bail.
eapply transitive_proper.
typeclasses eauto.
proper_applies.
typeclasses eauto.
× convert_proper.
typeclasses eauto.
}
Abort.
assert (Morphisms.Proper (RE ==> flip impl) (R (op (op z y) x))
∧ Morphisms.Proper (flip R ==> RE) (op z)); subst RE.
{
split.
× convert_proper.
proper_orientation_flip.
proper_partial_app_arg.
eapply proper_partial_app_bail.
eapply transitive_proper.
typeclasses eauto.
proper_applies.
typeclasses eauto.
× convert_proper.
typeclasses eauto.
}
Abort.
Goal
∀ A (a b: A) (R: rel A A) (H: R a b),
let f (x y: A × A) := (@pair (A+A) (A+A) (inr (fst x)) (inl (snd y))) in
Proper (R × ⊤ ++> ⊤ × R ++> (⊥ + R) × (R + ⊥)) f.
Proof.
intros; unfold f.
solve_monotonic.
Qed.
Goal
∀ {A1 A2 B1 B2} (R1 R1´: rel A1 A2) (R2 R2´: rel B1 B2),
subrel R1´ R1 →
subrel R2 R2´ →
subrel (R1 ++> R2) (R1´ ++> R2´).
Proof.
solve_monotonic.
Qed.
Goal
∀ A1 A2 B1 B2 C1 C2 (R1 R2: rel A1 A2) (R1´: rel B1 B2) (R: rel C1 C2),
subrel R1 R2 →
∀ x y,
(R2 ++> R) x y →
(R1 ++> R) x y.
Proof.
intros A1 A2 B1 B2 C1 C2 R1 R2 R1´ R HR12 x y H.
rewrite HR12.
assumption.
Qed.
FIXME: this should work as well.