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

时间:2016-10-01 11:52:47

标签: haskell io monad-transformers

我很困惑,编译器不会抱怨下面的代码(代码编译):

{-# 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),

编译器抱怨。

我还注意到,当我在那里添加约束liftIO时,我只能使用MonadIO m,无论我是否使用方法二和独立的派生和约束,这又是一种说得通。 MonadIO实例在<{1}} 的条件下

只是我,还是反直觉?

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

1 个答案:

答案 0 :(得分: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"