open import Algebra.Ring.Module
open import Algebra.Group.Ab
open import Algebra.Group
open import Algebra.Ring

open import Cat.Abelian.Instances.Ab
open import Cat.Abelian.Base
open import Cat.Prelude

module Algebra.Ring.Module.Category {β„“} (R : Ring β„“) where

The category R-ModπŸ”—

Let us investigate the structure of the category RR-Mod, for whatever your favourite ring RR is1. The first thing we’ll show is that it admits an Ab{{\mathbf{Ab}}}-enrichment. This is the usual β€œpointwise” group structure, but proving that the pointwise sum is a still a linear map is, ahem, very annoying. See for yourself:

R-Mod-ab-category : βˆ€ {β„“β€²} β†’ Ab-category (R-Mod β„“β€² R)
R-Mod-ab-category .Group-on-hom A B = to-group-on grp  where
  module A = Module A
  module B = Module B
  grp : make-group (R-Mod.Hom A B)
  grp .group-is-set = R-Mod.Hom-set _ _

  grp .mul f g .map x = f .map x B.+ g .map x
  grp .mul f g .linear r m s n =
    fβ‚˜ (r A.⋆ m A.+ s A.⋆ n) B.+ gβ‚˜ (r A.⋆ m A.+ s A.⋆ n)       β‰‘βŸ¨ apβ‚‚ B._+_ (f .linear _ _ _ _) (g .linear _ _ _ _) βŸ©β‰‘
    (r B.⋆ fβ‚˜ m B.+ s B.⋆ fβ‚˜ n) B.+ (r B.⋆ gβ‚˜ m B.+ s B.⋆ gβ‚˜ n) β‰‘βŸ¨ B.G.pullr (B.G.pulll B.G.commutative) βŸ©β‰‘
    r B.⋆ fβ‚˜ m B.+ (r B.⋆ gβ‚˜ m B.+ s B.⋆ fβ‚˜ n) B.+ s B.⋆ gβ‚˜ n   β‰‘βŸ¨ B.G.pulll (B.G.pulll (sym (B.⋆-add-r r _ _))) βŸ©β‰‘
    (r B.⋆ (fβ‚˜ m B.+ gβ‚˜ m) B.+ (s B.⋆ fβ‚˜ n)) B.+ (s B.⋆ gβ‚˜ n)   β‰‘βŸ¨ B.G.pullr (sym (B.⋆-add-r s _ _)) βŸ©β‰‘
    r B.⋆ (fβ‚˜ m B.+ gβ‚˜ m) B.+ s B.⋆ (fβ‚˜ n B.+ gβ‚˜ n)             ∎
    where
      fβ‚˜ = f .map
      gβ‚˜ = g .map
The rest of the construction is also Just Like That, so I’m going to keep it in this <details> element out of decency.
  grp .unit .map x    = B.G.unit
  grp .unit .linear r m s n =
    B.G.unit                          β‰‘Λ˜βŸ¨ B.⋆-group-hom.pres-id _ βŸ©β‰‘Λ˜
    s B.⋆ B.G.unit                    β‰‘Λ˜βŸ¨ B.G.eliml (B.⋆-group-hom.pres-id _) βŸ©β‰‘Λ˜
    r B.⋆ B.G.unit B.+ s B.⋆ B.G.unit ∎
  grp .inv f .map x   = B.G.inverse (f .map x)
  grp .inv f .linear r m s n =
       ap B.G.inverse (f .linear r m s n)
    Β·Β· B.G.inv-comm
    Β·Β· B.G.commutative
     βˆ™ apβ‚‚ B._+_ (sym (B.⋆-group-hom.pres-inv _)) (sym (B.⋆-group-hom.pres-inv _))
  grp .assoc x y z = Linear-map-path (funext Ξ» x β†’ sym B.G.associative)
  grp .invl x = Linear-map-path (funext Ξ» x β†’ B.G.inversel)
  grp .invr x = Linear-map-path (funext Ξ» x β†’ B.G.inverser)
  grp .idl x = Linear-map-path (funext Ξ» x β†’ B.G.idl)

R-Mod-ab-category .Hom-grp-ab A B f g =
  Linear-map-path (funext Ξ» x β†’ Module.G.commutative B)
R-Mod-ab-category .∘-linear-l {C = C} f g h =
  Linear-map-path refl
R-Mod-ab-category .∘-linear-r {B = B} {C} f g h =
  Linear-map-path $ funext Ξ» x β†’
    f .map (g .map x) C.+ f .map (h .map x)                     β‰‘βŸ¨ apβ‚‚ C._+_ (sym (C.⋆-id _)) (sym (C.⋆-id _)) βŸ©β‰‘
    R.1r C.⋆ f .map (g .map x) C.+ (R.1r C.⋆ f .map (h .map x)) β‰‘βŸ¨ sym (f .linear R.1r (g .map x) R.1r (h .map x)) βŸ©β‰‘
    f .map (R.1r B.⋆ g .map x B.+ R.1r B.⋆ h .map x)            β‰‘βŸ¨ ap (f .map) (apβ‚‚ B._+_ (B.⋆-id _) (B.⋆-id _)) βŸ©β‰‘
    f .map (g .map x B.+ h .map x)                              ∎
  where
    module C = Module C
    module B = Module B

