【问题标题】:Why can apparently any monad stack generally derive MonadIO?为什么显然任何 monad 堆栈通常都可以派生 MonadIO?
【发布时间】:2016-10-01 11:52:47
【问题描述】:

我很困惑编译器没有抱怨下面的代码(代码编译):

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Main where

import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Except   (ExceptT)

main = undefined

newtype Foo e m a = Foo { unFoo :: ExceptT e m a }
  deriving (Functor, Applicative, Monad, MonadIO)

如果我必须在某处添加 MonadIO m 作为约束,这对我来说会立即变得有意义,例如

deriving instance MonadIO m => MonadIO (Foo e m a)

确实,如果我尝试

deriving instance MonadIO (Foo e m a),

编译器抱怨。

我还注意到,当我在那里添加约束 MonadIO m 时,我只能使用 liftIO,稍后,无论我是否使用带有独立派生和约束的方法二,这又是有道理的。 MonadIO 实例MonadIO m 的条件下

只有我一个人,还是违反直觉?

是否与已弃用的 -XDatatypeContexts 扩展有关?

【问题讨论】:

    标签: haskell io monad-transformers


    【解决方案1】:

    对于GeneralizedNewtypeDeriving,所有实例都具有相同的约束——newtype 的基类型必须是同一类的实例:

    Generalised derived instances for newtypes

    实例所做的只是应用和删除 newtype 构造函数。

    Monad 的派生实例具有约束 Monad (ExceptT e m),该约束已存在。但是,MonadIO (ExceptT e m) 没有实例,因此它必须是对生成的 MonadIO 声明的约束。

    如果我尝试使用 MonadIO (Foo e m),则会产生错误:

    something :: Foo e m ()
    something = liftIO $ print "5"
    

    这是错误:

        • No instance for (MonadIO m) arising from a use of ‘liftIO’
          Possible fix:
            add (MonadIO m) to the context of
              the type signature for:
                something :: Foo e m ()
        • In the expression: liftIO $ print "5"
          In an equation for ‘something’: something = liftIO $ print "5"
    

    【讨论】:

    • 我知道这一点。但为什么这样做会如此尴尬?例如。当我导出MonadError e 时,我以后不必添加任何约束。
    • MonadError e 不需要对m 的任何约束,因为使用了来自ExceptT 的实现,它适用于所有mMonadIO 不能在没有m 约束的情况下实现,因为它必须使用来自m 的实现,从而确保存在这样的实现。这并不尴尬,或者至少它确实像它需要的那样尴尬。
    • 添加了文档链接,希望得到更好的解释。
    猜你喜欢
    • 1970-01-01
    • 2018-02-12
    • 2012-07-30
    • 2017-05-28
    • 1970-01-01
    相关资源
    最近更新 更多