我有以下新类型声明。它包装了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
。
答案 0 :(得分:6)
问题在于,如果此推导有效,您的MonadTrans
实例将转换函子f
,而您希望使用的ExceptT
实例作为派生的基础,正在转换单子ReaderT TrxDbFileBasedEnv f
。
这些在表示上并不等效,因此GeneralizedNewtypeDeriving
在这里无济于事。
这是另一种思考方式:尝试手动实现该类。如果您尝试这样做,您将看到必须像这样定义lift
:
lift = TrxDbFileBased . lift . lift
,即首先将f
放入ReaderT
,然后将ReaderT
放入ExceptT
,然后将所有内容包装在TrxDbFileBased
中。但是GND希望不进行无操作包装,这意味着直接重复使用方法字典,因为类型在表示上是等效的。情况并非如此。