Tealeaves.Classes.Categorical.Bimonad

(*|
 This file implements bimonads. A Bimonad is a functor equipped
 with a monad/comonad structure and a distributive law over itself,
 which ensures the composition (W ∘ W) is also a monad and a
 comonad. Additionally there are some coherence laws, below, which
 may be read into several equivalent ways:

* The cojoin is a monad homomorphism (W → W ∘ W) and the counit is a
  homomorphism (W → 1)
* The join is a comonad homomorphism (W ∘ W → W) and the unit is a
  homomorphism (1 → W)
* W is a monoid in the category of comonoids (in the category of
  endofunctors)
* W is a comonoid in the category of monoids (in the category of
  endofunctors)

|*)


From Tealeaves Require Export
  Classes.Categorical.Monad
  Classes.Categorical.Comonad
  Classes.Categorical.BeckDistributiveLaw.

Import Product.Notations.
Import Functor.Notations.
Import Monad.Notations.
Import Comonad.Notations.

#[local] Generalizable Variables W A B.
#[local] Arguments map F%function_scope {Map}
  {A B}%type_scope f%function_scope _.
#[local] Arguments join T%function_scope {Join}
  {A}%type_scope _.
#[local] Arguments ret T%function_scope {Return}
  {A}%type_scope _.
#[local] Arguments extract W%function_scope {Extract}
  (A)%type_scope _.
#[local] Arguments cojoin W%function_scope {Cojoin}
  {A}%type_scope _.

Bimonad typeclass

Section Bimonad.

  Context
    (W: Type Type)
    `{Map W}
    `{Return W}
    `{Join W}
    `{Cojoin W}
    `{Extract W}
    `{BeckDistributiveLaw W W}.

  Class Bimonad :=
    { bimonad_monad :> Monad W;
      bimonad_comonad :> Comonad W;
      bimonad_distributive_law :> BeckDistributiveLaw W W;
      bimonad_dist_counit_r:
      `(map W (extract W A) bdist W W = extract W (W A));
      bimonad_dist_counit_l:
      `(extract W (W A) bdist W W = map W (extract W A));
      bimonad_cap:
      `(extract W A join W = extract W A extract W (W A));
      bimonad_baton:
      `(extract W A ret W = @id A);
      bimonad_cup:
      `(cojoin W ret W = ret W ret W (A := A));
      bimonad_butterfly:
      `(cojoin W join W (A := A) =
          map W (join W) join W map W (bdist W W)
             cojoin W map W (cojoin W));
    }.

End Bimonad.

Kleisli lifting operation bibind

Section Bimonad_kleisli_operations.

  Context
    (W: Type Type)
    `{Map W} `{Join W} `{Cojoin W}
    `{BeckDistribution W W}.

  Definition bibind {A B}: (W A W B) (W A W B) :=
    fun fjoin W map W f cojoin W.

  Definition twist: W W W W
    := fun Amap W (join W) bdist W W map W (cojoin W).

  Definition kcomposebi {A B C}:
    (W B W C)
    (W A W B)
    (W A W C) :=
    fun g fjoin W map W g twist B map W f cojoin W.

End Bimonad_kleisli_operations.

Module Notations.
  Notation "g ⋆bi f" :=
    (kcomposebi _ g f) (at level 70): tealeaves_scope.
End Notations.

Import Notations.

