Library SSProve.Mon.Monoid

From Coq Require Import ssreflect ssrfun ssrbool List.
From Coq Require Import FunctionalExtensionality.
From SSProve.Mon Require Export Base.
From Coq Require Import Relation_Definitions Morphisms.
From SSProve.Mon Require Import SPropBase SPropMonadicStructures.

Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Set Primitive Projections.

Section Monoid.
  Record monoid :=
    mkMonoid
      { monoid_carrier :> Type
      ; monoid_unit : monoid_carrier
      ; monoid_mult : monoid_carrier monoid_carrier monoid_carrier
      ; monoid_law1 : m, monoid_mult monoid_unit m = m
      ; monoid_law2 : m, monoid_mult m monoid_unit = m
      ; monoid_law3 : m1 m2 m3,
          monoid_mult (monoid_mult m1 m2) m3 = monoid_mult m1 (monoid_mult m2 m3)
      }.

  Definition e := monoid_unit.
End Monoid.

Notation "x ⋅ y" := (monoid_mult x y) (at level 55).

Section MonoidAction.

  Record monoid_action (M : monoid) :=
    mkAction
      { monact_carrier :> Type
      ; monact_action : M monact_carrier monact_carrier
      ; monact_unit : x, monact_action (e M) x = x
      ; monact_mult : m1 m2 x, monact_action (m1 m2) x = monact_action m1 (monact_action m2 x)
      }.

End MonoidAction.

Notation "m ⧕ x" := (monact_action m x) (at level 55).

