3

我正在學習monad變形金剛,我讀了this SO貼子,關於如何避免lift s。Haskell:爲什麼這個monad轉換是錯誤的?

我的想法是,MonadIO的單子,其中IO可以嵌入,並MonadWriter w的單子,其中WriterT w可以嵌入。所以我編寫了下面的代碼(讀取,累積和記錄數字,直到我們得到一個零),其中一個使用明確的lift的工作版本在註釋中。但GHC抱怨。我究竟做錯了什麼?

{-# LANGUAGE FlexibleContexts #-} 
import Control.Monad.IO.Class 
import Control.Monad.Writer.Class (MonadWriter) 
import Control.Monad.Trans.Reader 
import Control.Monad.Trans.Writer 

-- f :: ReaderT Int (WriterT [String] IO) Int 
-- m1 = ReaderT, m2 = WriterT 
f :: (MonadWriter [String] m1, MonadIO m2) => m1 (m2 (IO Int)) 
f = do 
    s <- liftIO getLine 
    tell ["Input: " ++ s] -- lift $ tell ["Input: " ++ s] 
    let i = read s :: Int 
    if i == 0 
     then ask 
     else local (+i) f 

main = do 
    rst <- runWriterT $ runReaderT f 0 
    print rst 
+2

如何GHC抱怨嗎? (總是添加錯誤信息) – Zeta

回答

5

我的想法是,MonadIO是單子在IO可以嵌入,並MonadWriter w爲單子其中WriterT W能夠被嵌入。

這不完全正確。 MonadIO s可以使用liftIO,而MonadWriter可以使用tell。因此,如果你想使用liftIOtellasklocal在相同的上下文/單子沒有解除,您使用單子一定是所有的人的一個實例:

f :: (MonadWriter [String] m -- monad supports tell :: [String] -> m() 
    , MonadReader Int  m -- monad supports ask ::    m Int 
    , MonadIO    m -- monad supports liftIO :: IO a  -> m a 
    )   => m Int  -- only a single m 

請注意,你不能使用transformer,但mtl獲得自動提升。因此,進口量也發生變化:

import Control.Monad.Reader (runReaderT, MonadReader) 
import Control.Monad.Writer (runWriterT, MonadWriter) 
import Control.Monad.IO.Class (liftIO, MonadIO) 

MonadIO進口不會改變,因爲IO行動永遠不會自動解除。

順便說一句,你的runWriterTrunReaderT使用已刪除所有含糊不清的變壓器組,因爲這將使用

ReaderT Int (WriterT [String] IO Int) 
+0

有'提升基地'(IIRC)提升'IO'行動。 – dfeuer