Ответ 1
hGetContents
не слишком ленив, он просто должен быть скомбинирован с другими вещами, чтобы получить желаемый эффект. Может быть, ситуация была бы яснее, если бы она была переименована exposeContentsToEvaluationAsNeededForTheRestOfTheAction
или просто listen
.
withFile
открывает файл, делает что-то (или ничего, как вам угодно - именно то, что вам нужно от него в любом случае), и закрывает файл.
Едва ли будет достаточно выявить все тайны "ленивого ввода-вывода", но теперь рассмотрим эту разницу в брекетинге
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
стоит знать; второе действие с 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, которые вам нужны, и затем они привлекают к себе одинаковые различия между формами композиции, Если вы имеете дело с одним из (огромных) классов случаев, когда этот тип ввода-вывода неуместен, взгляните на enumerator
, conduit
и co., Все замечательно.