packages feed

agda2hs-1.4: lib/base/Haskell/Law/Num/Def.agda

module Haskell.Law.Num.Def where

open import Haskell.Prim
open import Haskell.Prim.Num
open import Haskell.Prim.Integer

record IsLawfulNum (a : Type) ⦃ iNum : Num a ⦄ : Type₁ where
  field
    +-assoc : ∀ (x y z : a) → (x + y) + z ≡ x + (y + z)

    +-comm : ∀ (x y : a) → x + y ≡ y + x

    +-idˡ : ∀ (x : a) {{@0 _ : Num.FromIntegerOK iNum 0}}
      → fromInteger 0 + x ≡ x
    +-idʳ : ∀ (x : a) {{@0 _ : Num.FromIntegerOK iNum 0}}
      → x + fromInteger 0 ≡ x

    neg-inv : ∀ (x : a) {{@0 _ : Num.FromIntegerOK iNum 0}} {{@0 _ : Num.NegateOK iNum x}}
      → x + negate x ≡ fromInteger 0

    *-assoc : ∀ (x y z : a) → (x * y) * z ≡ x * (y * z)

    *-idˡ : ∀ (x : a) {{@0 _ : Num.FromIntegerOK iNum 1}}
      → fromInteger 1 * x ≡ x
    *-idʳ : ∀ (x : a) {{@0 _ : Num.FromIntegerOK iNum 1}}
      → x * fromInteger 1 ≡ x

    distributeˡ : ∀ (x y z : a) → x * (y + z) ≡ (x * y) + (x * z)
    distributeʳ : ∀ (x y z : a) → (y + z) * x ≡ (y * x) + (z * x)

    -- We are currently missing the following because toInteger is missing in our Prelude.
    -- "if the type also implements Integral, then fromInteger is a left inverse for toInteger, i.e. fromInteger (toInteger i) == i"
open IsLawfulNum ⦃ ... ⦄ public

open import Haskell.Law.Equality
open import Haskell.Law.Function

{-|
A number homomorphism establishes a homomorphism from one Num type a to another one b.
In particular, zero and one are mapped to zero and one in the other Num type,
and addition, multiplication, and negation are homorphic.
-}
record NumHomomorphism (a b : Type) ⦃ iNuma : Num a ⦄ ⦃ iNumb : Num b ⦄ (φ : a → b) : Type where
  0ᵃ = Num.fromInteger iNuma (pos 0)
  0ᵇ = Num.fromInteger iNumb (pos 0)
  1ᵃ = Num.fromInteger iNuma (pos 1)
  1ᵇ = Num.fromInteger iNumb (pos 1)

  field
    +-hom : Homomorphism₂ _+_ _+_ φ
    *-hom : Homomorphism₂ _*_ _*_ φ
    ⦃ minus-ok ⦄       : ∀ {x y : a}     → ⦃ MinusOK x y ⦄               → MinusOK (φ x) (φ y)
    ⦃ negate-ok ⦄      : ∀ {x   : a}     → ⦃ NegateOK x ⦄                → NegateOK (φ x)
    ⦃ fromInteger-ok ⦄ : ∀ {i : Integer} → ⦃ Num.FromIntegerOK iNuma i ⦄ → Num.FromIntegerOK iNumb i
    0-hom : ⦃ @0 _ : Num.FromIntegerOK iNuma (pos 0) ⦄ → φ 0ᵃ ≡ 0ᵇ
    1-hom : ⦃ @0 _ : Num.FromIntegerOK iNuma (pos 1) ⦄ → φ 1ᵃ ≡ 1ᵇ
    negate-hom : ∀ (x : a) → ⦃ @0 _ : Num.NegateOK iNuma x ⦄ → φ (negate x) ≡ negate (φ x) --Homomorphism₁ inlined for type instances

