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

时间:2019-08-25 22:57:23

标签: haskell monads monad-transformers

我有以下新类型声明。它包装了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

1 个答案:

答案 0 :(得分:6)

问题在于,如果此推导有效,您的MonadTrans实例将转换函子f,而您希望使用的ExceptT实例作为派生的基础,正在转换单子ReaderT TrxDbFileBasedEnv f

这些在表示上并不等效,因此GeneralizedNewtypeDeriving在这里无济于事。

这是另一种思考方式:尝试手动实现该类。如果您尝试这样做,您将看到必须像这样定义lift

lift = TrxDbFileBased . lift . lift

,即首先将f放入ReaderT,然后将ReaderT放入ExceptT,然后将所有内容包装在TrxDbFileBased中。但是GND希望不进行无操作包装,这意味着直接重复使用方法字典,因为类型在表示上是等效的。情况并非如此。