2016-10-01 42 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

相關問題