universe variables u v

structure Category :=
  (Obj : Type u)
  (Hom : Obj → Obj → Type v)

universe variables u1 v1 u2 v2

structure Functor (C : Category.{ u1 v1 }) (D : Category.{ u2 v2 }) :=
  (onObjects   : C^.Obj → D^.Obj)

@[reducible] definition ProductCategory (C : Category) (D : Category) :
  Category :=
  {
    Obj      := C^.Obj × D^.Obj,
    Hom      := (λ X Y : C^.Obj × D^.Obj, C^.Hom (X^.fst) (Y^.fst) × D^.Hom (X^.snd) (Y^.snd))
  }

namespace ProductCategory
  notation C `×` D := ProductCategory C D
end ProductCategory

@[reducible] definition TensorProduct ( C: Category ) := Functor ( C × C ) C

structure MonoidalCategory
  extends carrier : Category :=
  (tensor : TensorProduct carrier)

instance MonoidalCategory_coercion : has_coe MonoidalCategory Category :=
  ⟨MonoidalCategory.carrier⟩

-- Convenience methods which take two arguments, rather than a pair. (This seems to often help the elaborator avoid getting stuck on `prod.mk`.)
@[reducible] definition MonoidalCategory.tensorObjects { C : MonoidalCategory } ( X Y : C^.Obj ) : C^.Obj := C^.tensor^.onObjects (X, Y)

definition tensor_on_left { C: MonoidalCategory.{u v} } ( Z: C^.Obj ) : Functor.{u v u v} C C :=
{
  onObjects := λ X, C^.tensorObjects Z X
}
