2016-10-01 69 views
0

我感到困惑的是,编译器不会抱怨下面的代码(代码编译):为什么显然任何monad栈通常会派生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 m的条件下的MonadIO实例是

这只是我,还是那种反直觉?

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

回答

1

随着GeneralizedNewtypeDeriving,所有实例都具有相同的约束 - 对newtype基类型必须是相同的类的一个实例:

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" 
+0

我是知道的。但为什么这样做太笨拙了?例如。当我导出'MonadError e'时,我不必在稍后添加任何约束。 –

+0

''MonadError e''不需要'm'的任何约束,因为使用'ExceptT'的实现,它适用于所有'm'。 'MonadIO'不能在没有'm'约束的情况下实现,因为它必须使用'm'中的实现,从而确保这种实现存在。它不是尴尬的,或者最不重要的就是它应该是尴尬的。 –

+0

添加了文档链接并希望有更好的解释。 – Koterpillar

相关问题