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