【问题标题】:Can't make a derived instance of MonadTrans for newtype无法为 newtype 创建 MonadTrans 的派生实例
【发布时间】:2019-08-25 22:57:23
【问题描述】:

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

【问题讨论】:

    标签: haskell monads monad-transformers


    【解决方案1】:

    问题是,如果这个推导有效,您的MonadTrans 实例将转换函子f,而您希望用作推导基础的ExceptT 实例是转换单子ReaderT TrxDbFileBasedEnv f

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

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

    lift = TrxDbFileBased . lift . lift
    

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

    【讨论】:

    • 是的,你是对的。我刚刚按照您建议的方式定义了lift
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-16
    • 2014-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-07
    相关资源
    最近更新 更多