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

module Categories.Comonad.Construction.CoKleisli where

-- Definition of kleisli cotriple as a relative comonad
-- and the equivalence of kleisli cotriple and comonad
-- mostly dual to Categories.Monad.Construction.Kleisli

open import Level
open import Categories.Category.Core using (Category)
open import Categories.Comonad.Relative using (RComonad⇒Functor) renaming (Comonad to RComonad)
open import Categories.Comonad using (Comonad)
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 cotriple is a relative monad with J = idF
  KleisliCotriple : Set (o    e)
  KleisliCotriple = RComonad {C = C} idF

  -- kleisli cotriples are equivalent to comonads
  Kleisli⇒Comonad : KleisliCotriple  Comonad C
  Kleisli⇒Comonad K = record
    { F = F
    ; ε = ε
    ; δ = δ
    ; assoc = assoc'
    ; sym-assoc = sym assoc'
    ; identityˡ = identityˡ'
    ; identityʳ = K.identityʳ
    }
    where
      module K = RComonad K
      open K using (counit; cobind)

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

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

      -- helper for δ.commute and assoc'
      commute' :  {X Y : Obj } (f : F₀ Y  X)  cobind id  cobind f  cobind (cobind f  counit)  cobind id
      commute' {X} {Y} f = begin
        cobind id  cobind f                     ≈⟨ K.sym-assoc 
        cobind (id  cobind f)                   ≈⟨ K.cobind-≈ id-comm-sym 
        cobind (cobind f  id)                   ≈⟨ sym (K.cobind-≈ (pullʳ K.identityʳ)) 
        cobind ((cobind f  counit)  cobind id) ≈⟨ K.assoc 
        cobind (cobind f  counit)  cobind id   

      -- constructing δ from cobind
      δ = ntHelper {F = F} {G = F ∘F F} record 
        { η = λ X  cobind id 
        ; commute = λ f  commute' (f  counit) 
        }

      module ε = NaturalTransformation ε
      module δ = NaturalTransformation δ

      assoc' : {X : Obj}  δ.η (F₀ X)  δ.η X  F₁ (δ.η X)  δ.η X
      assoc' = commute' id

      identityˡ' : {X : Obj}  F₁ (ε.η X)  δ.η X  id
      identityˡ' = begin 
        cobind (counit  counit)  cobind id   ≈⟨ K.sym-assoc  
        cobind ((counit  counit)  cobind id) ≈⟨ K.cobind-≈ (pullʳ K.identityʳ)  
        cobind (counit  id)                   ≈⟨ K.cobind-≈ identityʳ  
        cobind counit                          ≈⟨ K.identityˡ  
        id                                     

  Comonad⇒Kleisli : Comonad C  KleisliCotriple
  Comonad⇒Kleisli M = record
    { F₀ = F₀
    ; counit = ε.η _
    ; cobind = λ f  F₁ f  δ.η _
    ; identityʳ = identityʳ'
    ; identityˡ = M.identityˡ
    ; assoc = assoc'
    ; sym-assoc = sym assoc'
    ; cobind-≈ = λ fg  ∘-resp-≈ˡ (F-resp-≈ fg)
    }
    where
      module M = Comonad M
      open Comonad M using (F; ε; δ)
      open Functor F

      identityʳ' : {X Y : Obj} {k : F₀ X  Y}  ε.η Y  F₁ k  δ.η X  k
      identityʳ' {X} {Y} {k} = begin 
        ε.η Y  F₁ k  δ.η X     ≈⟨ pullˡ (ε.commute _) 
        (k  ε.η (F₀ X))  δ.η X ≈⟨ cancelʳ M.identityʳ 
        k                        

      assoc' : {X Y Z : Obj} {k : F₀ X  Y} {l : F₀ Y  Z}  F₁ (l  F₁ k  δ.η X)  δ.η X  (F₁ l  δ.η Y)  F₁ k  δ.η X
      assoc' {X} {Y} {Z} {k} {l} = begin
        F₁ (l  F₁ k  δ.η X)  δ.η X             ≈⟨ homomorphism ⟩∘⟨refl 
        (F₁ l  F₁ (F₁ k  δ.η X) )  δ.η X       ≈⟨ (refl⟩∘⟨ homomorphism) ⟩∘⟨refl 
        (F₁ l  (F₁ (F₁ k)  F₁ (δ.η X)))  δ.η X ≈⟨ pullʳ (pullʳ M.sym-assoc) 
        F₁ l  F₁ (F₁ k)  δ.η (F₀ X)  δ.η X     ≈⟨ refl⟩∘⟨ pullˡ (sym (δ.commute k)) 
        F₁ l  (δ.η Y  F₁ k)  δ.η X             ≈⟨ assoc²δγ 
        (F₁ l  δ.η Y)  F₁ k  δ.η X