我学习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)
是一个表达式并且也返回值。
答案 0 :(得分:7)
问题是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表达式所做的事情(至少没有编译时魔术)。
答案 1 :(得分:2)
cnt <- hGetContents h
基本上是hGetContents h >>= \cnt ->
的语法糖。
不是一个表达式,它是用于do-block中自己的行的糖。
如果您仍希望将其保留在一行,则可以执行此操作,但稍后您将无法参考该文件的内容:
import System.IO
main = do
h <- openFile "text.txt" ReadMode
hGetContents h >>= mapM_ putStrLn . lines
hClose h