/-- The Yoneda embedding for preadditive categories sends an object `X` to the copresheaf sending an
object `Y` to the group of morphisms `X ⟶ Y`. At each point, we get an additional `End X`-module
structure, see `preadditiveCoyonedaObj`.
-/
@[simps]
def preadditiveCoyoneda : Cᵒᵖ ⥤ C ⥤ AddCommGroupCat.{v} where
  obj X := preadditiveCoyonedaObj X ⋙ forget₂ _ _
  map f :=
    { app := fun Y =>
        { toFun := fun g => f.unop ≫ g
          map_zero' := Limits.comp_zero
          map_add' := fun g g' => comp_add _ _ _ _ _ _ }
      naturality := fun Y Y' g =>
        AddCommGroupCat.ext fun x => Eq.symm <| Category.assoc _ _ _ }
  map_id _ := by ext; dsimp; simp
  map_comp f g := by ext; 

Proof state:
case w.h.w
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : Preadditive C
X✝ Y✝ Z✝ : Cᵒᵖ
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
x✝¹ : C
x✝ :
  ↑(({ obj := fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat,
              map := fun {X Y} f =>
                NatTrans.mk fun Y_1 =>
                  { toZeroHom := { toFun := fun g => f.unop ≫ g, map_zero' := (_ : f.unop ≫ 0 = 0) },
                    map_add' :=
                      (_ :
                        ∀
                          (g g' :
                            ↑(((fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat) X).obj
                                Y_1)),
                          f.unop ≫ (g + g') = f.unop ≫ g + f.unop ≫ g') } }.obj
          X✝).obj
      x✝¹)
⊢ (({ obj := fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat,
                map := fun {X Y} f =>
                  NatTrans.mk fun Y_1 =>
                    { toZeroHom := { toFun := fun g => f.unop ≫ g, map_zero' := (_ : f.unop ≫ 0 = 0) },
                      map_add' :=
                        (_ :
                          ∀
                            (g g' :
                              ↑(((fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat)
                                      X).obj
                                  Y_1)),
                            f.unop ≫ (g + g') = f.unop ≫ g + f.unop ≫ g') } }.map
            (f ≫ g)).app
        x✝¹)
      x✝ =
    (({ obj := fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat,
                  map := fun {X Y} f =>
                    NatTrans.mk fun Y_1 =>
                      { toZeroHom := { toFun := fun g => f.unop ≫ g, map_zero' := (_ : f.unop ≫ 0 = 0) },
                        map_add' :=
                          (_ :
                            ∀
                              (g g' :
                                ↑(((fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat)
                                        X).obj
                                    Y_1)),
                              f.unop ≫ (g + g') = f.unop ≫ g + f.unop ≫ g') } }.map
              f ≫
            { obj := fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat,
                  map := fun {X Y} f =>
                    NatTrans.mk fun Y_1 =>
                      { toZeroHom := { toFun := fun g => f.unop ≫ g, map_zero' := (_ : f.unop ≫ 0 = 0) },
                        map_add' :=
                          (_ :
                            ∀
                              (g g' :
                                ↑(((fun X => preadditiveCoyonedaObj X ⋙ forget₂ (ModuleCat (End X)) AddCommGroupCat)
                                        X).obj
                                    Y_1)),
                              f.unop ≫ (g + g') = f.unop ≫ g + f.unop ≫ g') } }.map
              g).app
        x✝¹)
      x✝

Next tactic:
dsimp