Cannot 'coerce' data type with 'Reader' as a field
Let's investigate:
> :info Reader
type Reader r = ReaderT r Data.Functor.Identity.Identity :: * -> *
-- Defined in `Control.Monad.Trans.Reader'
So, Reader
is defined in terms of ReaderT
.
> :info ReaderT
type role ReaderT representational representational nominal
newtype ReaderT r (m :: k -> *) (a :: k)
= ReaderT {runReaderT :: r -> m a}
-- Defined in `Control.Monad.Trans.Reader'
... and ReaderT
is nominal
on its third argument, causing Reader
to be nominal
in its second argument, and making your coercion fail. You can't subvert this using a role annotation for your Flow
type, since that would cope with the previous role annotation of ReaderT
.
Now, you might wonder why ReaderT
has a nominal
third argument. To understand that, consider its definition:
newtype ReaderT r m a = ReaderT (r -> m a)
What should be the role of a
, above? Well, it depends. If m :: * -> *
is representational
on its argument, then ReaderT
is such on a
. The same holds for nominal
and phantom
. The "best" way to express the role here would be to use a role polymorphism like
type role forall r .
ReaderT representational (representational :: (* with role r) -> *) r
where the role of the third argument depends on the second higher-kinded argument.
Alas, GHC does not support role polymorphism like the above one, so we get to use only the most restrictive role: nominal
.
The issue, explained by @chi, is no longer an issue if you avoid using ReaderT and use a textbook (->) r
monad:
{-# LANGUAGE FlexibleContexts #-}
module Foo where
import Data.Coerce (Coercible, coerce)
newtype RT r o = RT { runR :: r -> o }
data Flow i o = Flow (i -> RT Int o) (o -> i)
coerceFlow
:: (Coercible i i', Coercible o o')
=> Flow i o
-> Flow i' o'
coerceFlow = coerce