I have been thinking about strictness recently, because it matters for implementation, and I think I have a “nicer” way of doing monoidal categories and symmetric monoidal categories.
In programming languages, we often have a notion of tuple type. For instance, in Julia we have Tuple{A,B,C...}
. Modeling this as a monoidal structure on the category of types in the traditional way, where we have a binary operation and a unit, and then associators and unitors, doesn’t actually fit what’s going on, because we have native n-ary operations.
Instead, we could model this as an algebra of the list 2-monad \mathrm{List} \colon \mathsf{Cat} \to \mathsf{Cat}. The list monad sends a category \mathsf{C} to the category \mathrm{List}(\mathsf{C}), where an object is a pair (n \in \mathbb{N}, \{X_i \in \mathsf{C}\}_{i \in \{1,\ldots,n\}}), and a morphism from (n, \{X_i\}) to (n, \{Y_i\}) is a choice of maps f_i \colon X_i \to Y_i (there are no morphisms (n,\{X_i\}) \to (m,\{Y_i\}) for n \neq m).
This is a monad essentially because we can collapse lists.
This models our Tuple
operation: we take a list of types, and we get a type.
However, this is not a strict algebra, because Tuple{A, Tuple{B,C}}
is not equal to Tuple{Tuple{A,B},C}
, even though flatten [[1,2],3] == flatten [1,[2,3]]
.
One thought I had was to instead model this as a strict algebra of \mathrm{List}^\ast, the free monad on the list 2-monad. \mathrm{List}^\ast sends a category \mathsf{C} to the category where an object is a tuple (t, \{ X_i \}_{i \in \mathrm{leaf}(t)}), where t is an arbitrary nesting of the list [1,\ldots,n] for some n. I.e., [1,[2,[3],[[4]]]] or something. This allows the 2-monad to “remember” the nesting, and thus we can have a strict algebra without forcing Tuple{A,Tuple{B,C}}
to be equal to Tuple{Tuple{A,B},C}
.
However, this doesn’t give us any associativity properties; any functor \mathrm{List}(\mathsf{C}) \to \mathsf{C} automatically gives us an algebra of \mathrm{List}^\ast.
So instead, we do something sneaky. We modify \mathrm{List}^\ast to say that the morphisms between (t,\{X_i\}) and (t',\{Y_i\}) are collections \{f_i \colon X_i \to Y_i\} even when t \neq t', as long as t and t' flatten to the same list. This means that there is a canonical morphism between any two nestings of the same list given by f_i = \mathrm{id}_{X_i}. With this modification, \mathrm{List}^\ast(\mathsf{C}) is equivalent to \mathrm{List}(\mathsf{C}), but not isomorphic. Then Tuple
forms a strict algebra of \mathrm{List}^\ast!
I think, but I’m not sure, that this is an instance of a general construction where pseudo-algebras of a 2-monad are equivalent to strict algebras of another 2-monad; I remember seeing something like this in A 2-categories companion but my 2-fu is not strong enough yet to be fully fluent with this stuff.
Anyways, I like this way of doing monoidal categories because I think it captures more faithfully how people actually use monoidal categories in practice, with unbiased operations like A \otimes B \otimes C.
I also think that we can do a similar construction for symmetric monoidal categories where we modify \mathsf{List}^\ast to say that the morphisms between (t,\{X_i\}) and (t',\{Y_i\}) are pairs of permutations \sigma \in S_n and morphisms \{f_i \colon X_i \to Y_{\sigma(i)}\}.
I also want to model more carefully the operation of NamedTuple
and the operation of struct creation, but that will have to wait for another day.