Search code examples
haskelltypeclassmonad-transformerstype-constraintslifting

Type constraints for polymorphic functions like lift


So I have this code

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import MonadA

data A = A

newtype MonadA a => MyStateT a b { runMyStateT :: StateT A a b }
    deriving (Functor, Applicative, Monad, MonadIO, MonadState A)

instance MonadTrans MyStateT where
    lift = MyStateT . lift

and I get the compiler complaining that it cannot prove the m from signature of lift is of type MonadA or thats how I read these cryptic error messages.

Could not deduce (MonadA m) arising from a use of `MyStateT'
from the context (Monad m)
  bound by the type signature for
             lift :: Monad m => m a -> MyStateT m a

is there a way to deal with this? I think I need the constraint to be able to instantiate as follows:

instance MonadA a => MonadA (MyStateT a) where
    f = MyStateT . lift . f

Also would such an implementation of f work? (I did not get that far due to the above error). I hope f on the right side to resolve to f over the inner monad a.


Edit: It indeed helped to drop the type constraint in newtype MonadA a => MyStateT ... to avoid the exact error that I mentioned. However there was just another error which I previously attributed to the same thing, consider this continuation of the example code above (some parts are repeated, now without type constraints):

class MonadB m where
    fB :: m ()

newtype MyStateT m a = MyStateT { runMyStateT :: StateT A m a}
    deriving (... MonadState A ...)

instance MonadTrans MyStateT where
    lift = MyStateT . lift

instance MonadA a => MonadA (MyStateT a) where
    f = lift . f

instance MonadA a => MonadB (MyStateT a) where
    fB = lift (modify (...))

the error is

Could not deduce (a ~ StateT A m0) from context (MonadA (MyStateT a), MonadA a)

in the implementation of fB. Earlier I tried class MonadA m => MonadB m to no avail. It doesn't even make sense to be matching a with StateT A m. Since MyStateT is an instance of MonadState A it should work, no?

Edit:

OK, got it working:

fB = MyStateT (modify ...)

stupid me.


Solution

  • The solution is dropping the constraint from the MyStateT definition:

    newtype MyStateT a b { runMyStateT :: StateT A a b }
        deriving (Functor, Applicative, Monad, MonadIO, MonadState A)
    

    Datatype constraints are essentially useless, as you need to put them in the function signatures anyway. For that reason, that feature is actually deprecated.

    I think I need the constraint to be able to instantiate as follows:

    Not really; instance MonadA a => MonadA (MyStateT a) will work just fine without it.

    Also would such an implementation of f work?

    It will. Note that, as you provided a MonadTrans instance for MyStateT, you don't actually need to wrap with MyStateT explicitly:

    instance MonadA a => MonadA (MyStateT a) where
        f = lift . f