module DynamicallyChecked.Lens where open import DynamicallyChecked.Partiality open import DynamicallyChecked.Utilities open import Function open import Data.Product open import Relation.Nullary open import Relation.Binary open import Relation.Binary.PropositionalEquality record Lens (S V : Set) : Set₁ where field put : S → V → Par S get : S → Par V PutGet : {s : S} {v : V} {s' : S} → put s v ↦ s' → get s' ↦ v GetPut : {s : S} {v : V} → get s ↦ v → put s v ↦ s infix 1 _⇆_ _⇆_ : Set → Set → Set₁ _⇆_ = Lens _≼ᴾ_ : {A : Set} → Par A → Par A → Set₁ mx ≼ᴾ my = ∀ {a} → mx ↦ a → my ↦ a _≈ᴾ_ : {A : Set} → Par A → Par A → Set₁ mx ≈ᴾ my = (mx ≼ᴾ my) × (my ≼ᴾ mx) uniqueness-lemma : {S V : Set} (l l' : S ⇆ V) {s : S} {v : V} → ({s : S} {v : V} → Lens.put l s v ≼ᴾ Lens.put l' s v) → Lens.get l s ↦ v → Lens.get l' s ↦ v uniqueness-lemma l l' put-≼ = Lens.PutGet l' ∘ put-≼ ∘ Lens.GetPut l uniqueness : {S V : Set} (l l' : S ⇆ V) → ({s : S} {v : V} → Lens.put l s v ≈ᴾ Lens.put l' s v) → {s : S} → Lens.get l s ≈ᴾ Lens.get l' s uniqueness l l' put-eq = uniqueness-lemma l l' (proj₁ put-eq) , uniqueness-lemma l' l (proj₂ put-eq) iso-lens : {A B : Set} → A ≅ B → A ⇆ B iso-lens iso = record { put = const (Iso.from iso) ; get = Iso.to iso ; PutGet = Iso.from-to-inverse iso ; GetPut = Iso.to-from-inverse iso } skip-lens : {S V : Set} → Decidable (_≡_ {A = V}) → (S → V) → S ⇆ V skip-lens {S} {V} _≟_ f = record { put = put ; get = return ∘ f ; PutGet = λ put↦ → return (PutGet put↦) ; GetPut = λ { {_} {._} (return refl) → GetPut } } where put : S → V → Par S put s v with f s ≟ v put s v | yes _ = return s put s v | no _ = fail PutGet : {s : S} {v : V} {s' : S} → put s v ↦ s' → f s' ≡ v PutGet {s} {v} put↦ with f s ≟ v PutGet (return refl) | yes refl = refl PutGet () | no _ GetPut : {s : S} → put s (f s) ↦ s GetPut {s} with f s ≟ f s GetPut {s} | yes _ = return refl GetPut {s} | no neq with neq refl GetPut {s} | no neq | () infixr 3 _↔_ _↕_ _◂_ _▸_ _↔_ : {A B C : Set} → A ⇆ B → B ⇆ C → A ⇆ C l ↔ r = record { put = λ a c → Lens.get l a >>= λ b → Lens.put r b c >>= Lens.put l a ; get = Lens.get r <=< Lens.get l ; PutGet = λ { (get-l-a↦b >>= put-r-b↦b' >>= put-l-a-b'↦a') → Lens.PutGet l put-l-a-b'↦a' >>= Lens.PutGet r put-r-b↦b' } ; GetPut = λ { (get-l-a↦b >>= get-r-b↦c) → get-l-a↦b >>= Lens.GetPut r get-r-b↦c >>= Lens.GetPut l get-l-a↦b } } _◂_ : {A B C : Set} → A ⇆ B → B ≅ C → A ⇆ C l ◂ iso = record { put = λ a c → Iso.from iso c >>= Lens.put l a ; get = Iso.to iso <=< Lens.get l ; PutGet = λ { (from-v↦v' >>= put-s-v'↦s') → Lens.PutGet l put-s-v'↦s' >>= Iso.from-to-inverse iso from-v↦v' } ; GetPut = λ { (get-s↦v >>= to-v↦v') → Iso.to-from-inverse iso to-v↦v' >>= Lens.GetPut l get-s↦v } } _▸_ : {A B C : Set} → A ≅ B → B ⇆ C → A ⇆ C iso ▸ l = iso-lens iso ↔ l _↕_ : {A B C D : Set} → A ⇆ B → C ⇆ D → A × C ⇆ B × D l ↕ r = record { put = λ { (a , c) (b , d) → liftPar₂ _,_ (Lens.put l a b) (Lens.put r c d) } ; get = λ { (a , c) → liftPar₂ _,_ (Lens.get l a) (Lens.get r c) } ; PutGet = λ { (put-l↦ >>= put-r↦ >>= return refl) → Lens.PutGet l put-l↦ >>= Lens.PutGet r put-r↦ >>= return refl } ; GetPut = λ { (get-l↦ >>= get-r↦ >>= return refl) → Lens.GetPut l get-l↦ >>= Lens.GetPut r get-r↦ >>= return refl } }