2

我了解(有点)单子并了解运算符 <- 将从单子中提取值。

但它如何与不同的类型一起工作?

通常,我看到它被用来从 IO monad 中提取字符串。但是在下面的示例代码中,我看不到为什么它在主第 3 行失败,抱怨它期待一种 IO int?编译器如何推断需要 IO int?

<-它 ( ) 在方法中还有什么作用multWithLog

import Control.Monad.Trans.Writer.Lazy

main = do
   putStrLn $ show $ logNumber 3 -- prints WriterT (Identity (3,["Got Number: 3"]))
   putStrLn $ show $ multWithLog -- prints WriterT (Identity (3,["Got Number: 3"]))
    _ <- logNumber 3 -- fails with Couldn't match type ‘WriterT [String] Data.Functor.Identity.Identity’ with ‘IO’
                    -- Expected type: IO Int
                    -- Actual type: Writer [String] Int
   putStrLn "test"


logNumber :: Int -> Writer [String] Int
logNumber x = writer (x, ["Got Number: " ++ show x])

multWithLog :: Writer [String] Int
multWithLog = do
  a <- logNumber 3
  --b <- logNumber 5
  return a
4

3 回答 3

6

块中的每个语句都do必须来自相同的一元类型。

multWithLog = do
  a <- logNumber 3
  return a

我们有logNumber 3 :: Writer [String] Intand return a :: (Monad m) => m Int(它是多态的),所以整个事情的类型检查为Writer [String] Int(with m = Writer [String],这是一个单子)。

main = do
   putStrLn $ show $ logNumber 3
   putStrLn $ show $ multWithLog
    _ <- logNumber 3
   putStrLn "test"

我们有putStrLn ... :: IO ()logNumber 3 :: Writer [String] Int。这是一个类型错误,因为Writer [String]IO.

根本原因是do块只是调用>>=and的语法糖>>。例如,你的main真正意思

main =
   (putStrLn $ show $ logNumber 3) >>
   (putStrLn $ show $ multWithLog) >>
   logNumber 3 >>= \_ ->
   putStrLn "test"

(>>)  :: (Monad m) => m a -> m b -> m b
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b

这要求类型m始终保持不变。

于 2018-09-04T06:45:25.200 回答
3

小心措辞,例如

从 monad 中提取值

monad 不包含“a”值。例如,Maybe包含零或一个值。List ( []) 包含多个值。有关更多详细信息,请参阅此答案

例如,在列表情况下,<-操作员一次提取一个列表值。

使用do符号时,提取的所有值必须属于相同的Monad. 在 OP 中,编译器推断有Monad问题的是IO, 因为putStrLn返回IO ()值。

logNumber另一方面,返回Writer [String] Int值,虽然这也是一个Monad实例,但它与IO. 因此,代码不进行类型检查。

于 2018-09-04T06:50:24.563 回答
1

保持简单。这是两个事实

  1. Writer [String]实际上是 monad,所以Writer [String] Int可以看成m Int
  2. 块中的每个动作都do应该发生在同一个 monad 中。

main函数中,编译器的推理如下:

  1. 我在IOmonad 中工作,因为putStrLn ...是类型IO ()
  2. 让我计算一下_ <- logNumber 3。既然我在单IO子里,logNumber 3应该是IO WhatEver
  3. logNumber 3实际上是类型的一元值m Int
  4. 等待!mWriter [String]单子,不是IO单子
  5. 打印一个错误,说明Writer [String] Int不正确,应该是IO Int

所以这就是IO Int从哪里来的。我只是想在这里教学。检查@melpomene 的答案以获得完整的解释

希望能帮助到你。

于 2018-09-04T08:53:28.350 回答