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
大雑把に言えばbad、ファイルが何かをする前に、ファイルを開いたり閉じたりします。goodファイルを開いてから閉じるまでのすべてを実行します。あなたの最初の行動はに似ていましたbad。withFileハンドルに依存する、実行したいすべてのアクションを管理する必要があります。
小さなファイルなどで作業している場合は、厳密さの強制者は必要ありませんString。構成がどのように機能するかを理解するだけです。繰り返しbadますが、ファイルを閉じる前に「行う」のはすべてですexposeContentsToEvaluationAsNeededForTheRestOfTheAction。で、私が念頭に置いている残りのアクションを使用してgood作成exposeContentsToEvaluationAsNeededForTheRestOfTheActionし、ファイルを閉じます。
パトリックが言及したおなじみのlength+seqトリック、またはlength+evaluateは知っておく価値があります。での2番目のアクション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
言うまでもなく、ByteStringとTextは知っておく価値がありますが、評価を念頭に置いた再編成の方が優れています。なぜなら、それらを使用してもLazyバリアントが必要になることが多く、構成の形式間の同じ区別を把握する必要があるからです。この種のIOが不適切な(巨大な)クラスのケースの1つを扱っている場合は、すべて素晴らしいを見てenumeratorくださいconduit。