为什么显然任何 monad 堆栈通常都可以派生 MonadIO?

Why can apparently any monad stack generally derive MonadIO?

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

{-# 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 扩展有关?

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

的实例

Generalised derived instances for newtypes

All the instance does is apply and remove the newtype constructor.

的派生实例,即 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"