haskell 中的运算符 <- 的行为如何?

How does the operator <- in haskell behave?

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

但是它如何处理不同的类型?

通常,我看到它被用来从 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

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

multWithLog = do
  a <- logNumber 3
  return a

我们有 logNumber 3 :: Writer [String] Intreturn a :: (Monad m) => m Int(它是多态的),所以整个东西类型检查为 Writer [String] Intm = Writer [String],这是一个 monad)。

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

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

不同

根本原因是 do 块只是调用 >>=>> 的语法糖。例如。你的 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 始终保持不变。

注意

等措辞

extract the value from the monad

Monad 不包含 'a' 值。例如,Maybe 包含零个或一个值。列表 ([]) 包含多个值。有关详细信息,请参阅

例如,在列表情况下,<- 运算符一次提取 每个 个列表值。

当您使用 do 表示法时,提取的所有值必须属于同一个 Monad。在 OP 中,编译器推断有问题的 MonadIO,因为 putStrLn returns IO () 值。

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

让事情变得简单。这是两个事实

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

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

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

这就是 IO Int 的来源。我只是想在这里成为一名教师。查看@melpomene 的回答以获得完整的解释

希望对您有所帮助。