【问题标题】:Haskell: Why is this monad transformation wrong?Haskell:为什么这个单子转换是错误的?
【发布时间】:2016-03-08 09:55:32
【问题描述】:

我正在研究 monad 转换器,我阅读了 this SO 帖子,了解如何避免 lifts。

我的想法是MonadIO 是可以嵌入IO 的单子,而MonadWriter w 是可以嵌入WriterT w 的单子。所以我写了下面的代码(读取、累积和记录数字,直到我们得到零),其中使用显式 lift 的工作版本在 cmets 中。但 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

【问题讨论】:

  • GHC 怎么投诉? (总是添加错误信息)

标签: haskell functional-programming monads monad-transformers


【解决方案1】:

我的想法是 MonadIO 是可以嵌入 IO 的 monad,而 MonadWriter w 是可以嵌入 WriterT w 的 monad。

这并不完全正确。 MonadIOs 可以使用liftIOMonadWriter 可以使用tell。因此,如果您想在相同的上下文/monad 中使用liftIOtellasklocal 而无需提升,那么您使用的 single 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 操作永远不会自动解除。

顺便说一句,您使用 runWriterTrunReaderT 已经消除了转换器堆栈的所有歧义,因为这将使用

ReaderT Int (WriterT [String] IO Int)

【讨论】:

  • lifted-base (IIRC) 用于提升 IO 操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 2017-09-14
  • 1970-01-01
  • 1970-01-01
  • 2012-12-01
相关资源
最近更新 更多