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

时间:2015-01-12 19:34:49

标签: haskell

我学习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)是一个表达式并且也返回值。

2 个答案:

答案 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