Non-standalone deriving clause does not pick up superclass quantified coercible constraint
Summary
I have a case where a standalone deriving via typechecks but the non-standalone equivalent does not due to unknown roles, despite the derived class constraining the roles appropriately:
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE QuantifiedConstraints #-}
module Test where
import Control.Exception
import Data.Coerce
import Control.Monad.Trans.Reader
class (Monad m, forall x y. Coercible x y => Coercible (m x) (m y)) => MonadWith m where
stateThreadingGeneralWith
:: GeneralAllocate m SomeException releaseReturn b a
-> (a -> m b)
-> m (b, releaseReturn)
{-
newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad) deriving MonadWith via ReaderT Int m
Test.hs:46:99: error:
• Couldn't match representation of type: m (GeneralAllocated
(ReaderT Int m) SomeException releaseReturn b a)
with that of: m (GeneralAllocated
(FooT m) SomeException releaseReturn b a)
arising from the coercion of the method ‘stateThreadingGeneralWith’
from type ‘forall releaseReturn b a.
GeneralAllocate (ReaderT Int m) SomeException releaseReturn b a
-> (a -> ReaderT Int m b) -> ReaderT Int m (b, releaseReturn)’
to type ‘forall releaseReturn b a.
GeneralAllocate (FooT m) SomeException releaseReturn b a
-> (a -> FooT m b) -> FooT m (b, releaseReturn)’
NB: We cannot know what roles the parameters to ‘m’ have;
we must assume that the role is nominal
• When deriving the instance for (MonadWith (FooT m))
|
46 | newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad) deriving MonadWith via ReaderT Int m
| ^^^^^^^^^
Same issue with:
newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad, MonadWith)
-}
newtype FooT m a = FooT (ReaderT Int m a) deriving newtype (Functor, Applicative, Monad)
deriving via ReaderT Int m instance (MonadWith m) => MonadWith (FooT m)
newtype GeneralAllocate m e releaseReturn releaseArg a
= GeneralAllocate ((forall x. m x -> m x) -> m (GeneralAllocated m e releaseReturn releaseArg a))
data GeneralAllocated m e releaseReturn releaseArg a = GeneralAllocated
{ allocatedResource :: !a
, releaseAllocated :: !(Either e releaseArg -> m releaseReturn)
}
instance (MonadWith m) => MonadWith (ReaderT r m) where
stateThreadingGeneralWith
:: forall a b releaseReturn
. GeneralAllocate (ReaderT r m) SomeException releaseReturn b a
-> (a -> ReaderT r m b)
-> ReaderT r m (b, releaseReturn)
stateThreadingGeneralWith (GeneralAllocate allocA) go = ReaderT $ \r -> do
let
allocA' :: (forall x. m x -> m x) -> m (GeneralAllocated m SomeException releaseReturn b a)
allocA' restore = do
let
restore' :: forall x. ReaderT r m x -> ReaderT r m x
restore' mx = ReaderT $ restore . runReaderT mx
GeneralAllocated a releaseA <- runReaderT (allocA restore') r
let
releaseA' relTy = runReaderT (releaseA relTy) r
pure $ GeneralAllocated a releaseA'
stateThreadingGeneralWith (GeneralAllocate allocA') (flip runReaderT r . go)
Expected behavior
Both of these should behave the same, and should both work. I think this should also work with deriving newtype
instead of having to specify the via
type.
Environment
- GHC version used: 9.2.4, 9.8.1