Haskell:为什么这个monad转换是错误的?
Haskell: Why is this monad transformation wrong?
我正在研究 monad 转换器,我阅读了 this SO post 如何避免 lift
s。
我的想法是 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
My thought was that MonadIO are monads in which IO can be embedded, and MonadWriter w are monads in which WriterT w can be embedded.
这不完全正确。 MonadIO
可以用liftIO
,MonadWriter
可以用tell
。因此,如果要在同一个context/monad中使用liftIO
、tell
、ask
和local
而不进行提升,则单 你使用的 monad 必须是所有这些的实例:
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
操作永远不会自动解除。
顺便说一下,您对 runWriterT
和 runReaderT
的使用已经消除了转换器堆栈的所有歧义,因为这将使用
ReaderT Int (WriterT [String] IO Int)
我正在研究 monad 转换器,我阅读了 this SO post 如何避免 lift
s。
我的想法是 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
My thought was that MonadIO are monads in which IO can be embedded, and MonadWriter w are monads in which WriterT w can be embedded.
这不完全正确。 MonadIO
可以用liftIO
,MonadWriter
可以用tell
。因此,如果要在同一个context/monad中使用liftIO
、tell
、ask
和local
而不进行提升,则单 你使用的 monad 必须是所有这些的实例:
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
操作永远不会自动解除。
顺便说一下,您对 runWriterT
和 runReaderT
的使用已经消除了转换器堆栈的所有歧义,因为这将使用
ReaderT Int (WriterT [String] IO Int)