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?

1

There are 1 answers

0
dfeuer On BEST ANSWER

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.