hGetContents
nie jest zbyt leniwy, po prostu musi być skomponowany z innymi rzeczami odpowiednio, aby uzyskać pożądany efekt. Być może sytuacja byłaby wyraźniejsza, gdyby zmieniono ją na exposeContentsToEvaluationAsNeededForTheRestOfTheAction
lub po prostu listen
.
withFile
otwiera plik, robi coś (lub nic, jak chcesz - dokładnie to, czego potrzebujesz w każdym przypadku) i zamyka plik.
To ledwie wystarczy, aby wydobyć wszystkie tajemnice „leniwe IO”, ale teraz pod uwagę tę różnicę w bracketing
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
grubsza mówiąc, bad
otwiera i zamyka plik przed robi nic; good
robi wszystko pomiędzy otwarciem i zamknięciem pliku. Twoja pierwsza czynność była podobna do bad
.withFile
powinien regulować wszystkie czynności, które chcesz wykonać, a to zależy od uchwytu.
Nie musisz egzekwować przestrzegania rygorów, jeśli pracujesz z String
, małymi plikami itp., Tylko pomysłem, jak działa ta kompozycja. Ponownie, w bad
wszystko co robię przed zamknięciem pliku to exposeContentsToEvaluationAsNeededForTheRestOfTheAction
. W good
komponuję exposeContentsToEvaluationAsNeededForTheRestOfTheAction
z resztą akcji, którą mam na myśli, a następnie zamykam plik.
Znajomy length
+ seq
trik wspomina Patrick lub length
+ evaluate
warto wiedzieć; Twoje drugie działanie z putStrLn txt
było wariantem. Ale reorganizacja jest lepsza, chyba że leniwy IO jest zły dla twojej sprawy.
$ 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
Jest rzeczą oczywistą, że ByteString i tekst są warte wiedząc o, ale reorganizacja z oceny w umyśle jest lepszy, ponieważ nawet z nimi leniwym warianty są często to, co trzeba, a oni następnie angażować chwytając te same wyróżnienia między formami kompozycji. Jeśli masz do czynienia z jedną z (ogromnych) klas przypadków, w których ten rodzaj IO jest nieodpowiedni, spójrz na enumerator
, conduit
i wsp., Wszystkie cudowne.
Czy możesz pokazać kod, w którym sam używasz 'hClose'? Wygląda na to, że zamykasz go przed wprowadzeniem danych wejściowych. –