{-# OPTIONS --without-K --safe #-}
module Categories.Monad.Construction.Kleisli where
open import Level
open import Categories.Category.Core using (Category)
open import Categories.Monad.Relative using (RMonad⇒Functor) renaming (Monad to RMonad)
open import Categories.Monad using (Monad)
open import Categories.Functor using (Functor; Endofunctor; _∘F_) renaming (id to idF)
open import Categories.NaturalTransformation using (ntHelper; NaturalTransformation)
import Categories.Morphism.Reasoning as MR
private
  variable
    o ℓ e : Level
module _ (C : Category o ℓ e) where
  open Category C
  open HomReasoning
  open Equiv
  open MR C
  
  KleisliTriple : Set (o ⊔ ℓ ⊔ e)
  KleisliTriple = RMonad {C = C} idF
  
  Kleisli⇒Monad : KleisliTriple → Monad C
  Kleisli⇒Monad K = record
    { F = F
    ; η = η
    ; μ = μ
    ; assoc = assoc'
    ; sym-assoc = sym assoc'
    ; identityˡ = identityˡ'
    ; identityʳ = K.identityʳ
    }
    where
      module K = RMonad K
      open K using (unit; extend)
      
      F : Endofunctor C
      F = RMonad⇒Functor K
      open Functor F using (F₀; F₁)
      
      η = ntHelper {F = idF} {G = F} record
        { η = λ X → unit
        ; commute = λ f → sym K.identityʳ
        }
      
      commute' : ∀ {X Y : Obj } (f : X ⇒ F₀ Y) → extend id ∘ extend (unit ∘ extend f) ≈ extend f ∘ (extend id)
      commute' {X} {Y} f = begin
        extend id ∘ extend (unit ∘ extend f)   ≈⟨ K.sym-assoc ⟩
        extend (extend id ∘ unit ∘ extend f)   ≈⟨ K.extend-≈ (pullˡ K.identityʳ) ⟩
        extend (id ∘ extend f)                 ≈⟨ K.extend-≈ id-comm-sym ⟩
        extend (extend f ∘ id)                 ≈⟨ K.assoc ⟩
        extend f ∘ (extend id)                 ∎
      
      μ = ntHelper {F = F ∘F F} {G = F} record
        { η = λ X → extend id
        ; commute = λ f → commute' (unit ∘ f)
        }
      module η = NaturalTransformation η
      module μ = NaturalTransformation μ
      assoc' : ∀ {X : Obj} → μ.η X ∘ F₁ (μ.η X) ≈ μ.η X ∘ μ.η (F₀ X)
      assoc' = commute' id
      identityˡ' : ∀ {X : Obj} → μ.η X ∘ F₁ (η.η X) ≈ id
      identityˡ' = begin 
        extend id ∘ extend (unit ∘ unit)   ≈⟨ K.sym-assoc ⟩ 
        extend (extend id ∘ (unit ∘ unit)) ≈⟨ K.extend-≈ (pullˡ K.identityʳ) ⟩
        extend (id ∘ unit)                 ≈⟨ K.extend-≈ identityˡ ⟩
        extend unit                        ≈⟨ K.identityˡ ⟩
        id                                 ∎
  Monad⇒Kleisli : Monad C → KleisliTriple
  Monad⇒Kleisli M = record
    { F₀ = F₀
    ; unit = η.η _
    ; extend = λ f → μ.η _ ∘ F₁ f
    ; identityʳ = identityʳ'
    ; identityˡ = M.identityˡ
    ; assoc = assoc'
    ; sym-assoc = sym assoc'
    ; extend-≈ = λ fg → ∘-resp-≈ʳ (F-resp-≈ fg)
    }
    where
      module M = Monad M
      open Monad M using (F; η; μ)
      open Functor F
      identityʳ' : ∀ {X Y} {k : X ⇒ F₀ Y} → (μ.η Y ∘ F₁ k) ∘ η.η X ≈ k
      identityʳ' {X} {Y} {k}  = begin 
        ((μ.η Y ∘ F₁ k) ∘ η.η X)   ≈⟨ pullʳ (sym (η.commute _)) ⟩
        (μ.η Y ∘ η.η (F₀ Y) ∘ k)   ≈⟨ cancelˡ M.identityʳ ⟩
        k                          ∎
      assoc' : ∀  {X Y Z} {k : X ⇒ F₀ Y} {l : Y ⇒ F₀ Z} → μ.η Z ∘ F₁ ((μ.η Z ∘ F₁ l) ∘ k) ≈ (μ.η Z ∘ F₁ l) ∘ μ.η Y ∘ F₁ k
      assoc' {X} {Y} {Z} {k} {l} = begin
        (μ.η Z ∘ F₁ ((μ.η Z ∘ F₁ l) ∘ k))           ≈⟨ refl⟩∘⟨ homomorphism ⟩ 
        (μ.η Z ∘ F₁ (μ.η Z ∘ ₁ l) ∘ F₁ k)           ≈⟨ refl⟩∘⟨ homomorphism ⟩∘⟨refl ⟩ 
        (μ.η Z ∘ (F₁ (μ.η Z) ∘ F₁ (F₁ l)) ∘ F₁ k)   ≈⟨ pullˡ (pullˡ M.assoc) ⟩ 
        (((μ.η Z ∘ μ.η (F₀ Z)) ∘ F₁ (F₁ l)) ∘ F₁ k) ≈⟨ pullʳ (μ.commute l) ⟩∘⟨refl ⟩
        (μ.η Z ∘ F₁ l ∘ μ.η Y) ∘ F₁ k               ≈⟨ trans assoc²' sym-assoc ⟩
        (μ.η Z ∘ F₁ l) ∘ μ.η Y ∘ F₁ k               ∎