The codensity monad on a type constructor f
is defined by:
newtype C f a = C { unC ∷ forall r. (a → f r) → f r }
It is well known that C f
is a monad for any type constructor f
(not necessarily covariant). The codensity monad is useful in several ways but it is a complicated type that contains a higher-order function under a universal type quantifier.
My question is, for what f
can one show that C f
is equivalent to a simpler monad that is defined without type quantifiers?
Some examples where a simplification is possible:
f a = a
(the identity functor), for whichC f a = a
.f a = r -> a
(the Reader monad), for whichC f
is the State monad (C f a = r -> (a, r)
).f a = (w, a)
(the Writer monad), for whichC f a = ((a -> w) -> a, (a -> w) -> w)
f a = a -> s
(a contravariant functor) and thenC f a = (a -> s) -> s
(the continuation monad).f a = a -> a
(neither covariant nor contravariant) and theC f a = List a
In the first four of those cases, the type equivalence can be derived from the Yoneda identity: forall r. (a -> r) -> F r = F a
when F
is a covariant functor. The last case is derived via the Church encoding of the inductive type List
.
I looked at some other examples and found that in most cases C f
does not seem to be equivalent to anything simpler.
Even if we just take f a = Maybe a
the resulting type does not seem to be equivalent to a simpler type expression:
newtype CMaybe a = CMaybe { unCMaybe ∷ forall r. (a → Maybe r) → Maybe r }
The Yoneda identity cannot be used here. My best guess (I have no proof so far) is that CMaybe a = (a -> Bool) -> Bool
with some additional laws imposed on the functions of that type. Imposing equations on values can be adequately expressed only within a dependently-typed language.
Can one simplify the codensity monad on Maybe
?
Are there other examples of type constructors f
where C f
can be simplified to a type without type quantifiers?
CMaybe
is a bit more complicated. A value forCMaybe Int
could takef
as input, computemap f [0..99]
, and observe which results areNothing
orJust x_i
. Depending on these "100 bits" of information, we can either returnNothing
orJust x_j
which is more or less like returning a number in[-1..99]
(Nothing
or the indexj
). So... maybe it's closer to(a -> Bool) -> Nat
? I can't really say. – AdriannaadrianneMaybe
is a sum typeMaybe a = 1 + a
, and sum types are defined by mapping out. Instead here you have a mapping into a sum. You essentially have(1+r)^(1+r)^a
which, even as a high-school algebraic expression, doesn't have a compact form. – Sawmill