无法为新类型创建 MonadTrans 的派生实例

Can't make a derived instance of MonadTrans for newtype

我有以下新类型声明。它正在包装一个 Monad 堆栈转换器,堆叠一些标准的 mtl monad,如 Reader 和 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)

我希望这个新类型成为 MonadTrans 的一个实例,但出现以下错误。

    • 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
   |         ^^^^^^^^^^

我不明白为什么不能导出 MonadTrans,因为基础类型是 ExceptT,它是 MonadTrans.

的一个实例

问题是,如果此推导有效,您的 MonadTrans 实例将转换函子 f,而 ExceptT 的实例将转换为您期望的用作推导的基础,正在转换 monad ReaderT TrxDbFileBasedEnv f

这些在表示上并不等同,所以GeneralizedNewtypeDeriving在这里帮不了你。

这是另一种思考方式:尝试手动实施 class。如果你尝试这样做,你会发现你的 lift 必须这样定义:

lift = TrxDbFileBased . lift . lift

也就是说,首先将 f 提升为 ReaderT,然后将 ReaderT 提升为 ExceptT,然后将所有这些包裹在 TrxDbFileBased 中。但是 GND 期望只做无操作包装,这意味着直接重用方法字典,因为类型在表示上是等价的。你的情况不是这样。