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
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[....]
That's not how those defaults are intended to be used. The instances should look like
instance Class m a => Class (StateT s m) a
instance Class m a => Class (MaybeT m) a
and so on.