3
votes

I have the following newtype declaration. It is wrapping a Monad stack transformer stacking some standard mtl monads like Reader and Except.

newtype TrxDbFileBased f a = TrxDbFileBased {
        unTrxDbFileBased :: ExceptT TrxDbError (ReaderT TrxDbFileBasedEnv f) a
    } deriving (
        Functor
    ,   Applicative
    ,   Monad
    ,   MonadError TrxDbError
    ,   MonadReader TrxDbFileBasedEnv
    ,   MonadIO
    ,   MonadTrans
    )

data TrxDbFileBasedEnv = TrxDbFileBasedEnv {
        workingDirectory :: FilePath    
    } deriving (Show)

data TrxDbError = TrxDbErrorIO TrxDbFileBasedEnv IOException
                | TrxDbErrorStr TrxDbFileBasedEnv String 
                deriving (Show)

I want this newtype to be an instance of MonadTrans but I am getting the following error.

    • Can't make a derived instance of ‘MonadTrans TrxDbFileBased’
        (even with cunning GeneralizedNewtypeDeriving):
        cannot eta-reduce the representation type enough
    • In the newtype declaration for ‘TrxDbFileBased’
   |        
31 |     ,   MonadTrans
   |         ^^^^^^^^^^

I don't understand why MonadTrans cannot be derived given that the underlying type is ExceptT which is an instance of MonadTrans.

1

1 Answers

9
votes

The problem is that, if this derivation worked, your instance of MonadTrans would be transforming the functor f, while the instance for ExceptT, which you're expecting to be used as base of derivation, is transforming monad ReaderT TrxDbFileBasedEnv f.

These are not representationally equivalent, so GeneralizedNewtypeDeriving can't help you here.

Here's another way to think about it: try implementing the class manually. If you try that, you'll see that your lift will have to be defined like this:

lift = TrxDbFileBased . lift . lift

that is, first lifting f into ReaderT, then lifting ReaderT into ExceptT, and then wrapping all of that in TrxDbFileBased. But GND expects to do nothing more than the no-op wrapping, which means just straight up reusing the method dictionary, since the types are representationally equivalent. This is not the case in your case.