函数中不一致的 do 符号

Ger*_*wer 8 monads haskell type-mismatch do-notation

为什么允许使用此功能:

-- function 1
myfunc :: String
myfunc = do
  x <- (return True)
  show x
Run Code Online (Sandbox Code Playgroud)

这不是:

-- function 2
myfunc :: String
myfunc = do
  x <- getLine
  show x
Run Code Online (Sandbox Code Playgroud)

编译错误:

Couldn't match type `[]' with `IO'
Expected type: IO Char
Actual type: String
Run Code Online (Sandbox Code Playgroud)

我明白为什么功能 2 不应该工作,但为什么功能 1 工作呢?

为什么这会起作用:

-- function 3
myfunc = do
  x <- getLine
  return (show x)
Run Code Online (Sandbox Code Playgroud)

我知道它IO String然后返回,但是为什么函数 1 也没有被迫这样做?

sri*_*sri 12

function1 中do块 inmyfunc正在列表 monad 中工作,因为String实际上只是[Char]. 在那里,return True只需创建[True]. 当你这样做时x <- return True,“提取”True出来[True]并将其绑定到x. 下一行show x转换True为 String "True"。这是编译器值期望看到的返回值,最终工作正常。

同时,在function2 中domyfunc 中的块也在列表 monad 上工作(出于同样的原因,String真的是[Char]),但调用getLine仅在IOmonad 中可用。所以不出所料,这失败了。

-- 编辑 1

OP增加了一个功能3

-- function 3
myfunc :: String
myfunc = do
  x <- getLine
  return (show x)
Run Code Online (Sandbox Code Playgroud)

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

-- 编辑 2

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

-- function 3
myfunc = do
  x <- getLine
  return (show x)
Run Code Online (Sandbox Code Playgroud)

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

注意 - 我以尽可能随意的语气写了这个答案,而不会出错,目的是让它平易近人。


dfe*_*uer 9

do块在任意Monad. 的Monad,在这种情况下,是[]Monad列表的实例基于列表理解:

instance Monad [] where
  return x = [x]
  xs >>= f = [y | x <- xs, y <- f x]
Run Code Online (Sandbox Code Playgroud)

您可以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]
Run Code Online (Sandbox Code Playgroud)

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

myfunc = [y | y <- show True]
Run Code Online (Sandbox Code Playgroud)

当然,“所有的清单y,以便yshow True”就是show True