(*
(** ** Specifications for suboperations *)
(**********************************************************************)
Section Bimonad_suboperations.

  Context
    `{Bimonad W}.

  Lemma cobind_to_bibind: forall `(f: W A -> B),
      cobind W f = bibind W (ret W ∘ f).
  Proof.
    intros. unfold bibind. unfold_ops @Cobind_Cojoin.
    rewrite <- (fun_map_map W). reassociate <-.
    now rewrite (mon_join_map_ret W).
  Qed.

  Lemma bind_to_bibind: forall `(f: A -> W B),
      bind W f = bibind W (f ∘ extract W).
  Proof.
    intros. unfold bibind. unfold_ops @Bind_Join.
    rewrite <- (fun_map_map W).
    reassociate <-. reassociate ->.
    now rewrite (com_map_extr_cojoin W).
  Qed.

  Lemma map_to_bibind: forall `(f: A -> B),
      map W f = bibind W (ret W ∘ f ∘ extract W).
  Proof.
    intros. unfold bibind.
    do 2 rewrite <- (fun_map_map W).
    repeat reassociate <-.
    rewrite (mon_join_map_ret W).
    reassociate ->.
    now rewrite (com_map_extr_cojoin W).
  Qed.

End Bimonad_suboperations.

(** ** Bimonadic Kleisli composition laws *)
(**********************************************************************)
Section Bimonad_kleisli_composition.

  Context
    `{Bimonad W}.

  (** Composition where <<g>> is a co-Kleisli arrow reduces to usual
      Kleisli composition. *)

  Lemma bi_kcompose1 {A B C}: forall (g: B -> W C) (f: W A -> W B),
      (g ∘ extract W) ⋆bi f = g ⋆ f.
  Proof.
    intros. unfold kcomposebi, kcompose.
    rewrite <- (fun_map_map W).
    reassociate <-. unfold twist.
    repeat reassociate <-.
    reassociate -> near (map W (join W)).
    rewrite (fun_map_map W). rewrite (bimonad_cap W).
    rewrite <- (fun_map_map W).
    repeat reassociate <-.
    reassociate -> near (bdist W W).
    rewrite (bimonad_dist_counit_r W).
    reassociate -> near (map W (cojoin W)).
    rewrite <- (natural (ϕ := @extract W _)); unfold_ops @Map_I.
    reassociate <-. reassociate -> near (cojoin W (A := B)).
    rewrite (com_map_extr_cojoin W).
    reassociate -> near (map W f).
    rewrite <- (natural (ϕ := @extract W _)); unfold_ops @Map_I.
    repeat reassociate ->. rewrite (com_extract_cojoin W).
    reflexivity.
  Qed.

  (** Composition where <<f>> is a Kleisli arrow reduces to usual
      co-Kleisli composition. *)

  Lemma bi_kcompose2 {A B C}: forall (g: W B -> W C) (f: W A -> B),
      g ⋆bi (ret W ∘ f) = g co⋆ f.
  Proof.
    intros. unfold kcomposebi, kcompose.
    rewrite <- (fun_map_map W).
    reassociate <-. unfold twist.
    repeat reassociate <-.
    reassociate -> near (map W (ret W)).
    rewrite (fun_map_map W). rewrite (bimonad_cup W).
    rewrite <- (fun_map_map W).
    repeat reassociate <-.
    reassociate -> near (map W (ret W) (A := W B)).
    rewrite (dist_unit_r W W).
    reassociate -> near (ret W).
    rewrite (natural (ϕ := @ret W _)); unfold_ops @Map_I.
    reassociate <-. reassociate -> near (map W (ret W)).
    rewrite (mon_join_map_ret W).
    reassociate -> near (ret W).
    rewrite (natural (ϕ := @ret W _)); unfold_ops @Map_I.
    reassociate <-. rewrite (mon_join_ret W).
    reflexivity.
  Qed.

End Bimonad_kleisli_composition.

(** ** Functoriality of bibind *)
(**********************************************************************)
Section Bimonad_bibind.

  Context
    `{Bimonad W}.

  Definition bind_id {A}:
    bibind W (ret W ∘ extract W) = @id (W A).
  Proof.
    intros. unfold bibind.
    rewrite <- (fun_map_map W).
    do 2 reassociate -> on left.
    rewrite (com_map_extr_cojoin W).
    reassociate <- on left.
    now rewrite (mon_join_map_ret W).
  Qed.

  Definition bind_functorial {A B C}:
 forall (g: W B -> W C) (f: W A -> W B),
      bibind W (g ⋆bi f) = bibind W g ∘ bibind W f.
  Proof.
    intros. unfold bibind. unfold kcomposebi.
    (** *)
    repeat reassociate -> on left.
    rewrite <- (fun_map_map W).
    repeat  reassociate <- on left.
    rewrite <- (mon_join_join W).
    (** *)
    rewrite <- (fun_map_map W).
    repeat reassociate -> on left.
    rewrite <- (com_cojoin_cojoin W).
    repeat  reassociate <- on left.
    (** *)
    repeat reassociate -> on left.
    rewrite <- (fun_map_map W).
    repeat  reassociate <- on left.
    reassociate -> near (map W (map W g)).
    change (map W (map W g)) with (map (W ∘ W) g).
    Set Keyed Unification.
    rewrite <- (natural (ϕ := @join W _)).
    Unset Keyed Unification.
    (** *)
    rewrite <- (fun_map_map W).
    repeat reassociate -> on left.
    reassociate <- near (map W (map W f)).
    change (map W (map W f)) with (map (W ∘ W) f).
    Set Keyed Unification.
    rewrite (natural (ϕ := @cojoin W _)).
    Unset Keyed Unification.
    repeat reassociate <- on left.
    repeat reassociate <- on right.
    (** *)
    reassociate -> near (join W).
    rewrite (bimonad_butterfly W).
    repeat reassociate <-.
    reassociate -> near (join W).
    Set Keyed Unification.
    rewrite (natural (ϕ := @join W _) (μ W)).
    Unset Keyed Unification.
    reassociate -> near (map W (cojoin W)).
    Set Keyed Unification.
    rewrite <- (natural (ϕ := @cojoin W _) (cojoin W)).
    Unset Keyed Unification.
    unfold twist.
    repeat reassociate <-.
    now repeat rewrite <- (fun_map_map W).
  Qed.

End Bimonad_bibind.
*)