I have this record:
import Data.Functor.Identity
import Control.Monad.Trans.Identity
import Data.Coerce
data Env m = Env {
logger :: String -> m ()
}
env :: Env IO
env = undefined
and this coercion function
decorate
:: Coercible (r_ m) (r_ (IdentityT m))
=> r_ m -> r_ (IdentityT m)
decorate = coerce
which is applicable to the record value without problems:
decoratedEnv :: Env (IdentityT IO)
decoratedEnv = decorate env
However, if I define the only slightly more complex record
data Env' h m = Env' {
logger' :: h (String -> m ())
}
env' :: Env' Identity IO
env' = undefined
And try to insert an IdentityT wrapper like I did before
decoratedEnv' :: Env' Identity (IdentityT IO)
decoratedEnv' = decorate env'
I get the error:
* Couldn't match type `IO' with `IdentityT IO'
arising from a use of `decorate'
To my mind, the extra Identity parameter taken by Env' shouldn't stop coerce from working. Why does coerce fail in this case? Is there a way to make coerce work?
A coercion
Coercible A Bdoes not imply a coercionCoercion (h A) (h B)for allh.Consider this GADT:
Coercing
T AtoT Bamounts to coercingInttoBool, and that clearly should never happen.We could perform that coercion only if we knew that the parameter of
hhas the right role (e.g.representationalorphantom, but notnominal).Now, in your specific case
his known (Identity), and has surely the right role, so it should work. I guess the GHC coercion system is not so powerful to handle such a "well behaved"hwhile rejecting the ill-behaved ones, so it conservatively rejects everything.Adding a type hole seems to confirm this. I tried
and got the error
The "NB:" part is right on the spot.
I also tried to insist, and force the role
to no avail:
The best workaround I could find is to unwrap/rewrap, and exploit
QuantifiedConstraintsto essentially require thathhas a non-nominalrole:Not an ideal solution, since it goes against the spirit of
Coercible. In this case, the rewrapping has only O(1) cost, but if we had, say, a list ofEnv' Identity IOto convert, we would pay O(N).