Finite biproductsπŸ”—

Let’s now prove that RR-Mod is a preadditive category. This is exactly as in Ab{{\mathbf{Ab}}}: The zero object is the zero group, equipped with its unique choice of RR-module structure, and direct products MβŠ•NM \oplus N are given by direct products of the underlying groups MGβŠ•NGM_G \oplus N_G with the canonical choice of RR-module structure.

The zero object is simple, because the unit type is so well-behaved2 when it comes to definitional equality: Everything is constantly the unit, including the paths, which are all reflexivity.

R-Mod-is-additive : is-additive (R-Mod _ R)
R-Mod-is-additive .has-ab = R-Mod-ab-category
R-Mod-is-additive .has-terminal = record
  { top  = _ , βˆ…α΄Ή
  ; has⊀ = Ξ» x β†’ contr
    (record { map    = Ξ» _ β†’ lift tt
            ; linear = Ξ» _ _ _ _ β†’ refl
            })
    (Ξ» _ β†’ Linear-map-path refl)
  }
  where
    βˆ…α΄Ή : Module-on R (Ab-is-additive .has-terminal .Terminal.top)
    βˆ…α΄Ή .Module-on._⋆_ = Ξ» _ _ β†’ lift tt
    βˆ…α΄Ή .Module-on.⋆-id _        = refl
    βˆ…α΄Ή .Module-on.⋆-add-r _ _ _ = refl
    βˆ…α΄Ή .Module-on.⋆-add-l _ _ _ = refl
    βˆ…α΄Ή .Module-on.⋆-assoc _ _ _ = refl

For the direct products, on the other hand, we have to do a bit more work. Like we mentioned before, the direct product of modules is built on the direct product of abelian groups (which is, in turn, built on the Cartesian product of types). The module action, and its laws, are defined pointwise using the RR-module structures of MM and NN:

R-Mod-is-additive .has-prods M N = prod where
  module P = is-additive.Product
    Ab-is-additive
    (Ab-is-additive .has-prods (M .fst) (N .fst))
  module M = Module M
  module N = Module N

  MβŠ•α΅£N : Module-on R P.apex
  MβŠ•α΅£N .Module-on._⋆_ r (a , b) = r M.⋆ a , r N.⋆ b
  MβŠ•α΅£N .Module-on.⋆-id _        = Ξ£-pathp (M.⋆-id _)        (N.⋆-id _)
  MβŠ•α΅£N .Module-on.⋆-add-r _ _ _ = Ξ£-pathp (M.⋆-add-r _ _ _) (N.⋆-add-r _ _ _)
  MβŠ•α΅£N .Module-on.⋆-add-l _ _ _ = Ξ£-pathp (M.⋆-add-l _ _ _) (N.⋆-add-l _ _ _)
  MβŠ•α΅£N .Module-on.⋆-assoc _ _ _ = Ξ£-pathp (M.⋆-assoc _ _ _) (N.⋆-assoc _ _ _)

We can readily define the universal cone: The projection maps are the projection maps of the underlying type, which are definitionally linear. Proving that this cone is actually universal involves a bit of path-mangling, but it’s nothing too bad:

  open Ab-category.is-product
  open Ab-category.Product
  prod : Ab-category.Product R-Mod-ab-category M N
  prod .apex = _ , MβŠ•α΅£N
  prod .π₁ .map (a , _)    = a
  prod .π₁ .linear r m s n = refl
  prod .Ο€β‚‚ .map (_ , b)    = b
  prod .Ο€β‚‚ .linear r m s n = refl

  prod .has-is-product .⟨_,_⟩ f g .map x = f .map x , g .map x
  prod .has-is-product .⟨_,_⟩ f g .linear r m s n =
    Ξ£-pathp (f .linear _ _ _ _) (g .linear _ _ _ _)
  prod .has-is-product .Ο€β‚βˆ˜factor = Linear-map-path refl
  prod .has-is-product .Ο€β‚‚βˆ˜factor = Linear-map-path refl
  prod .has-is-product .unique other p q = Linear-map-path {β„“β€² = lzero} $ funext Ξ» x β†’
    Ξ£-pathp (ap map p $β‚š x) (ap map q $β‚š x)

  1. if you don’t have a favourite ring, just pick the integers, they’re fine.β†©οΈŽ

  2. and Lift types, tooβ†©οΈŽ