Why do I have to coerce this data type by fields, rather than all at once?
The problem lies in the roles of the arguments m
in your general Iso
type.
Consider:
data T a b where
K1 :: Int -> T () ()
K2 :: String -> T () (Identity ())
type (<->) = Iso T
You can't really expect to be able to convert T () ()
into T () (Identity ())
even if ()
and Identity ()
are coercible.
You would need something like (pseudo code):
type role m representational representational =>
(Iso m) representational representational
but this can not be done in current Haskell, I believe.
Not a direct answer, but I want to share this relevant trick: Whenever m
is a profunctor (I suspect it will usually be), you can use a Yoneda-esque transformation to make an equivalent type with representational arguments.
newtype ProYo m a b = Yo2 (forall x y. (x -> a) -> (b -> y) -> m x y)
ProYo m
is isomorphic to m
, except its argument roles are representational, by the following isomorphism:
toProYo :: (Profunctor m) => m a b -> ProYo m a b
toProYo m = ProYo (\f g -> dimap f g m)
fromProYo :: ProYo m a b -> m a b
fromProYo (ProYo p) = p id id
If we define your Iso
in terms of this
data Iso m a b = Iso { to :: ProYo m a b, from :: ProYo m b a }
coerceIso
passes without modification.