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