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?
Class (T M) B
instance in the first place? Can't that case be covered by(MonadTrans t, Class m a) => Class (t m) a
? – Fyodor SoikinT M
to onlyB
, but disallow the use ofA
withT M
? – Fyodor Soikinmethod
forB
is inT M
, and is impossible to define in justM
. – notBob