This question can be considered a follow-up of
Lift instance of class with a `MonadIO` type-variable to the transformed monad
Which provides an example of an application of where this would be used.
The idea is that a typeclass
class (Monad m) => Class m a where
method :: a -> m ()
exist, with base instances in different monads
instance Class M A where
method = undefined
instance Class (T M) B where
method = undefined
. What I want is a way to lift any instance to itself, or higher in the transformer stack, much like what liftIO
does for IO
. My initial Idea was to define a lifting instance
instance (MonadTrans t, Class m a) => Class (t m) a where
method = lift . method
This has the problem however of creating overlapping instances when more than one transformer is applied, as lift
is polymorphic, and can be replaced by, for example, lift . lift
.
It was suggested to instead use a similar default instance,
class (Monad m) => Class m a where
method :: a -> m ()
default method :: (m ~ t n, MonadTrans t, Class n a) => a -> m ()
method = lift . method
that can then be used to declare lifting instances
instance (MonadTrans t) => Class (t M) A
instance (MonadTrans t) => Class (t (T M)) B
. This works, but lifting instances need to be declared for each base instance, so I am curious; is there any other way to solve this without resorting to overlapping instances?
You write
That's not how those defaults are intended to be used. The instances should look like
and so on.