Haskell:为什么这段代码失败了?

时间:2012-08-17 20:42:53

标签: haskell file-io monads lazy-evaluation

当我尝试运行此代码时......

module Main where

import qualified Data.Text.Lazy.IO as LTIO
import qualified Data.Text.Lazy as LT
import System.IO (IOMode(..), withFile)

getFirstLine :: FilePath -> IO String
getFirstLine path =
        withFile path ReadMode (\f -> do
                contents <- LTIO.hGetContents f
                return ("-- "++(LT.unpack . head $ LT.lines contents)++" --"))

main::IO()
main = do
        firstLine <- getFirstLine "/tmp/foo.csv"
        print firstLine

我得到了

"-- *** Exception: Prelude.head: empty list

...我希望它能打印出第一行“/tmp/foo.csv”。你能解释一下原因吗?最后,我试图弄清楚如何从文件输入创建一个懒惰的文本列表。

2 个答案:

答案 0 :(得分:4)

正如Daniel Lyons在评论中提到的那样,这是由于IO和懒惰相互作用。

想象一下,如果你愿意的话:

  • withFile打开文件,文件句柄f
  • 返回使用f内容的Thunk。
  • withFile关闭文件。
  • 评估Thunk。已关闭的文件中没有内容。

HaskellWiki / Maintaining laziness页面上提到了此陷阱。

要修复此问题,您可以在withFile内阅读整个文件内容(可能强制使用seq),也可以懒洋洋地关闭文件,而不是使用withFile

答案 1 :(得分:1)

我认为是这样的:withFile在执行函数后关闭文件。 hGetContents懒惰地读取内容(懒惰的IO),当它需要读取内容时,文件将被关闭。

不要使用withFile,只需使用openFile,而不是关闭它。 hGetContents将在文件读取后将文件置于半封闭状态。或者更好,只需使用readFile

直接阅读内容