Section MonoidExamples.

  Program Definition endMonoid (X : Type) : monoid :=
    @mkMonoid (X X) id (fun f g xf (g x)) _ _ _.

  Program Definition unitMonoid : monoid :=
    @mkMonoid unit tt (fun _ _tt) _ _ _.
  (* This does not solve the goal but the latter does ??? *)
  (* Solve Obligations with move: m =>  //. *)
  Solve Obligations with destruct m ; reflexivity.

  Program Definition oneMonoid : monoid := endMonoid False.

  Import FunctionalExtensionality.
  Program Definition pointwiseMonoid (X:Type) (M:monoid) : monoid :=
    @mkMonoid (X M) (fun _e M) (fun f g xf x g x) _ _ _.
  Next Obligation. extensionality y ; rewrite monoid_law1 //. Qed.
  Next Obligation. extensionality y ; rewrite monoid_law2 //. Qed.
  Next Obligation. extensionality y ; rewrite monoid_law3 //. Qed.

  Program Definition listMonoid (X:Type) : monoid :=
    @mkMonoid (list X) nil (@app _) _ (@List.app_nil_r _) (fun l m neq_sym (@List.app_assoc _ l m n)).

  Program Definition prodMonoid (M1 M2:monoid) : monoid :=
    @mkMonoid (M1 × M2) e M1, e M2 (fun x ynfst x nfst y, nsnd x nsnd y)
              _ _ _.
  Next Obligation. rewrite !monoid_law1 //. Qed.
  Next Obligation. rewrite !monoid_law2 //. Qed.
  Next Obligation. rewrite !monoid_law3 //. Qed.

  Program Definition optionMonoid (X:Type) : monoid :=
    @mkMonoid (option X) None (fun m1 m2match m1 with
                                         | Nonem2
                                         | Some xSome x end) _ _ _.
  Next Obligation. move: m ⇒ [] //. Qed.
  Next Obligation. move: m1 m2 m3 ⇒ [?|] [?|] [?|] //. Qed.

  Import SPropNotations.
  Program Definition overwriteMonoid (X:Type) : monoid :=
    @mkMonoid { f : X X | (m: optionMonoid X), x, Some (f x) = m⋅(Some x)}
              (exist _ id _)
              (fun f gexist _ (proj1_sig f \o proj1_sig g) _) _ _ _.
  Next Obligation. None. move⇒ ? //. Qed.
  Next Obligation.
    move: H1 H2 H H0mf Hf mg Hg.
     (@monoid_mult (optionMonoid X) mf mg).
    move⇒ ? ; move: mf mg Hf Hg ⇒ [?|] [?|] Hf Hg /= ; try by apply Hf.
    all: eapply (eq_trans (Hf _)); apply Hg.
  Qed.
  Next Obligation. compute. f_equal.
    apply ax_proof_irrel.
  Qed.
  Next Obligation. compute. f_equal.
    apply ax_proof_irrel.
  Qed.
  Next Obligation. compute. f_equal. apply ax_proof_irrel. Qed.

End MonoidExamples.

Section ActionExamples.
  Program Definition multAction (M:monoid) : monoid_action M :=
    @mkAction M M (fun m1 m2m1 m2) _ _.
  Next Obligation. rewrite monoid_law1 //. Defined.
  Next Obligation. rewrite monoid_law3 //. Defined.

  Program Definition trivialAction (M : monoid) : monoid_action M :=
    @mkAction M unit (fun _ xx) _ _.

  Program Definition endAction (X:Type) : monoid_action (endMonoid X) :=
    @mkAction _ X (fun f xf x) _ _.

  Program Definition unitAction (X:Type) : monoid_action unitMonoid :=
   @mkAction _ X (fun _ xx) _ _.

  Program Definition oneAction (X:Type) : monoid_action oneMonoid :=
    @mkAction _ X (fun _ xx) _ _.

  Section PointwiseAction.
    Context (A:Type) (M:monoid) (X:monoid_action M).
    Let A_M := pointwiseMonoid A M.
    Definition pointwise_action (m:A_M) (x : A X) (a:A) := m a x a.

    Definition pointwiseActionFromLaws pf1 pf2 :=
      @mkAction A_M (A X) pointwise_action pf1 pf2.

    Import FunctionalExtensionality.
    Program Definition pointwiseAction := pointwiseActionFromLaws _ _.
    Next Obligation. cbv ; extensionality a ; rewrite monact_unit //. Qed.
    Next Obligation. cbv ; extensionality a ; rewrite monact_mult //. Qed.

  End PointwiseAction.

  Section ProdAction.
    Context (M1 M2: monoid) (X1: monoid_action M1) (X2: monoid_action M2).
    Let M12 := prodMonoid M1 M2.
    Let X12 := X1 × X2.
    Definition product_action (m12 : M12) (x12:X12) :=
      nfst m12 nfst x12, nsnd m12 nsnd x12.
    Definition prodActionFromLaws pf1 pf2 :=
      @mkAction M12 (X1 × X2) product_action pf1 pf2.
    Program Definition prodAction := prodActionFromLaws _ _.
    Next Obligation. rewrite /product_action 2!monact_unit //. Qed.
    Next Obligation. rewrite /product_action 2!monact_mult //. Qed.
  End ProdAction.

  Program Definition optionAction (X:Type) : monoid_action (optionMonoid X):=
    @mkAction _ X (fun m xmatch m with Nonex | Some x'x' end) _ _.
  Next Obligation. move: m1 m2 ⇒ [?|] [?|] //. Qed.

  Program Definition overwriteAction (X:Type)
    : monoid_action (overwriteMonoid X) :=
    @mkAction _ X (fun f xproj1_sig f x) _ _.
End ActionExamples.

Section MonoidStrictification.
  (* Given any monoid with monoid laws holding propositionally,
     we can turn it into one where the laws hold definitionally *)

  Context (M : monoid).
  Import SPropNotations.

  Definition SM := { f : M M | m, m', f m' = m m'}.
  Program Definition se : SM := exist _ id _.
  Next Obligation.
     (e M). intros ; rewrite monoid_law1 //.
  Qed.

  Program Definition smult (sm1 sm2 : SM) : SM :=
    exist _ (proj1_sig sm1 \o proj1_sig sm2) _.
  Next Obligation.
    move:sm1 sm2⇒ [? [m1 H1]] [? [m2 H2]].
     (m1 m2) ; movem /=.
    rewrite monoid_law3. eelim (H2 _). eelim (H1 _) ⇒ //.
  Qed.

  Program Definition strict_monoid := @mkMonoid SM se smult _ _ _.

  Program Definition embed (m:M) : SM := exist _ (monoid_mult m) _.
  Next Obligation. m ; move⇒ ? //. Qed.

  Definition project (sm : SM) : M := proj1_sig sm (e M).

  Lemma embed_project_id : m, project (embed m) = m.
  Proof. intro. cbv. rewrite monoid_law2 //. Qed.

  Lemma sig_eq : (A : Type) (P : A Prop) (mx my : {x : A | P x}),
       proj1_sig mx = proj1_sig my mx = my.
  Proof.
    intros A P [mx ?] [my ?] H. simpl in H.
    induction H. compute. f_equal. apply ax_proof_irrel.
  Qed.

  Import SPropAxioms.
  Lemma project_embed_id : sm, embed (project sm) = sm.
  Proof.
    intro sm. apply sig_eq ; extensionality m0.
    cbv. move: (proj2_sig sm) ⇒ [m Hm].
    pose (H0 := Hm m0).
    apply eq_sym in H0.
    unshelve eapply (eq_trans _ H0).
    f_equiv. pose (He := Hm (e M)).
    apply (eq_trans He).
    rewrite monoid_law2 //.
   Qed.
  Next Obligation. compute. destruct m. f_equal. apply ax_proof_irrel. Qed.
  Next Obligation. compute. destruct m. f_equal. apply ax_proof_irrel. Qed.
  Next Obligation. compute. f_equal. apply ax_proof_irrel. Qed.

End MonoidStrictification.

(* A strictified version of the free monoid on a type O *)
(* Useful to obtain update monads satisfying definitional monad laws *)
Section StrictList.
  Context (O:Type).
  Definition strict_list_monoid : monoid := strict_monoid (listMonoid O).
  Import SPropNotations.
  Definition inject (o:O) : strict_list_monoid :=
    @embed (listMonoid O) (cons o nil).
  Definition snil : strict_list_monoid :=
    @embed (listMonoid O) nil.
End StrictList.