{-|
A number embedding is an invertible number homomorphism.
-}
record NumEmbedding (a b : Type) ⦃ iNuma : Num a ⦄ ⦃ iNumb : Num b ⦄ (φ : a → b) (φ⁻¹ : b → a) : Type where
  field
    hom   : NumHomomorphism a b φ
    embed : φ⁻¹ ∘ φ ≗ id

  open NumHomomorphism hom

  +-Embedding₂ : Embedding₂ _+_ _+_ φ φ⁻¹
  Embedding₂.hom   +-Embedding₂ = +-hom
  Embedding₂.embed +-Embedding₂ = embed

  *-Embedding₂ : Embedding₂ _*_ _*_ φ φ⁻¹
  Embedding₂.hom   *-Embedding₂ = *-hom
  Embedding₂.embed *-Embedding₂ = embed

  +-MonoidEmbedding₂ : ⦃ @0 _ : Num.FromIntegerOK iNuma (pos 0) ⦄ → MonoidEmbedding₂ _+_ _+_ φ φ⁻¹ 0ᵃ 0ᵇ
  MonoidEmbedding₂.embedding +-MonoidEmbedding₂ = +-Embedding₂
  MonoidEmbedding₂.0-hom     +-MonoidEmbedding₂ = 0-hom

  *-MonoidEmbedding₂ : ⦃ @0 _ : Num.FromIntegerOK iNuma (pos 1) ⦄ → MonoidEmbedding₂ _*_ _*_ φ φ⁻¹ 1ᵃ 1ᵇ
  MonoidEmbedding₂.embedding *-MonoidEmbedding₂ = *-Embedding₂
  MonoidEmbedding₂.0-hom     *-MonoidEmbedding₂ = 1-hom

{-|
Given an embedding from one number type a onto another one b,
we can conclude that b satisfies the laws of Num if a satisfies the
laws of Num.
-}
map-IsLawfulNum : ∀ {a b : Type} ⦃ iNuma : Num a ⦄ ⦃ iNumb : Num b ⦄
  → (a2b : a → b) (b2a : b → a)
  → NumEmbedding a b a2b b2a
  → IsLawfulNum b
    -----------------------
  → IsLawfulNum a
map-IsLawfulNum {a} {b} {{numa}} {{numb}} f g proj lawb =
  record
  { +-assoc = map-assoc _+ᵃ_ _+ᵇ_ f g +-Embedding₂ (+-assoc lawb)
  ; +-comm  = map-comm  _+ᵃ_ _+ᵇ_ f g +-Embedding₂ (+-comm lawb)
  ; +-idˡ = λ x → map-idˡ _+ᵃ_ _+ᵇ_ f g 0ᵃ 0ᵇ +-MonoidEmbedding₂ (λ y → +-idˡ lawb y) x
  ; +-idʳ = λ x → map-idʳ _+ᵃ_ _+ᵇ_ f g 0ᵃ 0ᵇ +-MonoidEmbedding₂ (λ y → +-idʳ lawb y) x
  ; neg-inv = map-neg-inv
  ; *-assoc = map-assoc _*ᵃ_ _*ᵇ_ f g *-Embedding₂ (*-assoc lawb)
  ; *-idˡ = λ x → map-idˡ _*ᵃ_ _*ᵇ_ f g 1ᵃ 1ᵇ *-MonoidEmbedding₂ (λ y → *-idˡ lawb y) x
  ; *-idʳ = λ x → map-idʳ _*ᵃ_ _*ᵇ_ f g 1ᵃ 1ᵇ *-MonoidEmbedding₂ (λ y → *-idʳ lawb y) x
  ; distributeˡ = map-distributeˡ _+ᵃ_ _+ᵇ_ _*ᵃ_ _*ᵇ_ f g embed +-hom *-hom (distributeˡ lawb)
  ; distributeʳ = map-distributeʳ _+ᵃ_ _+ᵇ_ _*ᵃ_ _*ᵇ_ f g embed +-hom *-hom (distributeʳ lawb)
  }
  where
    open NumEmbedding proj
    open NumHomomorphism hom
    open IsLawfulNum lawb
    open Num numa renaming (_+_ to _+ᵃ_; _*_ to _*ᵃ_; negate to negateᵃ)
    open Num numb renaming (_+_ to _+ᵇ_; _*_ to _*ᵇ_; negate to negateᵇ)

    map-neg-inv : ∀ (x : a) ⦃ @0 _ : Num.FromIntegerOK numa (pos 0) ⦄ ⦃ @0 _ : Num.NegateOK numa x ⦄
      → x +ᵃ negateᵃ x ≡ 0ᵃ
    map-neg-inv x =
      x +ᵃ negateᵃ x           ≡˘⟨ embed (x +ᵃ negateᵃ x) ⟩
      g (f (x +ᵃ negateᵃ x))   ≡⟨ cong g (+-hom x (negateᵃ x)) ⟩
      g (f x +ᵇ f (negateᵃ x)) ≡⟨ cong g (cong (f x +ᵇ_) (negate-hom x)) ⟩
      g (f x +ᵇ negateᵇ (f x)) ≡⟨ cong g (neg-inv lawb (f x)) ⟩
      g 0ᵇ                     ≡˘⟨ cong g 0-hom ⟩
      g (f 0ᵃ)                 ≡⟨ embed 0ᵃ ⟩
      0ᵃ ∎