module Prelude.Fam where

-- Families of Ds

open import Prelude.Basic
open import Prelude.Equality

{----- Basic definitions -------------------------------}

record Fam (D : Set₁) : Set₁ where
  constructor _,_
  field
    ind : Set       -- index set
    fib : ind -> D  -- fibres in D
open Fam public

Fam-map : {D D' : Set₁} -> (D -> D') -> Fam D -> Fam D'
Fam-map g (A , P) = (A , g  P)

{-------------------------------------------------------}

{----- (Cartesian) morphisms in Fam --------------------}

record _Fam⇒_ {D : Set₁} (X Y : Fam D) : Set₁ where
  constructor _,_
  field
    indmor  : ind X -> ind Y
    indmor= : (x : ind X) -> fib X x  fib Y (indmor x)
open _Fam⇒_

-- The identity morphism
FamId : {D : Set₁} -> (X : Fam D) -> X Fam⇒ X
FamId X = id ,  x  refl)

-- Composition
_Fam∘_ : {D : Set₁} -> {X Y Z : Fam D} ->
         (f : Y Fam⇒ Z) -> (g : X Fam⇒ Y) -> X Fam⇒ Z
(f , f=) Fam∘ (g , g=) = f  g ,  x  trans (g= x) (f= (g x)))

{-------------------------------------------------------}

{----- Equality of families and Fam morphisms ----------}

-- Since Fam is a sigma type in disguise, this is how one proves
-- families equal
Fam-≡ : {D : Set₁} -> {X Y : Fam D} ->
        (ind= : ind X  ind Y) ->
        (fib= : (x : ind X) -> fib X x  fib Y (subst id ind= x)) ->
        X  Y
Fam-≡ refl p = Fam-≡' refl (ext p)
  where Fam-≡' : {D : Set₁} -> {X Y : Fam D} ->
                 (ind= : ind X  ind Y) ->
                 (dec= : subst  z  z -> D) ind= (fib X)  fib Y) ->
                 X  Y
        Fam-≡' refl refl = refl

-- Similarly, since Fam⇒ is a sigma type in disguise, this is how one
-- proves morphisms to be equal
⇒-≡ : {D : Set₁} -> {X Y : Fam D} ->
        {f g : X Fam⇒ Y} ->
        (ind= : indmor f  indmor g) ->
        (fib= : subst  z  (x : ind X)  fib X x  fib Y (z x))
                      ind= (indmor= f)  indmor= g) ->
        f  g
⇒-≡ refl refl = refl

-- How does one prove that a composition is an identity?
comp-is-id : {D : Set₁} -> {X Y : Fam D} ->
             (f : X Fam⇒ Y) -> (g : Y Fam⇒ X) ->
             (ind= : id  (indmor f)  (indmor g)) ->
             (coh : (y : ind Y) -> trans (indmor= g y) (indmor= f (indmor g y))
                                   cong (fib Y) (happly ind= y)) ->
             (f Fam∘ g)  FamId Y
comp-is-id {X = X} {Y} f g ind= coh = sym (⇒-≡ ind= fib=)
  where fib= = begin
                 subst  z  (x : ind Y)  fib Y x  fib Y (z x))
                       ind=
                        x  refl)
               ≡⟨ subst-cod ind= 
                  x  subst  z  fib Y x  fib Y (z x))
                               ind=
                               refl)
               ≡⟨ ext  x  subst-cong {B = λ z  fib Y x  z} ind= {refl}) 
                  x  subst  z  fib Y x  z)
                               (cong  z  fib Y (z x)) ind=)
                               refl)
               ≡⟨ ext  x  subst-path (cong  z  fib Y (z x)) ind=) {refl}) 
                  x  cong  z  fib Y (z x)) ind=)
               ≡⟨ ext  x  cong-cong ind=) 
                  x  cong (fib Y) (cong  z  z x) ind=))
               ≡⟨ refl 
                  x  cong (fib Y) (happly ind= x))
               ≡⟨ sym (ext coh) 
                  x  trans (indmor= g x) (indmor= f (indmor g x)))
                where open ≡-Reasoning

comp-is-id-ext : {D : Set₁} -> {X Y : Fam D} ->
                 (f : X Fam⇒ Y) -> (g : Y Fam⇒ X) ->
                 (ind= : (x : _) -> x  (indmor f) (indmor g x)) ->
                 (coh : (y : ind Y) -> trans (indmor= g y) (indmor= f (indmor g y))
                                           cong (fib Y) (ind= y)) ->
                 (f Fam∘ g)  FamId Y
comp-is-id-ext {X = X} {Y} f g ind= coh
 = comp-is-id f g (ext ind=)  y  trans (coh y) (cong (cong (fib Y))
                                                         (sym (ext-β ind= y))))

{-------------------------------------------------------}

{----- Fam is a monad ----------------------------------}

-- Unit

Fam-η : {I : Set₁} -> I -> Fam I
Fam-η i = ( , const i)

-- Multiplication

Fam-μ : {D : Set₁} -> Fam (Fam D)  Fam D
Fam-μ {D} (A , B) = (Σ A (ind  B) , g)
                    where g : Σ A (ind  B) -> D
                          g (a , k) = fib (B a) k

-- Bind

_Fam->>=_ : {X Y : Set₁} -> Fam X  (X  Fam Y) -> Fam Y
P Fam->>= f = Fam-μ (Fam-map f P)

-- Dependent bind

_Fam->>==_ : {D : Set₁} -> {E : D -> Set₁} -> Fam D ->
             ((d : D) -> Fam (E d)) -> Fam (Σ D E)
P Fam->>== f = P Fam->>=  x  Fam-map  p  (x , p)) (f x))

{-------------------------------------------------------}

{----- Constructions on Fam ----------------------------}

-- Products (in the comma category Σ[ D ∈ Set1 ] (Fam D))

πFam : (S : Set){D : S -> Set1} -> ( S (Fam  D)) -> Fam ( S D)
πFam S T = ((s : S) -> ind (T s)) , \ f s -> fib (T s) (f s)

-- Powers as degenerate products

_Fam⟶_ : (S : Set){D : Set1} -> Fam D -> Fam (S -> D)
S Fam⟶ T = πFam S  _  T)

-- Coproduct of families

 : {D : Set1} -> (A : Set) -> (A -> Fam D) -> Fam D
 A f = (Σ[ a  A ] ind (f a)) ,  { (a , x)  fib (f a) x })

-- Binary coproduct
_Fam+_ : {D : Set1} -> Fam D -> Fam D -> Fam D
(A , P) Fam+ (B , Q) = (A  B) , [ P , Q ]′

{-------------------------------------------------------}