Source code on Github
{-# OPTIONS --with-K #-}
import Relation.Binary.Reasoning.Setoid as BinSetoid

open import Prelude.Init hiding (_⊆_; _⊈_); open SetAsType
open import Prelude.General
open import Prelude.Maybes
open import Prelude.DecEq
open import Prelude.Decidable
open import Prelude.Applicative
open import Prelude.Semigroup
open import Prelude.Monoid
open import Prelude.InferenceRules
open import Prelude.Functor
open import Prelude.Apartness
open import Prelude.Setoid

module Prelude.Maps.Abstract.Interface (K V : Type) (σ : Level) where

record Mapᴵ : Type (lsuc σ) where
  constructor mkMapᴵ
  field
    Map : Type σ
    ∅ : Map
    _∪_ : Op₂ Map -- left-biased
    _⁉_ : Map → K → Maybe V
    _∈ᵈ_ : K → Map → Type
    instance Apart-Map : Map //^⦅ 0ℓ ⦆ Map

  instance _ = Apart-Map
    -- ⦃ Apart-Map ⦄ : Map //^⦅ 0ℓ ⦆ Map

  -- syntactic sugar
  syntax Map {K = K} {V = V} = Map⟨ K ↦ V ⟩

  -- operator precedence
  infix  6 _⁉_
  infixr 5 _∪_
  infix  4 _∈ᵈ_ _∉ᵈ_ _♯ᵐ_ _≈ᵐ_

  _♯ᵐ_ = Rel₀ Map ∋ _♯_

  _∉ᵈ_ : K → Map → Type
  k ∉ᵈ m = ¬ (k ∈ᵈ m)

  open Derive-⊆-from-∈ _∈ᵈ_ public renaming
    ( _⊆_ to _⊆ᵈ_; _⊈_ to _⊈ᵈ_; ⊆-trans to ⊆ᵈ-trans
    ; _⊇_ to _⊇ᵈ_; _⊉_ to _⊉ᵈ_; ⊇-trans to ⊇ᵈ-trans
    )

  -- equivalence
  instance
    Setoid-Map : ISetoid Map
    Setoid-Map = λ where
      .relℓ → _
      ._≈_ m m′ → ∀ k → m ⁉ k ≡ m′ ⁉ k

    SetoidLaws-Map : SetoidLaws Map
    SetoidLaws-Map = record {isEquivalence = record
      { refl = λ _ → refl
      ; sym = λ p k → sym (p k)
      ; trans = λ p q k → trans (p k) (q k)
      }}

  _≈ᵐ_ = Rel₀ Map ∋ _≈_
  module ≈ᵐ-Reasoning = ≈-Reasoning {A = Map}
  open Alg _≈ᵐ_

  -- derived relations
  ⟨_⊎_⟩≡_ : 3Rel₀ Map
  ⟨ m ⊎ m′ ⟩≡ m″ = (m ♯ m′) × ((m ∪ m′) ≈ m″)

  _[_↦_] : Map → K → V → Type
  m [ k ↦ v ] = m ⁉ k ≡ just v

  _[_↦_]∅ : Map → K → V → Type
  m [ k ↦ v ]∅ = m [ k ↦ v ] × ∀ k′ → k′ ≢ k → k′ ∉ᵈ m

  field
    ⁉⇒∈ᵈ : ∀ s {k} → Is-just (s ⁉ k) → k ∈ᵈ s
    ∈ᵈ⇒⁉ : ∀ s {k} → k ∈ᵈ s → Is-just (s ⁉ k)

    -- _∪_ is left-biased
    ↦-∪⁺ˡ : ∀ s₁ s₂ {k v} → s₁ [ k ↦ v ] → (s₁ ∪ s₂) [ k ↦ v ]
    ↦-∪⁺ʳ : ∀ s₁ s₂ {k} → k ∉ᵈ s₁ → s₂ ⁉ k ≡ (s₁ ∪ s₂) ⁉ k

    -- commutativity
    ♯-comm : Symmetric _♯ᵐ_
    ∪-comm : ∀ s₁ s₂ → s₁ ♯ s₂ → (s₁ ∪ s₂) ≈ (s₂ ∪ s₁)

    -- congruences
    ♯-cong : ∀ (s₁ s₂ s₃ : Map) → s₁ ≈ᵐ s₂ → s₁ ♯ s₃ → s₂ ♯ s₃
    ∪-cong : ∀ s₁ s₂ s₃ → s₁ ≈ s₂ → (s₁ ∪ s₃) ≈ (s₂ ∪ s₃)
    ∈ᵈ-cong : ∀ k s₁ s₂ → s₁ ≈ s₂ → k ∈ᵈ s₁ → k ∈ᵈ s₂

    -- introduction/elimination of union
    ∈ᵈ-∪⁻ : ∀ k s₁ s₂ → k ∈ᵈ (s₁ ∪ s₂) → (k ∈ᵈ s₁) ⊎ (k ∈ᵈ s₂)
    ∈ᵈ-∪⁺ : ∀ k s₁ s₂ → (k ∈ᵈ s₁) ⊎ (k ∈ᵈ s₂) → k ∈ᵈ (s₁ ∪ s₂)
    ∪-chooseₗ : ∀ s₁ s₂ → s₁ ♯ s₂ → (∀ {k} → k ∉ᵈ s₂ → (s₁ ∪ s₂) ⁉ k ≡ s₁ ⁉ k)
    ∪-chooseᵣ : ∀ s₁ s₂ → s₁ ♯ s₂ → (∀ {k} → k ∈ᵈ s₂ → (s₁ ∪ s₂) ⁉ k ≡ s₂ ⁉ k)

    ♯-∪⁻ʳ : ∀ (s₁ s₂ s₃ : Map) → s₁ ♯ (s₂ ∪ s₃) → (s₁ ♯ s₂) × (s₁ ♯ s₃)
    ♯-∪⁻ˡ : ∀ (s₁ s₂ s₃ : Map) → (s₁ ∪ s₂) ♯ s₃ → (s₁ ♯ s₃) × (s₂ ♯ s₃)
    ♯-∪⁺ˡ : ∀ (s₁ s₂ s₃ : Map) → (s₁ ♯ s₃) × (s₂ ♯ s₃) → (s₁ ∪ s₂) ♯ s₃
    ♯-∪⁺ʳ : ∀ (s₁ s₂ s₃ : Map) → (s₁ ♯ s₂) × (s₁ ♯ s₃) → s₁ ♯ (s₂ ∪ s₃)

    -- associativity
    ∪-assocʳ : ∀ s₁ s₂ s₃ → s₁ ∪ (s₂ ∪ s₃) ≈ (s₁ ∪ s₂) ∪ s₃
    ∪≡-assocʳ : ∀ s₁ s₂ s₃ s → s₂ ♯ s₃ → ⟨ s₁ ⊎ (s₂ ∪ s₃) ⟩≡ s → ⟨ (s₁ ∪ s₂) ⊎ s₃ ⟩≡ s

  private variable
    s s₁ s₂ s₃ s₁₂ s₂₃ m m′ m₁ m₂ m₃ m₁₂ m₂₃ : Map
    k k′ : K
    v v′ : V
    f g : Map → Map

  module _ s {k} where
    ⁉⇒∉ᵈ : Is-nothing (s ⁉ k) → k ∉ᵈ s
    ⁉⇒∉ᵈ p k∈ with s ⁉ k | p | ∈ᵈ⇒⁉ _ k∈
    ... | just _ | M.All.just () | _

    ∉ᵈ⇒⁉ : k ∉ᵈ s → Is-nothing (s ⁉ k)
    ∉ᵈ⇒⁉ k∉ with s ⁉ k | ⁉⇒∈ᵈ s {k}
    ... | just _  | p = ⊥-elim $ k∉ (p auto)
    ... | nothing | _ = auto

  module _ s {k v} where
    ⁉⇒↦ : s ⁉ k ≡ just v → s [ k ↦ v ]
    ⁉⇒↦ = id

    ↦⇒⁉ : s [ k ↦ v ] → s ⁉ k ≡ just v
    ↦⇒⁉ = id

  ∉ᵈ-cong : ∀ {k s₁ s₂} → s₁ ≈ s₂ → k ∉ᵈ s₁ → k ∉ᵈ s₂
  ∉ᵈ-cong s≈ k∉ = k∉ ∘ ∈ᵈ-cong _ _ _ (≈-sym s≈)

  ↦-∪⁺ʳ′ : ∀ {s₁ s₂ k v} → k ∉ᵈ s₁ → s₂ [ k ↦ v ] → (s₁ ∪ s₂) [ k ↦ v ]
  ↦-∪⁺ʳ′ k∉ sk≡ = trans (sym (↦-∪⁺ʳ _ _ k∉)) sk≡

  ∪-congʳ : ∀ {s₁}{s₂}{s₃} → s₁ ♯ s₂ → s₂ ≈ s₃ → (s₁ ∪ s₂) ≈ (s₁ ∪ s₃)
  ∪-congʳ {s₁}{s₂}{s₃} s₁♯s₂ s₂≈s₃ =
    begin s₁ ∪ s₂ ≈⟨ ∪-comm _ _ s₁♯s₂ ⟩
          s₂ ∪ s₁ ≈⟨ ∪-cong _ _ _ s₂≈s₃ ⟩
          s₃ ∪ s₁ ≈⟨ ∪-comm _ _ (♯-cong _ _ _ s₂≈s₃ $ ♯-comm s₁♯s₂) ⟩
          s₁ ∪ s₃ ∎
    where open ≈-Reasoning

  ∈ᵈ-∪⁺ˡ : ∀ k s₁ s₂ → k ∈ᵈ s₁ → k ∈ᵈ (s₁ ∪ s₂)
  ∈ᵈ-∪⁺ˡ _ _ _ = ∈ᵈ-∪⁺ _ _ _ ∘ inj₁
  ∈ᵈ-∪⁺ʳ : ∀ k s₁ s₂ → k ∈ᵈ s₂ → k ∈ᵈ (s₁ ∪ s₂)
  ∈ᵈ-∪⁺ʳ _ _ _ = ∈ᵈ-∪⁺ _ _ _ ∘ inj₂

  ∉ᵈ-∪⁻ : k ∉ᵈ (s₁ ∪ s₂) → (k ∉ᵈ s₁) × (k ∉ᵈ s₂)
  ∉ᵈ-∪⁻ {k}{s₁}{s₂} k∉ = (k∉ ∘ ∈ᵈ-∪⁺ k s₁ s₂ ∘ inj₁) , (k∉ ∘ ∈ᵈ-∪⁺ k s₁ s₂ ∘ inj₂)

  ∉ᵈ-∪⁺ : (k ∉ᵈ s₁) × (k ∉ᵈ s₂) → k ∉ᵈ (s₁ ∪ s₂)
  ∉ᵈ-∪⁺ {k}{s₁}{s₂} (k∉₁ , k∉₂) k∈ = case ∈ᵈ-∪⁻ k s₁ s₂ k∈ of λ where
    (inj₁ k∈₁) → k∉₁ k∈₁
    (inj₂ k∈₂) → k∉₂ k∈₂

  ∈ᵈ-∪≡ : ⟨ s₁ ⊎ s₂ ⟩≡ s → k ∈ᵈ s → k ∈ᵈ s₁ ⊎ k ∈ᵈ s₂
  ∈ᵈ-∪≡ (_ , p) = ∈ᵈ-∪⁻ _ _ _ ∘ ∈ᵈ-cong _ _ _ (≈-sym p)

  ∉ᵈ-∪≡ : ⟨ s₁ ⊎ s₂ ⟩≡ s → k ∉ᵈ s₁ → k ∉ᵈ s₂ → k ∉ᵈ s
  ∉ᵈ-∪≡ (_ , p) k∉₁ k∉₂ k∈
    with ∈ᵈ-∪⁻ _ _ _ (∈ᵈ-cong _ _ _ (≈-sym p) k∈)
  ... | inj₁ k∈₁ = ⊥-elim $ k∉₁ k∈₁
  ... | inj₂ k∈₂ = ⊥-elim $ k∉₂ k∈₂

  KeyMonotonic KeyPreserving : (Map → Map) → Type σ
  KeyMonotonic  f = ∀ s → s   ⊆ᵈ f s
  KeyPreserving f = ∀ s → f s ⊆ᵈ s

  ≈-cong : ∀ {P : K → Maybe V → Type}
    → s₁ ≈ s₂
    → (∀ k → P k (s₁ ⁉ k))
    → (∀ k → P k (s₂ ⁉ k))
  ≈-cong {P = P} eq p k = subst (P k) (eq k) (p k)

  ∪≡-comm : Symmetric (⟨_⊎_⟩≡ s)
  ∪≡-comm (s₁♯s₂ , ≡s) = ♯-comm s₁♯s₂ , ≈-trans (≈-sym $ ∪-comm _ _ s₁♯s₂) ≡s

  ∪≡-cong : s₁ ≈ s₂ → ⟨ s₁ ⊎ s₃ ⟩≡ s → ⟨ s₂ ⊎ s₃ ⟩≡ s
  ∪≡-cong eq (s₁♯s₃ , ≡s) = ♯-cong _ _ _ eq s₁♯s₃ , ≈-trans (≈-sym (∪-cong _ _ _ eq)) ≡s

  ∪-assocˡ : ∀ s₁ s₂ s₃ → (s₁ ∪ s₂) ∪ s₃ ≈ s₁ ∪ (s₂ ∪ s₃)
  ∪-assocˡ s = ≈-sym ∘₂ ∪-assocʳ s

  ⊎≈-assocʳ :
    ∙ ⟨ s₁ ⊎ s₂₃ ⟩≡ s
    ∙ ⟨ s₂ ⊎ s₃  ⟩≡ s₂₃
      ───────────────────
      ⟨ (s₁ ∪ s₂) ⊎ s₃ ⟩≡ s
    × (s₁ ♯ s₂)
  ⊎≈-assocʳ {s₁}{s₂₃}{s}{s₂}{s₃} (s₁♯s₂₃ , ≡s) (s₂♯s₃ , ≡s₂₃) =
    ∪≡-assocʳ _ _ _ _ s₂♯s₃ ≡ss , proj₁ (♯-∪⁻ʳ _ _ _ s₁♯s₂₃′)
    where
      s₁♯s₂₃′ : s₁ ♯ (s₂ ∪ s₃)
      s₁♯s₂₃′ = ♯-comm $ ♯-cong _ _ _ (≈-sym ≡s₂₃) $ ♯-comm s₁♯s₂₃

      ≡ss : ⟨ s₁ ⊎ (s₂ ∪ s₃) ⟩≡ s
      ≡ss = s₁♯s₂₃′
          , ≈-trans (≈-trans (∪-comm _ _ s₁♯s₂₃′)
                             (≈-trans (∪-cong _ _ _ ≡s₂₃)
                                      (≈-sym $ ∪-comm _ _ s₁♯s₂₃)))
                    ≡s

  ∪≡-assocˡ : ∀ {s₁ s₂ s₃ s} → s₁ ♯ s₂ → ⟨ (s₁ ∪ s₂) ⊎ s₃ ⟩≡ s → ⟨ s₁ ⊎ (s₂ ∪ s₃)  ⟩≡ s
  ∪≡-assocˡ {s₁}{s₂}{s₃}{s} s₁♯s₂ p@(s₁₂♯s₃ , _) =
      ∪≡-comm ∘ ∪≡-assocʳ _ _ _ _ (♯-comm $ proj₁ $ ♯-∪⁻ˡ _ _ _ s₁₂♯s₃)
    $ ∪≡-comm ∘ ∪≡-assocʳ _ _ _ _ s₁♯s₂
    $ ∪≡-comm p

  ⊎≈-assocˡ :
    ∙ ⟨ s₁₂ ⊎ s₃ ⟩≡ s
    ∙ ⟨ s₁ ⊎ s₂  ⟩≡ s₁₂
      ───────────────────
      ⟨ s₁ ⊎ (s₂ ∪ s₃) ⟩≡ s
    × (s₂ ♯ s₃)
  ⊎≈-assocˡ {s₁₂}{s₃}{s}{s₁}{s₂} (s₁₂♯s₃ , ≡s) (s₁♯s₂ , ≡s₁₂) =
    ∪≡-assocˡ s₁♯s₂ ≡ss , proj₂ (♯-∪⁻ˡ s₁ _ _ s₁₂♯s₃′)
    where
      s₁₂♯s₃′ : (s₁ ∪ s₂) ♯ s₃
      s₁₂♯s₃′ = ♯-cong _ _ _ (≈-sym ≡s₁₂) s₁₂♯s₃

      ≡ss : ⟨ (s₁ ∪ s₂) ⊎ s₃ ⟩≡ s
      ≡ss = s₁₂♯s₃′ , ≈-trans (∪-cong _ _ _ ≡s₁₂) ≡s

record FinMapᴵ : Type (lsuc σ) where
  field
    mapᴵ : Mapᴵ
    ⦃ DecEq-K ⦄ : DecEq K
  open Mapᴵ mapᴵ public

  -- record DecEqMapᴵ : Type (lsuc σ) where
  --   field deceq : DecEq Map
  --   instance DecEq-Map = deceq

  field
    _∈ᵈ?_ : Decidable² _∈ᵈ_

    buildMap : (K → Maybe V) → Map
    buildMap-sound : ∀ (f : K → Maybe V) → ∀ k → buildMap f ⁉ k ≡ f k

    singleton : K × V → Map
    singleton-law : ∀ {k v} → singleton (k , v) [ k ↦ v ]∅
    singleton∈ : ∀ {A v k} → k ∈ᵈ singleton (A , v) → k ≡ A
    singleton♯ : ∀ {k v k′ v′} → k ≢ k′ → singleton (k , v) ♯ singleton (k′ , v′)
    singleton-collapse : ∀ {k v v′} → singleton (k , v) ∪ singleton (k , v′) ≈ singleton (k , v)

  singleton-law′ : ∀ {k v} → singleton (k , v) [ k ↦ v ]
  singleton-law′ {k}{v} = singleton-law {k}{v} .proj₁

  -- overwriting update
  update : K × V → Map → Map
  update entry = singleton entry ∪_

  field
    ≡-cong-update : ∀ s₁ s₂ {k k′ v} → s₁ ⁉ k′ ≡ s₂ ⁉ k′ → update (k , v) s₁ ⁉ k′ ≡ update (k , v) s₂ ⁉ k′
    ♯-cong-pre : ∀ f (s₁ s₂ : Map) → KeyPreserving f → s₁ ♯ s₂ → f s₁ ♯ s₂

  private variable
    s s₁ s₂ s₃ s₁₂ s₂₃ m m′ m₁ m₂ m₃ m₁₂ m₂₃ : Map
    k k′ : K
    v v′ : V
    f g : Map → Map

  infix 4 _∈ᵈ?_ _∉ᵈ?_
  _∉ᵈ?_ : Decidable² _∉ᵈ_
  k ∉ᵈ? m = ¬? (k ∈ᵈ? m)

  instance
    Dec-∈ᵈ : ∀ {k : K} {m : Map} → (k ∈ᵈ m) ⁇
    Dec-∈ᵈ .dec = _∈ᵈ?_ _ _

  module _ ⦃ _ : Semigroup V ⦄ ⦃ mᵥ : Monoid V ⦄
           ⦃ _ : SemigroupLaws≡ V ⦄ ⦃ _ : MonoidLaws≡ V ⦄ where
    infix 6 _⁉ε_
    _⁉ε_ : Map → K → V
    m ⁉ε k = fromMaybe ε (m ⁉ k)

    instance
      Semigroup-Map : Semigroup Map
      Semigroup-Map ._◇_ m m′ = buildMap λ k → (m ⁉ k) ◇ (m′ ⁉ k)

    ◇-⁉ : ∀ m m′ → (m ◇ m′) ⁉ k ≡ (m ⁉ k) ◇ (m′ ⁉ k)
    ◇-⁉ {k} m m′ = buildMap-sound (λ k → (m ⁉ k) ◇ (m′ ⁉ k)) k

    ⟨_◇_⟩≡_ : 3Rel₀ Map
    ⟨ m₁ ◇ m₂ ⟩≡ m = m₁ ◇ m₂ ≈ m

    open Alg _≈ᵐ_
    private
      ◇ᵐ-comm : Commutative (_◇_ {A = Map})
      ◇ᵐ-comm m m′ k =
          begin
            (buildMap λ k → (m ⁉ k) ◇ (m′ ⁉ k)) ⁉ k
          ≡⟨ buildMap-sound _ _ ⟩
            (m ⁉ k) ◇ (m′ ⁉ k)
          ≡⟨ ◇-comm≡ (m ⁉ k) (m′ ⁉ k) ⟩
            (m′ ⁉ k) ◇ (m ⁉ k)
          ≡⟨ sym $ buildMap-sound _ _ ⟩
            (buildMap λ k → (m′ ⁉ k) ◇ (m ⁉ k)) ⁉ k
          ∎ where open ≡-Reasoning

      ◇ᵐ-assocʳ : Associative (_◇_ {A = Map})
      ◇ᵐ-assocʳ m₁ m₂ m₃ k =
        begin
          ((m₁ ◇ m₂) ◇ m₃) ⁉ k
        ≡⟨ ◇-⁉ _ _ ⟩
          ((m₁ ◇ m₂) ⁉ k) ◇ (m₃ ⁉ k)
        ≡⟨ go ⟩
          (m₁ ⁉ k) ◇ ((m₂ ◇ m₃) ⁉ k)
        ≡⟨ sym $ ◇-⁉ _ _ ⟩
          (m₁ ◇ m₂ ◇ m₃) ⁉ k
        ∎ where
          open ≡-Reasoning

          go : ((m₁ ◇ m₂) ⁉ k) ◇ (m₃ ⁉ k)
              ≡ (m₁ ⁉ k) ◇ ((m₂ ◇ m₃) ⁉ k)
          go
            rewrite ◇-⁉ {k} (m₁ ◇ m₂) m₃
                  | ◇-⁉ {k} m₁ m₂
                  | ◇-⁉ {k} m₁ (m₂ ◇ m₃)
                  | ◇-⁉ {k} m₂ m₃
            = ◇-assocʳ≡ (m₁ ⁉ k) _ _
    instance
      SemigroupLaws-Map : SemigroupLaws Map
      SemigroupLaws-Map = record {◇-comm = ◇ᵐ-comm; ◇-assocʳ = ◇ᵐ-assocʳ}

    ◇≡-comm : Symmetric (⟨_◇_⟩≡ m)
    ◇≡-comm ≈m = ≈-trans (◇ᵐ-comm _ _) ≈m

    ◇-congˡ :
      m₁ ≈ m₂
      ───────────────────
      (m₁ ◇ m₃) ≈ (m₂ ◇ m₃)
    ◇-congˡ {m₁}{m₂}{m₃} m₁≈m₂ k =
      begin
        (m₁ ◇ m₃) ⁉ k
      ≡⟨ buildMap-sound _ _ ⟩
        (m₁ ⁉ k) ◇ (m₃ ⁉ k)
      ≡⟨  cong (_◇ (m₃ ⁉ k)) (m₁≈m₂ k) ⟩
        (m₂ ⁉ k) ◇ (m₃ ⁉ k)
      ≡⟨ sym $ buildMap-sound _ _ ⟩
        (m₂ ◇ m₃) ⁉ k
      ∎ where open ≡-Reasoning

    ◇-congʳ : m₂ ≈ m₃ → (m₁ ◇ m₂) ≈ (m₁ ◇ m₃)
    ◇-congʳ {m₂}{m₃}{m₁} m₂≈m₃ =
      begin m₁ ◇ m₂ ≈⟨ ◇ᵐ-comm _ _ ⟩
            m₂ ◇ m₁ ≈⟨ ◇-congˡ m₂≈m₃ ⟩
            m₃ ◇ m₁ ≈⟨ ◇ᵐ-comm _ _ ⟩
            m₁ ◇ m₃ ∎ where open ≈-Reasoning

    ◇≈-assocˡ :
      ∙ ⟨ m₁₂ ◇ m₃ ⟩≡ m
      ∙ ⟨ m₁ ◇ m₂  ⟩≡ m₁₂
        ───────────────────
        ⟨ m₁ ◇ (m₂ ◇ m₃) ⟩≡ m
    ◇≈-assocˡ ≡m ≡m₁₂ = ≈-trans (≈-trans (≈-sym (◇-assocʳ _ _ _)) (◇-congˡ ≡m₁₂)) ≡m
      -- begin m₁ ◇ (m₂ ◇ m₃) ≈⟨ ≈-sym $ ◇-assocʳ _ _ _ ⟩
      --       (m₁ ◇ m₂) ◇ m₃ ≈⟨ ◇-congˡ ≡m₁₂ ⟩
      --       m₁₂ ◇ m₃       ≈⟨ ≡m ⟩
      --       m              ∎ where open ≈-Reasoning

    ◇≈-assocʳ :
      ∙ ⟨ m₁ ◇ m₂₃ ⟩≡ m
      ∙ ⟨ m₂ ◇ m₃  ⟩≡ m₂₃
        ───────────────────
        ⟨ (m₁ ◇ m₂) ◇ m₃ ⟩≡ m
    ◇≈-assocʳ ≡m ≡m₂₃ = ≈-trans (≈-trans (◇-assocʳ _ _ _) (◇-congʳ ≡m₂₃)) ≡m
      -- begin (m₁ ◇ m₂) ◇ m₃ ≈⟨ ◇-assocʳ _ _ _ ⟩
      --       m₁ ◇ (m₂ ◇ m₃) ≈⟨ ◇-congʳ ≡m₂₃ ⟩
      --       m₁ ◇ m₂₃       ≈⟨ ≡m ⟩
      --       m              ∎ where open ≈-Reasoning

    Is-just-◇⁻ : ∀ (m m′ : Maybe V) → Is-just (m ◇ m′) → Is-just m ⊎ Is-just m′
    Is-just-◇⁻ (just _) _       _ = inj₁ auto
    Is-just-◇⁻ nothing (just _) _ = inj₂ auto

    Is-just-◇⁺ : ∀ (m m′ : Maybe V) → Is-just m ⊎ Is-just m′ → Is-just (m ◇ m′)
    Is-just-◇⁺ (just _) (just _) _        = auto
    Is-just-◇⁺ (just _) nothing  (inj₁ _) = auto
    Is-just-◇⁺ nothing  (just _) (inj₂ _) = auto

    ∈ᵈ-◇⁻ : ∀ k s₁ s₂ → k ∈ᵈ (s₁ ◇ s₂) → (k ∈ᵈ s₁) ⊎ (k ∈ᵈ s₂)
    ∈ᵈ-◇⁻ k s₁ s₂ k∈ = k∈′
      where
        p : Is-just ((s₁ ⁉ k) ◇ (s₂ ⁉ k))
        p = subst Is-just (buildMap-sound _ k) (∈ᵈ⇒⁉ _ k∈)

        k∈′ : (k ∈ᵈ s₁) ⊎ (k ∈ᵈ s₂)
        k∈′ = case Is-just-◇⁻ (s₁ ⁉ k) (s₂ ⁉ k) p of λ where
          (inj₁ x) → inj₁ (⁉⇒∈ᵈ _ x)
          (inj₂ x) → inj₂ (⁉⇒∈ᵈ _ x)

    ∈ᵈ-◇⁺ : ∀ k s₁ s₂ → (k ∈ᵈ s₁) ⊎ (k ∈ᵈ s₂) → k ∈ᵈ (s₁ ◇ s₂)
    ∈ᵈ-◇⁺ k s₁ s₂ k∈ = ⁉⇒∈ᵈ _ k∈′
      where
        p : Is-just (s₁ ⁉ k) ⊎ Is-just (s₂ ⁉ k)
        p = case k∈ of λ where
          (inj₁ x) → inj₁ (∈ᵈ⇒⁉ _ x)
          (inj₂ x) → inj₂ (∈ᵈ⇒⁉ _ x)

        k∈′ : Is-just ((s₁ ◇ s₂) ⁉ k)
        k∈′ = subst Is-just (sym $ buildMap-sound _ k) $′ Is-just-◇⁺ (s₁ ⁉ k) (s₂ ⁉ k) p

    ↦-◇⁺ˡ : k ∉ᵈ s₂ → s₁ ⁉ k ≡ (s₁ ◇ s₂) ⁉ k
    ↦-◇⁺ˡ {k = k}{s₂}{s₁} k∉ =
      begin
        s₁ ⁉ k
      ≡⟨ sym $ ε-identity≡ .proj₂ (s₁ ⁉ k) ⟩
        (s₁ ⁉ k) ◇ nothing
      ≡⟨ cong ((s₁ ⁉ k) ◇_) (sym (Is-nothing≡ (∉ᵈ⇒⁉ _ k∉))) ⟩
        (s₁ ⁉ k) ◇ (s₂ ⁉ k)
      ≡⟨ sym $ ◇-⁉ _ _  ⟩
        (s₁ ◇ s₂) ⁉ k
      ∎ where open ≡-Reasoning

    ↦-◇⁺ʳ : k ∉ᵈ s₁ → s₂ ⁉ k ≡ (s₁ ◇ s₂) ⁉ k
    ↦-◇⁺ʳ {k = k}{s₁}{s₂} k∉ =
      begin
        s₂ ⁉ k
      ≡⟨ sym $ ε-identity≡ .proj₁ (s₂ ⁉ k) ⟩
        nothing ◇ (s₂ ⁉ k)
      ≡⟨ cong (_◇ (s₂ ⁉ k)) (sym (Is-nothing≡ (∉ᵈ⇒⁉ _ k∉))) ⟩
        (s₁ ⁉ k) ◇ (s₂ ⁉ k)
      ≡⟨ sym $ ◇-⁉ _ _  ⟩
        (s₁ ◇ s₂) ⁉ k
      ∎ where open ≡-Reasoning
    update-mon : ∀ {k v} → KeyMonotonic (update (k , v))
    update-mon s = ∈ᵈ-∪⁺ʳ _ _ _

    ≈-cong-update : s₁ ≈ s₂ → update (k , v) s₁ ≈ update (k , v) s₂
    ≈-cong-update s₁≈s₂ = ≡-cong-update _ _ ∘ s₁≈s₂

    update-other : k ≢ k′ → update (k , v) s ⁉ k′ ≡ s ⁉ k′
    update-other {k}{k′}{v}{s} k≢ =
      begin
        update (k , v) s ⁉ k′
      ≡⟨⟩
        (singleton (k , v) ∪ s) ⁉ k′
      ≡⟨ sym $ ↦-∪⁺ʳ (singleton (k , v)) s {k′} (≢-sym k≢ ∘ singleton∈) ⟩
        s ⁉ k′
      ∎ where open ≡-Reasoning

    update-update : update (k , v′) (update (k , v) s) ≈ update (k , v′) s
    update-update {k}{v′}{v}{s} =
      begin
        update (k , v′) (update (k , v) s)
      ≡⟨⟩
        singleton (k , v′) ∪ (singleton (k , v) ∪ s)
      ≈⟨ ∪-assocʳ _ _ _ ⟩
        (singleton (k , v′) ∪ singleton (k , v)) ∪ s
      ≈⟨ ∪-cong _ _ _ singleton-collapse ⟩
        singleton (k , v′) ∪ s
      ≡⟨⟩
        update (k , v′) s
      ∎ where open ≈-Reasoning

    singleton≈ : m [ k ↦ v ]∅ → m ≈ singleton (k , v)
    singleton≈ {m}{k}{v} (eq , p) k′
      with k′ ≟ k
    ... | yes refl = trans eq (sym singleton-law′)
    ... | no  ≢k
      with m ⁉ k′ | ⁉⇒∈ᵈ m {k′}
    ... | just _  | q = ⊥-elim $ p k′ ≢k (q auto)
    ... | nothing | _
      with singleton (k , v) ⁉ k′ | ⁉⇒∈ᵈ (singleton (k , v)) {k′}
    ... | just _  | q = ⊥-elim $ ≢k (singleton∈ (q auto))
    ... | nothing | _ = refl

    singleton-accept : (singleton (k , v) ∪ s) ⁉ k ≡ just v
    singleton-accept {k}{v}{s} rewrite ↦-∪⁺ˡ _ s {k}{v} singleton-law′ = refl

    singleton-reject : k′ ≢ k → (singleton (k , v) ∪ s) ⁉ k′ ≡ s ⁉ k′
    singleton-reject {s = s} k≢ rewrite ↦-∪⁺ʳ _ s (k≢ ∘ singleton∈) = refl

    -- ** Command DSL for defining map transformations.
    module CommandDSL where
      modify : K → (V → V) → (Map → Map)
      modify k f m with m ⁉ k
      ... | nothing = m
      ... | just v  = update (k , f v) m

      modify-mon : ∀ {f} → KeyMonotonic (modify k f)
      modify-mon {k = k} {f = f} s with s ⁉ k
      ... | nothing = id
      ... | just v  = update-mon s

      modify-pre : ∀ {f} → KeyPreserving (modify k f)
      modify-pre {k = k} {f = f} s {k′} k∈
        with s ⁉ k in sk≡
      ... | nothing = k∈
      ... | just v
        with ∈ᵈ-∪⁻ k′ (singleton (k , f v)) s k∈
      ... | inj₁ k∈′
        rewrite singleton∈ k∈′
              = ⁉⇒∈ᵈ _
              $ subst Is-just (sym sk≡) (M.Any.just tt)
      ... | inj₂ k∈s = k∈s

      modify-other : ∀ {f} → k ≢ k′ → modify k f s ⁉ k′ ≡ s ⁉ k′
      modify-other {k = k} {s = s} k≢ with s ⁉ k
      ... | nothing = refl
      ... | just _  = update-other k≢

      ≡-cong-modify : ∀ {f} → s₁ ⁉ k′ ≡ s₂ ⁉ k′ → modify k f s₁ ⁉ k′ ≡ modify k f s₂ ⁉ k′
      ≡-cong-modify {k′ = k′} {k = k} s≡
        with k ≟ k′
      ≡-cong-modify {s₁ = s₁} {s₂ = s₂} {k = k} s≡ | yes refl
        with s₁ ⁉ k | s₂ ⁉ k | s≡
      ... | nothing | nothing | _ = s≡
      ... | just _  | just _  | eq
        rewrite M.just-injective eq = ≡-cong-update _ _ s≡
      ≡-cong-modify {s₁ = s₁} {s₂ = s₂} {k = k} {f = f} s≡ | no k≢
        with s₁ ⁉ k | s₂ ⁉ k
      ... | nothing | nothing
        = s≡
      ... | just x | nothing
        rewrite update-other {k = k} {v = f x} {s = s₁} k≢ = s≡
      ... | nothing | just y
        rewrite update-other {k = k} {v = f y} {s = s₂} k≢ = s≡
      ... | just x | just y
        rewrite update-other {k = k} {v = f x} {s = s₁} k≢
              | update-other {k = k} {v = f y} {s = s₂} k≢ = s≡

      ≈-cong-modify : ∀ {f} → s₁ ≈ s₂ → modify k f s₁ ≈ modify k f s₂
      ≈-cong-modify {k = k} s₁≈s₂ = ≡-cong-modify ∘ s₁≈s₂

      modify-this : ∀ {f} → (modify k f s ⁉ k) ≡ (f <$> (s ⁉ k))
      modify-this {k}{s}{f} with s ⁉ k in s≡
      ... | nothing rewrite s≡ = refl
      ... | just v  rewrite ↦-∪⁺ˡ _ s $ singleton-law′ {k = k}{f v} = refl

      modify∘modify : ∀ {f g} → modify k f (modify k g s) ≈ modify k (f ∘ g) s
      modify∘modify {k}{s}{f}{g} k′
        with k ≟ k′
      ... | no k≢ =
        begin
          modify k f (modify k g s) ⁉ k′
        ≡⟨ modify-other k≢ ⟩
          modify k g s ⁉ k′
        ≡⟨ modify-other k≢ ⟩
          s ⁉ k′
        ≡⟨ sym $ modify-other k≢ ⟩
          modify k (f ∘ g) s ⁉ k′
        ∎ where open ≡-Reasoning
      ... | yes refl =
        begin
          modify k f (modify k g s) ⁉ k
        ≡⟨ modify-this ⟩
          f <$> (modify k g s ⁉ k)
        ≡⟨ cong (fmap f) modify-this ⟩
          f <$> (g <$> (s ⁉ k))
        ≡⟨ sym $ M.map-compose (s ⁉ k) ⟩
          (f ∘ g) <$> (s ⁉ k)
        ≡⟨ sym $ modify-this ⟩
          modify k (f ∘ g) s ⁉ k
        ∎ where open ≡-Reasoning

      modify-id : ∀ {f} → (∀ x → f x ≡ x) → modify k f s ≈ s
      modify-id {k}{s}{f} f≗id k′
        with k ≟ k′
      ... | no k≢ rewrite modify-other {k = k}{k′}{s}{f} k≢ = refl
      ... | yes refl
        =
        begin
          modify k f s ⁉ k
        ≡⟨ modify-this ⟩
          f <$> (s ⁉ k)
        ≡⟨ IdFun-fmap f≗id (s ⁉ k) ⟩
          s ⁉ k′
        ∎ where open ≡-Reasoning

      singleton-accept◇ : k ∉ᵈ s → (singleton (k , v) ◇ s) ⁉ k ≡ just v
      singleton-accept◇ {k}{s}{v} k∉ =
        begin (singleton (k , v) ◇ s) ⁉ k ≡⟨ sym $ ↦-◇⁺ˡ k∉ ⟩
              singleton (k , v) ⁉ k       ≡⟨ singleton-law′ {k}{v} ⟩
              just v                      ∎ where open ≡-Reasoning

      singleton-reject◇ : k′ ≢ k → (singleton (k , v) ◇ s) ⁉ k′ ≡ s ⁉ k′
      singleton-reject◇ {s = s} k≢ rewrite ↦-◇⁺ʳ {s₂ = s} (k≢ ∘ singleton∈) = refl

      singleton◇ : singleton (k , v) ◇ singleton (k , v′) ≈ singleton (k , v ◇ v′)
      singleton◇ {A}{v}{v′} k
        with k ≟ A
      ... | yes refl =
        begin
          (singleton (A , v) ◇ singleton (A , v′)) ⁉ A
        ≡⟨ ◇-⁉ _ _ ⟩
          (singleton (A , v) ⁉ A) ◇ (singleton (A , v′) ⁉ A)
        ≡⟨ cong (_◇ (singleton (A , v′) ⁉ A)) singleton-law′ ⟩
          just v ◇ (singleton (A , v′) ⁉ A)
        ≡⟨ cong (just v ◇_) singleton-law′ ⟩
          just v ◇ just v′
        ≡⟨⟩
          just (v ◇ v′)
        ≡⟨ sym $ singleton-law′ ⟩
          singleton (A , v ◇ v′) ⁉ A
        ∎ where open ≡-Reasoning
      ... | no k≢A =
        begin
          (singleton (A , v) ◇ singleton (A , v′)) ⁉ k
        ≡⟨ ◇-⁉ _ _ ⟩
          (singleton (A , v) ⁉ k) ◇ (singleton (A , v′) ⁉ k)
        ≡⟨ cong (_◇ (singleton (A , v′) ⁉ k)) (Is-nothing≡ $ ∉ᵈ⇒⁉ _ k∉) ⟩
          nothing ◇ (singleton (A , v′) ⁉ k)
        ≡⟨ cong (nothing ◇_) (Is-nothing≡ $ ∉ᵈ⇒⁉ _ k∉) ⟩
          nothing
        ≡⟨ sym $ Is-nothing≡ $ ∉ᵈ⇒⁉ _ k∉ ⟩
          singleton (A , v ◇ v′) ⁉ k
        ∎ where open ≡-Reasoning
                k∉ : ∀ {v} → k ∉ᵈ singleton (A , v)
                k∉ = singleton-law .proj₂ k k≢A

      modify-thisˡ : ∀ {f} → k ∉ᵈ s → modify k f (singleton (k , v) ◇ s) ≈ (singleton (k , f v) ◇ s)
      modify-thisˡ {k = k} {s = s} {v = v} {f = f} k∉ k′
        with k ≟ k′
      ... | yes refl
        = qed
        where
          p : (singleton (k , v) ◇ s) ⁉ k ≡ just v
          p rewrite sym $ ↦-◇⁺ˡ {s₁ = singleton (k , v)} k∉ = singleton-law′

          q : (singleton (k , f v) ◇ s) ⁉ k ≡ just (f v)
          q rewrite sym $ ↦-◇⁺ˡ {s₁ = singleton (k , f v)} k∉ = singleton-law′

          qed : modify k f (singleton (k , v) ◇ s) ⁉ k ≡ (singleton (k , f v) ◇ s) ⁉ k
          qed rewrite p | q | p = singleton-accept {k}{f v}
      ... | no  k≢
        = begin
          modify k f (singleton (k , v) ◇ s) ⁉ k′
        ≡⟨ modify-other k≢ ⟩
          (singleton (k , v) ◇ s) ⁉ k′
        ≡⟨ singleton-reject◇ (≢-sym k≢) ⟩
          s ⁉ k′
        ≡⟨ (sym $ singleton-reject◇ (≢-sym k≢)) ⟩
          (singleton (k , f v) ◇ s) ⁉ k′
        ∎ where open ≡-Reasoning

      data Cmd : Type₁ where
        _∶_ : Cmd → Cmd → Cmd
        iff⦅_⦆_ : ∀ {P : Type} → Dec P → Cmd → Cmd
        just? : K → (V → Cmd) → Cmd
        _≔_ : K → V → Cmd
        [_←_] : (V → V) → K → Cmd

      iff¿_¿_ : (P : Type) → ⦃ P ⁇ ⦄ → Cmd → Cmd
      iff¿ P ¿ c = iff⦅ ¿ P ¿ ⦆ c

      infix 5 _≔_

      run : Cmd → (Map → Map)
      run (k ≔ v) = update (k , v)
      run [ f ← k ] = modify k f
      run (cmd₁ ∶ cmd₂) = run cmd₂ ∘ run cmd₁
      run (iff⦅ b ⦆ cmd) with b
      ... | yes _ = run cmd
      ... | no  _ = id
      run (just? k go) s with s ⁉ k
      ... | just v  = run (go v) s
      ... | nothing = s

      ≈-cong-cmd : ∀ {s₁ s₂} cmd → s₁ ≈ s₂ → run cmd s₁ ≈ run cmd s₂
      ≈-cong-cmd {s₁} {s₂} (cmd₁ ∶ cmd₂) s₁≈s₂ k = ≈-cong-cmd cmd₂ (≈-cong-cmd cmd₁ s₁≈s₂) k
      ≈-cong-cmd {s₁} {s₂} (_ ≔ _) s₁≈s₂ k = ≈-cong-update s₁≈s₂ k
      ≈-cong-cmd {s₁} {s₂} [ _ ← _ ] s₁≈s₂ k = ≈-cong-modify s₁≈s₂ k
      ≈-cong-cmd {s₁} {s₂} (iff⦅ b ⦆ cmd) s₁≈s₂ k
        with b
      ... | yes _ = ≈-cong-cmd cmd s₁≈s₂ k
      ... | no  _ = s₁≈s₂ k
      ≈-cong-cmd {s₁} {s₂} (just? k′ go) s₁≈s₂ k
        with s₁ ⁉ k′ | s₂ ⁉ k′ | s₁≈s₂ k′
      ... | nothing | .nothing  | refl = s₁≈s₂ k
      ... | just v  | .(just v) | refl = ≈-cong-cmd (go v) s₁≈s₂ k

      cmd-mon : ∀ cmd → KeyMonotonic (run cmd)
      cmd-mon (cmd₁ ∶ cmd₂) s = cmd-mon cmd₂ (run cmd₁ s) ∘ cmd-mon cmd₁ s
      cmd-mon (iff⦅ b ⦆ cmd) with b
      ... | yes _ = cmd-mon cmd
      ... | no  _ = λ _ → id
      cmd-mon (just? k′ go) s with s ⁉ k′
      ... | just v  = cmd-mon (go v) s
      ... | nothing = id
      cmd-mon [ _ ← _ ] = modify-mon
      cmd-mon (_ ≔ _) = update-mon

      module _ {s k v go} where
        just?-accept : s [ k ↦ v ] → run (just? k go) s ≈ run (go v) s
        just?-accept eq _ with s ⁉ k | eq
        ... | just v | refl = refl

        just?-reject : k ∉ᵈ s → run (just? k go) s ≈ s
        just?-reject k∉ _ with s ⁉ k | ⁉⇒∈ᵈ s {k}
        ... | just _  | p = ⊥-elim $ k∉ (p auto)
        ... | nothing | _ = refl

      module _ {s cmd} {P : Type} {b : Dec P} where
        iff-accept : True b → run (iff⦅ b ⦆ cmd) s ≈ run cmd s
        iff-accept _ _ with ⟫ yes _ ← ⟫ b = refl

        iff-reject : False b → run (iff⦅ b ⦆ cmd) s ≈ s
        iff-reject _ _ with ⟫ no _ ← ⟫ b = refl