Library liblayers.lib.Lens
Require Import Coq.Program.Basics.
Require Import Coq.Classes.Morphisms.
Require Import Coq.Classes.RelationClasses.
Require Export Coq.Classes.RelationPairs.
Require Import Coq.Setoids.Setoid.
Require Import Coq.Classes.Morphisms.
Require Import Coq.Classes.RelationClasses.
Require Export Coq.Classes.RelationPairs.
Require Import Coq.Setoids.Setoid.
Interface
Class LensOps {S V: Type} (π: S → V) := {
set: V → S → S
}.
Arguments set {S V} π {_} _ _ : simpl never.
Class LensSetGet {S V} π
`{lens_ops: LensOps S V π} :=
{
lens_set_get s:
set π (π s) s = s
}.
Class LensGetSet {S V} π
`{lens_ops: LensOps S V π} :=
{
lens_get_set v s:
π (set π v s) = v
}.
Class LensSetSet {S V} π
`{lens_set: LensOps S V π} :=
{
lens_set_set u v s:
set π u (set π v s) = set π u s
}.
Class Lens {S V} π `{lens_ops: LensOps S V π} := {
lens_lens_set_get :> LensSetGet π;
lens_lens_get_set :> LensGetSet π;
lens_lens_set_set :> LensSetSet π
}.
The modify operation
Section MODIFY.
Context {S V π} `{HSV: Lens S V π}.
Definition modify (f: V → V) (s: S) :=
set π (f (π s)) s.
Lemma lens_unfold_modify f s:
modify f s = set π (f (π s)) s.
Proof.
reflexivity.
Qed.
End MODIFY.
Arguments modify {S V} π {_} _ _.
The same_context relation
Section SAMECONTEXT.
Context {S V π} `{lens_ops: LensOps S V π} `{HSV: LensSetSet S V π}.
Definition same_context: relation S :=
fun s t ⇒ ∀ v, set π v s = set π v t.
Lemma lens_set_same_context v s:
same_context (set π v s) s.
Proof.
intro u.
apply lens_set_set.
Qed.
Lemma lens_modify_same_context f s:
same_context (modify π f s) s.
Proof.
intro u.
unfold modify.
apply lens_set_set.
Qed.
Global Instance same_context_equiv: Equivalence same_context.
Proof.
split.
× intros s v.
reflexivity.
× intros s t Hst u.
symmetry; now auto.
× intros s1 s2 s3 H12 H23 u.
transitivity (set π u s2); now auto.
Qed.
Global Instance same_context_set_mor:
Proper (eq ==> same_context ==> eq) (set π).
Proof.
intros u v Huv s t Hst.
subst.
apply Hst.
Qed.
Global Instance same_context_modify_mor f:
Proper (same_context ==> same_context) (modify π f).
Proof.
intros s t Hst u.
unfold modify.
rewrite !lens_set_set.
apply Hst.
Qed.
End SAMECONTEXT.
Arguments same_context {S V} π {_} _ _.
Consequences of LensGetSet
Section GETSET.
Context {S V} `{Hgs: LensGetSet S V}.
Lemma lens_get_modify f s:
π (modify π f s) = f (π s).
Proof.
apply lens_get_set.
Qed.
Theorem lens_eq_set (s: S) (u v: V):
set π u s = set π v s ↔ u = v.
Proof.
split; intros.
× rewrite <- (lens_get_set u s).
rewrite <- (lens_get_set v s).
apply f_equal.
assumption.
× apply (f_equal (fun x ⇒ set π x s)).
assumption.
Qed.
End GETSET.
Consequences of LensSetGet
Section SETGET.
Context {S V} `{HSV: LensSetGet S V}.
Lemma lens_same_context_eq s1 s2:
same_context π s1 s2 →
π s1 = π s2 →
s1 = s2.
Proof.
intros Hc Hv.
rewrite <- (lens_set_get s1).
rewrite <- (lens_set_get s2).
rewrite Hv.
apply Hc.
Qed.
End SETGET.
Orthogonal lenses
Class Orthogonal {S U V} (π: S → U) (ρ: S → V)
`{πs: !LensOps π}
`{ρs: !LensOps ρ}: Prop :=
{
lens_ortho_setr_setl u v s:
set ρ v (set π u s) = set π u (set ρ v s)
}.
Section ORTHOGONAL_LENSES.
Context {S U V} (π: S → U) (ρ: S → V).
Context `{πops: !LensOps π}.
Context `{ρops: !LensOps ρ}.
Context `{Hπρ: !Orthogonal π ρ}.
Lemma lens_ortho_getl_setr `{Hπgs: !LensGetSet π} `{Hπsg: !LensSetGet π} s v:
π (set ρ v s) = π s.
Proof.
rewrite <- (lens_set_get (π := π) s) at 1.
rewrite lens_ortho_setr_setl.
rewrite lens_get_set.
reflexivity.
Qed.
Lemma lens_ortho_getr_setl `{Hρgs: !LensGetSet ρ} `{Hρsg: !LensSetGet ρ} s u:
ρ (set π u s) = ρ s.
Proof.
rewrite <- (lens_set_get (π := ρ) s) at 1.
rewrite <- lens_ortho_setr_setl.
rewrite lens_get_set.
reflexivity.
Qed.
End ORTHOGONAL_LENSES.
Hint Rewrite
@lens_get_set
@lens_set_get
@lens_set_set
@lens_unfold_modify
@lens_eq_set
@lens_set_same_context
@lens_modify_same_context
using typeclasses eauto : lens.
Tactics
Ltac lens_norm_ortho :=
repeat progress
match goal with
| H: context [set ?β ?v (set ?α ?u ?s)] |- _ ⇒
rewrite (lens_ortho_setr_setl u v s) in H
| |- context [set ?β ?v (set ?α ?u ?s)] ⇒
rewrite (lens_ortho_setr_setl u v s)
| H: context [?α (set ?β ?v ?s)] |- _ ⇒
rewrite (lens_ortho_getl_setr α β s v) in H
| |- context [?α (set ?β ?v ?s)] ⇒
rewrite (lens_ortho_getl_setr α β s v)
| H: context [?β (set ?α ?u ?s)] |- _ ⇒
rewrite (lens_ortho_getr_setl α β s u) in H
| |- context [?β (set ?α ?u ?s)] ⇒
rewrite (lens_ortho_getr_setl α β s u)
end.
Ltac lens_norm :=
repeat progress (simpl in *;
lens_norm_ortho;
autorewrite with lens in *).
Ltac lens_simpl :=
repeat progress (lens_norm; autorewrite with lens_simpl in *).
Ltac lens_unfold :=
repeat progress (lens_simpl; unfold set in *).
Section PAIR.
Global Instance fst_lensops A B: LensOps (@fst A B) := {
set a x := (a, snd x)
}.
Global Instance fst_lens A B: Lens (@fst A B).
Proof.
split; split; intuition.
Qed.
Global Instance snd_lensops A B: LensOps (@snd A B) := {
set b x := (fst x, b)
}.
Global Instance snd_lens A B: Lens (@snd A B).
Proof.
split; split; intuition.
Qed.
Here are some product-specific theorems.
Lemma fst_same_context_eq_snd {A B} (x y: A × B):
same_context (@fst A B) x y ↔ snd x = snd y.
Proof.
destruct x as [a1 b1].
destruct y as [a2 b2].
split; intro H.
× specialize (H a1).
inversion H.
reflexivity.
× intro a.
compute in ×.
congruence.
Qed.
Lemma snd_same_context_eq_fst {A B} (x y: A × B):
same_context (@snd A B) x y ↔ fst x = fst y.
Proof.
destruct x as [a1 b1].
destruct y as [a2 b2].
split; intro H.
× specialize (H b1).
inversion H.
reflexivity.
× intro b.
compute in ×.
congruence.
Qed.
End PAIR.
Hint Rewrite
@fst_same_context_eq_snd
@snd_same_context_eq_fst
: lens_simpl.
Section COMPOSE.
Context {A B C} (π: A → B) (ρ: B → C) `{Hπ: Lens _ _ π} `{Hρ: Lens _ _ ρ}.
Instance compose_lensops: LensOps (compose ρ π) := {
set c a := modify π (set ρ c) a
}.
Lemma lens_compose_get_simpl a:
compose ρ π a = ρ (π a).
Proof.
reflexivity.
Qed.
Lemma lens_compose_set_simpl c a:
set (compose ρ π) c a = modify π (set ρ c) a.
Proof.
reflexivity.
Qed.
Instance lens_compose_get_set: LensGetSet (compose ρ π).
Proof.
constructor; simpl; intros.
rewrite lens_compose_get_simpl.
rewrite lens_compose_set_simpl.
autorewrite with lens.
reflexivity.
Qed.
Instance lens_compose_set_get: LensSetGet (compose ρ π).
Proof.
constructor; simpl; intros.
rewrite lens_compose_get_simpl.
rewrite lens_compose_set_simpl.
autorewrite with lens.
reflexivity.
Qed.
Instance lens_compose_set_set: LensSetSet (compose ρ π).
Proof.
constructor; simpl; intros.
rewrite !lens_compose_set_simpl.
autorewrite with lens.
reflexivity.
Qed.
Instance lens_compose: Lens (compose ρ π) := {}.
End COMPOSE.
Hint Rewrite
@lens_compose_get_simpl
@lens_compose_set_simpl
using typeclasses eauto : lens_simpl.
Section INITIAL.
Context {V: Type} (π: Empty_set → V).
Global Instance lens_empty_ops: LensOps π := {
set a := Empty_set_rect _
}.
Global Instance lens_empty: Lens π.
Proof.
split; split; tauto.
Qed.
End INITIAL.
Section COPRODUCT.
Context {A B V} {α: A → V} {β: B → V}.
Context `{αops: LensOps A V α}.
Context `{βops: LensOps B V β}.
Definition fcoprod (f: A → V) (g: B → V): A + B → V :=
fun s ⇒ match s with
| inl a ⇒ f a
| inr b ⇒ g b
end.
Notation "[ f , g ]" := (fcoprod f g).
Instance lens_coprod_ops: LensOps [α, β] := {
set v ab :=
match ab with
| inl a ⇒ inl (set α v a)
| inr b ⇒ inr (set β v b)
end
}.
Instance lens_coprod_get_set:
LensGetSet α →
LensGetSet β →
LensGetSet [α,β].
Proof.
constructor.
intros v [a|b];
unfold set; simpl.
× lens_norm; reflexivity.
× lens_norm; reflexivity.
Qed.
Instance lens_coprod_set_get:
LensSetGet α →
LensSetGet β →
LensSetGet [α,β].
Proof.
constructor.
intros [a|b];
unfold set; simpl.
× lens_norm; reflexivity.
× lens_norm; reflexivity.
Qed.
Instance lens_coprod_set_set:
LensSetSet α →
LensSetSet β →
LensSetSet [α,β].
Proof.
constructor.
intros u v [a|b];
unfold set; simpl;
f_equal.
× lens_norm; reflexivity.
× lens_norm; reflexivity.
Qed.
Global Instance lens_coprod:
Lens α →
Lens β →
Lens [α,β].
Proof.
constructor;
typeclasses eauto.
Qed.
End COPRODUCT.
Section TERMINAL.
Context {S: Type} (π: S → unit).
Instance lens_unit_ops: LensOps (S := S) π := {
set u s := s
}.
Instance lens_unit: Lens π.
Proof.
assert (∀ u v: unit, u = v) by (intros [] []; reflexivity).
firstorder.
Qed.
End TERMINAL.
Interestingly, the product only works for orthogonal lenses.
Section PRODUCT.
Context {S A B} {α: S → A} {β: S → B}.
Context `{αops: LensOps S A α}.
Context `{βops: LensOps S B β}.
Definition fprod (f: S → A) (g: S → B): S → A × B :=
fun s ⇒ (f s, g s).
Notation "〈 f , g 〉" := (fprod f g).
Instance lens_prod_ops: LensOps 〈α, β〉 := {
set ab s := set α (fst ab) (set β (snd ab) s)
}.
Ltac go :=
unfold set, fprod;
simpl;
lens_norm;
try reflexivity.
Instance lens_prod_get_set:
Orthogonal α β →
LensSetGet β →
LensGetSet α →
LensGetSet β →
LensGetSet 〈α,β〉.
Proof.
constructor.
intros [a b] s.
go.
Qed.
Instance lens_prod_set_get:
LensSetGet α →
LensSetGet β →
LensSetGet 〈α,β〉.
Proof.
constructor.
intros s.
go.
Qed.
Instance lens_prod_set_set:
Orthogonal α β →
LensSetSet α →
LensSetSet β →
LensSetSet 〈α,β〉.
Proof.
constructor.
intros [a1 b1] [a2 b2] s.
go.
Qed.
Global Instance lens_prod:
Orthogonal α β →
Lens α →
Lens β →
Lens 〈α,β〉.
Proof.
constructor;
typeclasses eauto.
Qed.
End PRODUCT.