MathClasses.interfaces.sequences

Require Import
  List abstract_algebra theory.categories forget_algebra forget_variety
  theory.rings interfaces.universal_algebra interfaces.functors
  categories.setoids categories.varieties.
Require
  categories.product varieties.monoids categories.algebras
  categories.categories theory.setoids.

Module ua := universal_algebra.

Instance: Arrows Type := λ X Y, X → Y.


Class PoshSequence
   (free: setoids.Object → monoids.Object) `{!Fmap free}
   (inject: id ⇛ monoids.forget ∘ free)
   (extend: `((x ⟶ monoids.forget y) → (free x ⟶ y))): Prop :=
   { sequence_adjunction: ηAdjunction _ _ inject extend
   ; extend_morphism: `(Setoid_Morphism (extend x y)) }.


Section practical.

  Class ExtendToSeq (free: Type → Type) :=
    extend: ∀ {x y} `{!SgOp y} `{!MonUnit y}, (x → y) → (free x → y).

  Class InjectToSeq (free: Type → Type) := inject: ∀ x, x → free x.

  Context
   (free: Type → Type) {raw_fmap: Fmap free}
   `{∀ a, MonUnit (free a)}
   `{∀ a, SgOp (free a)}
   `{∀ a, Equiv a → Equiv (free a)}
   `{!InjectToSeq free} `{!ExtendToSeq free}.

  Class Sequence :=
    { sequence_makes_monoids:> ∀ `{Setoid a}, Monoid (free a)
    ; sequence_inject_morphism:> ∀ `{Setoid a}, Setoid_Morphism (inject a)
    ; sequence_map_morphism:> ∀ `{Equiv x} `{Equiv y} (f: x → y),
        Setoid_Morphism f → Monoid_Morphism (raw_fmap _ _ f)
    ; sequence_fmap_proper: ∀ `{Equiv x} `{Equiv y} (f g: x → y), f = g → fmap free f = raw_fmap _ _ g
    ; sequence_fmap_id: ∀ `{Equiv x}, raw_fmap _ _ (@id x) = id
    ; sequence_fmap_comp: ∀ `{Equiv x} `{Equiv y} `{Equiv z} (f: y → z) (g: x → y),
        raw_fmap _ _ (f ∘ g) = raw_fmap _ _ f ∘ raw_fmap _ _ g
    ; sequence_extend_makes_morphisms:> ∀ `{Equiv x} `{Monoid y} (f: x → y),
        Setoid_Morphism f → Monoid_Morphism (extend f)
    ; sequence_inject_natural: ∀ `{Setoid A} `{Setoid B} (f: A → B), Setoid_Morphism f →
        inject B ∘ f = raw_fmap _ _ f ∘ inject A
    ; sequence_extend_commutes: ∀ `{Setoid x} `{Monoid y} (f: x → y), Setoid_Morphism f →
        extend f ∘ inject x = f
    ; sequence_only_extend_commutes: ∀ `{Setoid x} `{Monoid y} (f: x → y), Setoid_Morphism f →
        (∀ (g: free x → y), Monoid_Morphism g → g ∘ inject x = f → g = extend f)
    ; sequence_extend_morphism: ∀ `{Setoid x} `{Monoid y} (f g: x → y),
        Setoid_Morphism f → Setoid_Morphism g →
        f = g → extend f (free:=free) = extend g (free:=free)
    }.


  Context `{PS: Sequence}.

  Program Definition posh_free (X: setoids.Object): monoids.Object := monoids.object (free X).

  Program Instance posh_fmap: functors.Fmap posh_free :=
    λ _ _ X _, raw_fmap _ _ X.

  Next Obligation. apply monoids.encode_morphism_only. destruct X. apply _. Qed.

  Instance: Functor posh_free posh_fmap.
  Proof with try apply _.
   constructor...
     repeat intro.
     constructor...
     repeat intro.
     simpl.
     apply sequence_fmap_proper.
     intro.
     apply H2.
     reflexivity.
    repeat intro.
    simpl.
    apply sequence_fmap_id.
    reflexivity.
   repeat intro.
   simpl.
   apply sequence_fmap_comp.
   reflexivity.
  Qed.

  Program Definition posh_inject: id ⇛ monoids.forget ∘ posh_free := λ a, inject a.

  Typeclasses Transparent compose.

  Program Definition posh_extend (x: setoids.Object) (y: monoids.Object)
    (X: x ⟶ monoids.forget y): posh_free x ⟶ y
    := λ u, match u return posh_free x u → y u with tt ⇒ extend X end.
  Next Obligation.
   apply monoids.encode_morphism_only.
   destruct X. simpl in ×.
   apply (sequence_extend_makes_morphisms _). apply _.
  Qed.


  Instance: NaturalTransformation posh_inject.
  Proof.
   unfold NaturalTransformation.
   intros [???] [???] [??] ?? E.
   simpl in ×.
   rewrite E.
   apply sequence_inject_natural.
   apply _.
   reflexivity.
  Qed.

  Goal @PoshSequence posh_free posh_fmap posh_inject posh_extend.
  Proof.
   constructor.
    constructor; try apply _.
    intros [x xE xH] y [f fM].
    pose proof (@monoids.decode_variety_and_ops y _ _ _).
    split.
     repeat intro.
     simpl in ×.
     rewrite H3.
     symmetry.
     apply (@sequence_extend_commutes PS x _ _ _ _ _ _ H2 f fM).
     reflexivity.
    unfold compose.
    intros [x0 h] H4 [] a.
    unfold equiv, setoids.Equiv_instance_0 in H4.
    simpl in ×.
    apply (@sequence_only_extend_commutes PS x _ _ _ _ _ _ H2 f _ (x0 tt)).
     apply (@monoids.decode_morphism_and_ops _ _ _ _ _ _ _ _ _ h).
    intros. symmetry. apply H4. reflexivity.
   unfold posh_extend.
   intros [x ??] [y ?? yV].
   constructor; try apply _.
   intros [] [] E [] a.
   simpl in ×.
   apply (@sequence_extend_morphism PS x _ _ _ _ _ _
     (@monoids.decode_variety_and_ops _ _ _ yV) _ _ _ _).
   intro. apply E. reflexivity.
  Qed.
  Definition fold `{MonUnit M} `{SgOp M}: free M → M := extend id.

  Global Instance fold_mor `{Monoid M}: Monoid_Morphism (fold (M:=M)).
  Proof. apply _. Qed.

End practical.