Edward Kmett's experimental roles package offers various utilities for lifting coercions, some of which I've pasted at the end of this question. The key class in the package is
class Representational (t :: k1 -> k2) where
-- | An argument is representational if you can lift a coercion of the argument into one of the whole
rep :: Coercion a b -> Coercion (t a) (t b)
Given the type
newtype Fix p a = In {out :: p (Fix p a) a}
I'm hoping to write something like
instance Representational p => Representational (Fix p)
I believe the following should work, except for one missing piece. I'm also a bit concerned that bar
may be strict enough to throw everything into an infinite loop.
-- With {-# LANGUAGE PolyKinds, ScopedTypeVariables, etc.)
import Data.Type.Coercion
import Data.Coerce
import Data.Roles
instance Representational p => Representational (Fix p) where
rep :: forall a b . Coercion a b -> Coercion (Fix p a) (Fix p b)
rep x = sym blah . quux . baz . blah
where
bar :: Coercion (p (Fix p a)) (p (Fix p b))
bar = rep (rep x)
baz :: Coercion (p (Fix p a) a) (p (Fix p b) a)
baz = eta bar
quux :: Coercion (p (Fix p b) a) (p (Fix p b) b)
quux = undefined -- ?????????
blah :: forall x . Coercion (Fix p x) (p (Fix p x) x)
blah = Coercion
Bits and pieces of roles
eta :: forall (f :: x -> y) (g :: x -> y) (a :: x).
Coercion f g -> Coercion (f a) (g a)
instance Representational Coercion
instance Representational f => Representational (Compose f)
The problem, as stated, cannot be solved, because the fact that
p
isRepresentational
(or evenPhantom
) does not imply thatp (Fix p a)
is representational. Here's a counterexample:Clearly,
NF a
is never representational; we cannot possibly implement(regardless of the choice of
a
) becauseNF a x
is only inhabited whenx ~ ()
.Therefore, we need a more refined notion of representational bifunctor to make this idea sensible.
unsafeCoerce
is almost certainly necessary to implement it in any case, because digging through an infinite chain ofCoercion
s would take an infinite amount of time, andCoercion
s can't be matched lazily.One (possibly valid?) concept, which I just suggested on GitHub:
The point of forcing application of
birep
is to (hopefully) make sure that it actually terminates, and therefore its type can be trusted.