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
。