I have the following definitions for a monoidal category class (Similar to the standard library, but providing inverses of the necessary natural isomorphisms):
class (Category r, Category s, Category t) => Bifunctor p r s t | p r -> s t, p s -> r t, p t -> r s where
bimap :: r a b -> s c d -> t (p a c) (p b d)
--
class (Bifunctor b k k k) => Associative k b where
associate :: k (b (b x y) z) (b x (b y z))
associateInv :: k (b x (b y z)) (b (b x y) z)
--
class (Bifunctor b k k k) => HasIdentity k b i | k b -> i
class (Associative k b, HasIdentity k b i) => Monoidal k b i | k b -> i where
idl :: k (b i a) a
idr :: k (b a i) a
idlInv :: k a (b i a)
idrInv :: k a (b a i)
--
The problem with composing morphisms in a monoidal category using (.)
is that the objects may be associated differently. For instance
Monoidal Hask (,) ()
, we might want to compose a morphism of type x -> ((a, b), c)
with a morphism of type ((a, ()), (b, c)) -> y
. To make the types fit, the natural isomorphism given by bimap idrInv id . associate
has to be applied.
Does the Haskell type system enable an automatic way of determining an appropriate isomorphism, based on the desired domain and and codomain type? I can't figure out how to do it.
I figured it out, sort of. The basic idea is to use a multi-parameter type class with a normalizing function and its inverse as methods. The normalizer associates everything to the right, recursively. The type class needs an instance for each case of the recursion. Then, to convert from one way to associate stuff to another, just compose the normalizer for the type of the first morphism and the inverse normalizer for the type of the second morphism.
I will link code here as soon as I will have published it.