函数中的 do 符号不一致

Inconsistent do notation in functions

为什么允许这个功能:

-- function 1
myfunc :: String
myfunc = do
  x <- (return True)
  show x

这不是:

-- function 2
myfunc :: String
myfunc = do
  x <- getLine
  show x

编译错误:

Couldn't match type `[]' with `IO'
Expected type: IO Char
Actual type: String

我明白为什么功能 2 不能工作,但为什么功能 1 可以工作?

以及为什么这样做有效:

-- function 3
myfunc = do
  x <- getLine
  return (show x)

我明白了 returns IO String 那么,为什么函数 1 也没有被强制执行此操作?

function1 中,myfunc 中的 do 块在列表 monad 中工作,因为 String 实际上只是 [Char].在那里,return True 只是创建了 [True]。当您执行 x <- return True 时,它会从 [True] 中“提取”True 并将其绑定到 x。下一行 show xTrue 转换为字符串 "True"。这是编译器值期望看到的 return 值,最终工作正常。

同时在 function2 中,myfunc 中的 do 块也在列表 monad 上工作(出于同样的原因,String 实际上是 [Char]) 但调用 getLine 仅在 IO monad 中可用。毫不奇怪,这失败了。

-- 编辑 1

OP 添加了一个 function3

-- function 3
myfunc :: String
myfunc = do
  x <- getLine
  return (show x)

不,这不应该出于同样的原因 function2 失败。

-- 编辑 2

OP 已更新 function3 以修复复制粘贴错误。

-- function 3
myfunc = do
  x <- getLine
  return (show x)

评论中提到了这一点,但为了清楚起见,这是可行的,因为当未指定类型信息时,GHC 会做出最好的推断,在看到 getLine 后,它认为它是 IO String确实提供 getLine.

注意 - 我用尽可能随意的语气写了这个答案,没有错,目的是让初学者可以轻松理解它。

do 块在任意 Monad 的上下文中工作。在这种情况下,Monad[]。列表的 Monad 实例基于列表理解:

instance Monad [] where
  return x = [x]
  xs >>= f = [y | x <- xs, y <- f x]

您可以这样对 do 符号进行脱糖处理:

myfunc :: String
myfunc = do
  x <- (return True)
  show x

-- ==>

myfunc = [y | x <- return True, y <- show x]

-- ==>

myfunc = [y | x <- [True], y <- show x]

在列表理解中,x <- [True] 实际上与 let x = True 相同,因为您只是从列表中绘制 一个 元素。所以

myfunc = [y | y <- show True]

当然,“yshow True 中的所有 y 的列表”只是 show True