MathClasses.interfaces.functors

Require Import
  abstract_algebra.
Require
  theory.setoids.

Section functor_class.
  Context `{Category C} `{Category D} (M: C D).

  Class Fmap: Type := fmap: {v w: C}, (v w) (M v M w).

  Class Functor `(Fmap): Prop :=
    { functor_from: Category C
    ; functor_to: Category D
    ; functor_morphism:> a b: C, Setoid_Morphism (@fmap _ a b)
    ; preserves_id: `(fmap (cat_id: a a) = cat_id)
    ; preserves_comp `(f: y z) `(g: x y): fmap (f g) = fmap f fmap g }.
End functor_class.

Typeclasses Transparent Fmap.


Section id_functor.
  Context `{Category C}.

  Global Instance: Fmap id := λ _ _, id.

  Global Instance id_functor: Functor (id: C C) _.
  Proof.
   constructor; try reflexivity; try apply _. intros.
   change (Setoid_Morphism (id: (a b) (a b))).
   apply _.
  Qed.
End id_functor.

Section compose_functors.
  Context
    A B C
    `{!Arrows A} `{!Arrows B} `{!Arrows C}
    `{!CatId A} `{!CatId B} `{!CatId C}
    `{!CatComp A} `{!CatComp B} `{!CatComp C}
    `{ x y: A, Equiv (x y)}
    `{ x y: B, Equiv (x y)}
    `{ x y: C, Equiv (x y)}
    `{!Functor (f: B C) f'} `{!Functor (g: A B) g'}.

  Global Instance comp_Fmap: Fmap (f g) := λ _ _, fmap f fmap g.

  Global Instance compose_functors: Functor (f g) _.
  Proof with intuition; try apply _.
   pose proof (functor_from g).
   pose proof (functor_to g).
   pose proof (functor_to f).
   constructor; intros; try apply _.
     apply (@setoids.compose_setoid_morphism _ _ _ _ _ _)...
     apply (@functor_morphism _ _ _ _ _ _ _ _ _ _ f _)...
    change (fmap f (fmap g (cat_id: a a)) = cat_id).
    repeat try rewrite preserves_id...
   change (fmap f (fmap g (f0 g0)) = fmap f (fmap g f0) fmap f (fmap g g0)).
   repeat try rewrite preserves_comp...
  Qed.
End compose_functors.

The Functor class is nice and abstract and theory-friendly, but not as convenient to use for regular programming as Haskell's Functor class. The reason for this is that our Functor is parameterized on two Categories, which by necessity bundle setoid- ness and setoid-morphism-ness into objects and arrows, respectively. The Haskell Functor class, by contrast, is essentially specialized for endofunctors on the category of Haskell types and functions between them. The latter corresponds to our setoid.Object category.
To recover convenience, we introduce a second functor type class tailored specifically to functors of this kind. The specialization allows us to forgo bundling, and lets us recover the down-to-earth Type→Type type for the type constructor, and the (a→b)→(F a→F b) type for the function map, with all setoid/morphism proofs hidden in the structure class in Prop.
To justify this definition, in theory/functors we show that instances of this new functor class do indeed give rise to instances of the original nice abstract Functor class.

Class SFmap (M : Type Type) := sfmap: `(A B), (M A M B).

Class SFunctor (M : Type Type)
     `{ `{Equiv A}, Equiv (M A)} `{SFmap M} : Prop :=
  { sfunctor_setoid `{Setoid A} :> Setoid (M A)
  ; sfmap_proper `{Setoid A} `{Setoid B} :>
      Proper (((=) ==> (=)) ==> ((=) ==> (=))) (@sfmap M _ A B)
  ; sfmap_id `{Setoid A} : sfmap id = id
  ; sfmap_comp `{Equiv A} `{Equiv B} `{Equiv C} `{!Setoid_Morphism (f : B C)} `{!Setoid_Morphism (g : A B)} :
      sfmap (f g) = sfmap f sfmap g }.