为什么我不能使用 (cnt <- hGetContents h) 表达式而不是 cnt?

Why I can't use the (cnt <- hGetContents h) expression instead of cnt?

我学Haskell。它工作正常:

import System.IO

main = do
  h <- openFile "text.txt" ReadMode
  cnt <- hGetContents h
  mapM_ putStrLn $ lines cnt
  hClose h

但这不起作用:

import System.IO

main = do
  h <- openFile "text.txt" ReadMode  
  mapM_ putStrLn $ lines (cnt <- hGetContents h)
  hClose h

为什么我的第二个变体不起作用?我希望这两个变体是相等的,因为 (cnt <- hGetContents h) 是一个表达式,而 returns 也是一个值。

问题是cnt <- hGetContents h不是一个表达式,它是do notation里面的一些特殊语法糖。这意味着它是编写以下正常 Haskell 代码的不同方式:

hGetContents h >>= \ cnt -> {- rest of do block -}

{- rest of the do block -} 之前的部分在这里不是一个完整的表达式,因为需要 do 块的其余部分来完成 lambda 的主体。

您可以手动将其脱糖以获得如下内容:

hGetContents h >>= \ cnt -> mapM_ putStrLn (lines cnt)

或免积分版

hGetContents h >>= mapM_ putStrLn . lines

您可以看出它是一个特殊的表达式,因为它引入了一个新的标识符 (cnt),您可以在表达式本身之外的其余代码中使用该标识符。这不是普通 Haskell 表达式可以做的事情(至少没有编译时魔术)。

cnt <- hGetContents h 本质上是 hGetContents h >>= \cnt ->.

的语法糖

不是一个表达式,它是用于 do 块中它自己的行的糖。

如果您仍想将它保留在一行中,您可以这样做,但您以后将无法引用该文件的内容:

import System.IO

main = do
  h <- openFile "text.txt" ReadMode  
  hGetContents h >>= mapM_ putStrLn . lines
  hClose h