{-# OPTIONS --cubical --safe #-}

module Lenses where

open import Prelude

record LensPart (A : Set a) (B : Set b) : Set (a ℓ⊔ b) where
  field
    get : B
    set : B  A
open LensPart public

record Lens (A : Set a) (B : Set b) : Set (a ℓ⊔ b) where
  field
    into : A  LensPart A B
    get-set :  s v  into (into s .set v) .get  v
    set-get :  s  into s .set (into s .get)  s
    set-set :  s v₁ v₂  into (into s .set v₁) .set v₂  into s .set v₂
open Lens public

infixl 4 _[_] _[_]≔_
_[_] : A  Lens A B  B
xs [ l ] = l .into xs .get

_[_]≔_ : A  Lens A B  B  A
xs [ l ]≔ x = l .into xs .set x

infixr 9 _⋯_
_⋯_ : Lens A B  Lens B C  Lens A C
(ab  bc) .into xs =
  let ab-xs = ab .into xs
      bc-ys = bc .into (ab-xs .get)
  in λ where .get  bc-ys .get
             .set  ab-xs .set  bc-ys .set
(ab  bc) .get-set s v = cong _[ bc ] (ab .get-set s _) ; bc .get-set (ab .into s .get) v
(ab  bc) .set-get s = cong (s [ ab ]≔_) (bc .set-get (ab .into s .get)) ; ab .set-get s
(ab  bc) .set-set s v₁ v₂ = ab .set-set s _ _ ; cong (s [ ab ]≔_) (cong (_[ bc ]≔ v₂) (ab .get-set _ _) ; bc .set-set _ _ _)

⦅fst⦆ : Lens (A × B) A
⦅fst⦆ .into (x , y) .get = x
⦅fst⦆ .into (_ , y) .set x = x , y
⦅fst⦆ .get-set _ _ = refl
⦅fst⦆ .set-get _ = refl
⦅fst⦆ .set-set _ _ _ = refl

⦅snd⦆ : Lens (A × B) B
⦅snd⦆ .into (x , y) .get = y
⦅snd⦆ .into (x , _) .set y = x , y
⦅snd⦆ .get-set _ _ = refl
⦅snd⦆ .set-get _ = refl
⦅snd⦆ .set-set _ _ _ = refl

⦅pair⦆ : Bool  Lens (A × A) A
⦅pair⦆ = bool ⦅fst⦆ ⦅snd⦆