haskell中的运算符<-表现如何?

时间:2018-09-04 06:32:23

标签: haskell do-notation

我理解(某种程度上)单子,并且理解运算符<-将从单子中提取值。

但是它如何与不同类型一起使用?

通常,我已经看到它被用来从IO monad中提取字符串。但是在下面的示例代码中,无法看到为什么它在主第三行中失败,抱怨说它期望使用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

3 个答案:

答案 0 :(得分:6)

do块中的每个语句都必须具有相同的单子类型。

multWithLog = do
  a <- logNumber 3
  return a

我们有logNumber 3 :: Writer [String] Intreturn a :: (Monad m) => m Int(是多态的),因此整个类型的类型检查都为Writer [String] Int(带有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块只是调用>>=>>的语法糖。例如。您的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始终保持不变。

答案 1 :(得分:3)

注意诸如此类的措辞

  

从monad中提取值

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

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

使用do表示法时,提取的所有值必须属于相同的Monad。在OP中,由于Monad返回IO的值,因此编译器推断所讨论的putStrLnIO ()

另一方面,

logNumber返回Writer [String] Int值,尽管这也是一个Monad实例,但它与IO不同。因此,该代码不会键入check。

答案 2 :(得分:1)

保持简单。这是两个事实

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

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

  1. 由于IO的类型为putStrLn ...,我在IO () monad中工作
  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的答案以获取完整说明

希望有帮助。