Consider constant functor \(\overline{\{0\}}\) moving all objects to a fixed singleton set and all morphisms to identity. We call representing object of \(\overline{\{0\}}\) terminal object of category \(\mathrm{C}\). By evaluating its universal property we have that it is the object \(T\) with exactly one morphism \(Y \to T\) for any \(Y\).
Assume \(\mathrm{C}\) has all binary products. Consider functor \(\operatorname{Hom}(\_ \times A, B)\). Its representing object \(B^A\) is called exponential object. Actually, we defined it via isomorphism \(\operatorname{Hom}(Y, B^A) \cong \operatorname{Hom}(Y \times A, B)\) which may look familiar.
By taking \(Y = B^A\) we obtain map \(eval : B^A \times A \to B\). We can draw the universal property:
Usually, terminal objects and products are defined in terms of limits. But limits are examples of representing objects and are not really important here. We will turn back to them later.
To prove the statement in the header we have to demonstrate the terminal object, exponentials, and products.
Since morphisms in \(Hask\) do not have to preserve any structure the only candidates for the terminal object are types with a single term. These are types isomorphic to ()
. They are all isomorphic (admit invertible bijections between each other), hence there is no need to check universality.
The evaluation map of the exponential object suggests an object satisfying universal property for types a, b
— it is a -> b
. Products are given by type with two natural projections — (a,b)
. Since universal objects are defined up to natural isomorphisms, product types data Cons = Cons B C
are also products, same for newtypes over arrow types, etc.
Definition of exponential object gives us a natural isomorphism (a,b) -> c
\(\cong\) a -> (b -> c)
for any types a, b, c
. This isomorphism is called currying. In Haskell it is common and is reflected in both implementation and notation — type a -> b -> c
is literally equivalent to a -> (b -> c)
(which is known as partial evaluation) and is isomorphic to (a,b) -> c
via inverse functions curry
and uncurry
.
There is an important proposition that any cartesian closed category is a closed symmetric monoidal category with the tensor product given by the product in the sense of given universal property.
Closedness (informally) means that hom-sets can be considered objects of the category. Again informally we can see it from the existence of exponentials — their elements are functions from a
to b
, and elements of \(Hom\)-sets are morphisms from a
to b
. The term closed monoidal category
encapsulates some compatibility conditions between the tensor product and exponentials. Rigorous definitions and proof of the claimed proposition are worth a separate post to be written.
So let’s proceed to the definition of the symmetric monoidal category.
Category \(\mathrm{C}\) is called monoidal if
Associativity law is the commutativity of the following diagram:
Identity law is the commutativity of the following diagram:
Monoidal category is called symmetric if \(\forall A,B\) there exists isomorphism \(s_{AB} : A \otimes B \to B \otimes A\) such that the following diagram commutes:
From cartesian closedness there automatically follows that \(Hask\) is symmetric monoidal with \(\otimes =\)(,)
and unit — ()
. Associator and unitor functors are obvious. Denote this structure as \((Hask, (,), ())\).
Consider \(End(Hask)\). It is a monoidal category with tensor product given by composition, unit — by Identity
functor, and with identical unitors and associator. Denote this structure as \((End(Hask),\circ,Id)\).
These two statements will be central in the next several posts.
Universal properties allow us to construct dual objects by formally reversing all arrows.
While doing it with already defined representing objects we obtain the following definitions:
Category with an initial object, all coexponentials, and all binary coproducts is called cocartesian coclosed.
\(Hask\) does not have all coexponentials and is not cocartesian coclosed.
But it has all coproducts given by Either a b
and it has the initial object Void
. This data is enough to equip \(Hask\) with another structure of symmetric (since coproducts are symmetric) monoidal category. The tensor product is given by the coproduct and the unit is given by the initial object. Denote this structure as \((Hask, Either, Void)\).
These universal objects with reversed arrows can be defined via duals to representable functors.
Functor \(F : \mathrm{C} \to Set\) is said to be corepresentable if is it naturally isomorphic to \(\operatorname{Hom}(A,\_)\) for some \(A\). \(A\) is called corepresenting object of functor \(F\). Representing object is unique up to canonical isomorphism.
Initial object corepresents \(\operatorname{Hom}(\emptyset, \_)\), \(A \coprod B\) corepresents \(\operatorname{Hom}(A,\_) \otimes \operatorname{Hom}(B,\_)\), coexponential corepresents \(\operatorname{Hom}(B, \_ \coprod A)\).
In \(Hask\) \(\operatorname{Hom}(A,\_)\) is written as (->) A
. We can restrict functors to instances of the Functor
type class and obtain the following definition of representable functor: functor F
is said to be representable if there exists a natural isomorphism between F a
and (->) A a
for every type a
. A
is the corepresenting object of F
. But F
must be considered as a functor to Set with values — sets of terms of resulting types of Functor
we started with instead of types.
This definition is captured in package representable-functors. Isomorphism Cons a ~ A -> a
in terms of this library is written as f a ~ Key a -> a
, to the left it is given by tabulate
, to the right by index
.