为什么类型在下一行匹配,但在 `do` 块中不在同一行?

Why does the type match on the next line but not on the same line in `do` block?

我正在阅读用户必须键入的输入中的几行:

main :: IO ()
main = do
  let size = 3
  arr <- replicateM size getLine
  let pairs = map parsePair arr
  print pairs

为什么我可以在单独的行上执行 map parsePair arr 但不在同一条线上,像这样:

arr <- map parsePair (replicateM size getLine)

这样做,我得到错误:

• Couldn't match type ‘[]’ with ‘IO’
  Expected type: IO [Int]
    Actual type: [[Int]]

为了给你更多的细节,这里是parsePair:

parsePair string = map parseInt $ words string

parseInt :: String -> Int
parseInt s = read s :: Int

因为replicateM size getLine的类型是<b>IO</b>[String],所以不是[=11=的列表]s,它基本上是对将获得 [String]IO 操作的描述。您可以在 IO monad 中看到箭头 <- 作为检索它并解压缩结果的一种方式。

但是你可以做一些处理,因为 IO 也是一个 Functor,你可以使用 fmap :: Functor f => (a -> b) -> f a -> f b:

main :: IO [Int]
main = do
  let size = 3
  <b>fmap (map parsePair)</b> (replicateM size getLine)

或者您可以将 fmap 移动到 getLine 部分:

main :: IO [Int]
main = do
  let size = 3
  replicateM size (<b>fmap parsePair</b> getLine)

请注意,有一个 readLn :: Read a => IO a 函数基本上是 fmap read getLine(除了它执行一些额外的错误处理)。因此我们可以使用:

main :: IO [Int]
main = do
  let size = 3
  replicateM size <b>readLn</b>

请注意,带有 <- 符号的 do 语法确实不像赋值运算符(在许多语言中是 =,有时 := 有时确实<-,例如在 R) 中。相反,它是一个特殊的构造,它 执行一个单子动作 并提取此动作的 结果 。一个动作和它的结果之间有一个根本的区别;经常被引用的类比是,一个动作就像蛋糕的食谱,结果就是蛋糕本身。

直接将map parsePair应用到IO动作就像拿刀把菜谱切成碎片,然后期望你可以用那个菜谱烤一个现成的蛋糕。显然这不是它的工作原理。

同样,您首先需要执行 (bind) 一个 IO 操作,然后才能操作结果。这就是 arr <- replicateM size getLine 行发生的事情:执行操作并且仅将其结果存储在 arr 中,这样您就可以编写 map parsePair arr.

或者,您可以使用 fmap 运算符。它所做的基本上是,它需要一个配方和一些如何处理结果的指令,然后将该指令添加到配方的末尾。如果再执行那个菜谱,结果确实是切块的蛋糕。