{-# OPTIONS --without-K --safe #-}

module Categories.Monad.Construction.Kleisli where

-- Definition of kleisli triple as a relative monad
-- and the equivalence of kleisli triple and monad
-- see https://ncatlab.org/nlab/show/extension+system (the naming differs)

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

  -- a kleisli triple is a relative monad with J = idF
  KleisliTriple : Set (o    e)
  KleisliTriple = RMonad {C = C} idF

  -- kleisli triples are equivalent to monads
  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)

      -- Extract a functor from the relative monad
      F : Endofunctor C
      F = RMonad⇒Functor K
      open Functor F using (F₀; F₁)

      -- constructing η from unit
      η = ntHelper {F = idF} {G = F} record
        { η = λ X  unit
        ; commute = λ f  sym K.identityʳ
        }

      -- helper for μ.commute and assoc'
      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)                 

      -- constructing μ from extend
      μ = 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               ≈⟨ assoc²βγ 
        (μ.η Z  F₁ l)  μ.η Y  F₁ k