我有以下代码片段,我传递给withFile:
text <- hGetContents hand
let code = parseCode text
return code
Run Code Online (Sandbox Code Playgroud)
这里hand是一个有效的文件句柄,打开ReadMode并且parseCode是我自己的函数,它读取输入并返回一个Maybe.实际上,函数失败并返回Nothing.如果,而是我写:
text <- hGetContents hand
putStrLn text
let code = parseCode text
return code
Run Code Online (Sandbox Code Playgroud)
我得到了一个Just,就像我应该的那样.
如果我openFile和hClose我自己,我有同样的问题.为什么会这样?我怎样才能干净利落地解决它?
谢谢
app*_*ive 12
hGetContents不是太懒,它只需要与其他东西合适地组合以获得理想的效果.如果它被重命名exposeContentsToEvaluationAsNeededForTheRestOfTheAction或仅仅是情况,情况会更清楚listen.
withFile 打开文件,执行某些操作(或者根本不做任何操作 - 确切地说,无论如何都需要它),并关闭文件.
揭开"懒惰IO"的所有神秘面纱是不够的,但现在考虑一下这种包围的区别
good file operation = withFile file ReadMode (hGetContents >=> operation >=> print)
bad file operation = (withFile file ReadMode hGetContents) >>= operation >>= print
-- *Main> good "lazyio.hs" (return . length)
-- 503
-- *Main> bad "lazyio.hs" (return . length)
-- 0
Run Code Online (Sandbox Code Playgroud)
bad在它做任何事之前,粗略地放置,打开和关闭文件; good在打开和关闭文件之间做所有事情.你的第一个动作类似于bad.withFile应该控制你想要完成的所有动作,这取决于句柄.
如果您正在使用String,小文件等,您不需要严格执行者,只需了解组合的工作原理.再次,在bad关闭文件之前我所做的一切都是exposeContentsToEvaluationAsNeededForTheRestOfTheAction.在good我构思exposeContentsToEvaluationAsNeededForTheRestOfTheAction我想到的其余动作时,然后关闭文件.
熟悉length+ seq帕特里克,或提及招length+ evaluate是值得了解的; 你的第二个动作putStrLn txt是变种.但重组更好,除非懒惰IO对你的情况是错误的.
$ time ./bad
bad: Prelude.last: empty list
-- no, lots of Chars there
real 0m0.087s
$ time ./good
'\n' -- right
()
real 0m15.977s
$ time ./seqing
Killed -- hopeless, attempting to represent the file contents
real 1m54.065s -- in memory as a linked list, before finding out the last char
Run Code Online (Sandbox Code Playgroud)
不言而喻,ByteString和Text值得了解,但是考虑到评估的重组更好,因为即使使用它们,Lazy变体通常也是你需要的,然后他们就会在构图形式之间理解相同的区别.如果你正在处理这种IO不适合的(巨大的)类别的情况之一,那么看看enumerator,conduit并且co.,一切都很棒.
hGetContents使用懒惰的IO; 它只会在您强制执行更多字符串时从文件中读取,并且只在评估它返回的整个字符串时才关闭文件句柄.问题是你把它封闭在里面withFile; 相反,只需使用openFile和hGetContents直接(或更简单地说readFile).完全评估字符串后,文件仍将关闭.像这样的东西应该做的,以确保文件完全读取和立即通过强制整个字符串关闭:
import Control.Exception (evaluate)
readCode :: FilePath -> IO Code
readCode fileName = do
text <- readFile fileName
evaluate (length text)
return (parseCode text)
Run Code Online (Sandbox Code Playgroud)
像这样的不直观的情况是人们现在倾向于避免懒惰IO的原因之一,但不幸的是你不能改变它的定义hGetContents.严格的包中hGetContents提供了严格的IO版本,但它可能不值得依赖于该功能的包.
如果你想避免在这里两次遍历字符串所产生的开销,那么你应该考虑使用一种比它更有效的类型String.该Text类型有严格的IO当量为多的String基于IO功能,一样ByteString(如果你处理的二进制数据,而不是Unicode文本).