[More lazy I/O John Goerzen **20070709051711] { hunk ./en/ch06-io.xml 642 - - One of these is the &hGetContents; function.There is - also a shortcut function &getContents; that operates on standard - input. &hGetContents; has the type - Handle -> IO String. The &String; it returns - represents the entire data in the file given by the - &Handle;.More precisely, it is the entire data from the - current position of the file pointer to the end of the - file. - - - In an imperative language, use of such a function is often a bad idea. - It may be fine to read the entire contents of a 2KB file, but if you - try to read the entire contents of a 500GB file, you are likely to - crash due to lack of RAM to store all that data. - - - But &hGetContents; is different. The &String; it returns is evaluated - lazily. At the moment you call &hGetContents;, nothing is actually - read. Data is only read from the &Handle; as the elements (characters) - of the list are processed. As elements of the &String; are no longer - used, Haskell automatically frees that memory. All of this happens - completely transparently to you. And since you have what looks like -- - and, really, is -- a pure &String;, you can pass it to pure (non-&IO;) - code. - - - Let's take a quick look at an example. Back in - , you saw an imperative program - that converted the entire content of a file to uppercase. Its - imperative algorithm was similar to what you'd see in many other - languages. Here now is the much simpler lazy algorithm: - - &toupper-lazy1.hs:all; + + hGetContents + + One novel way to approach I/O is the &hGetContents; function.There is + also a shortcut function &getContents; that operates on standard + input. &hGetContents; has the type + Handle -> IO String. The &String; it returns + represents the entire data in the file given by the + &Handle;.More precisely, it is the entire data from the + current position of the file pointer to the end of the + file. + + + In an imperative language, use of such a function is often a bad idea. + It may be fine to read the entire contents of a 2KB file, but if you + try to read the entire contents of a 500GB file, you are likely to + crash due to lack of RAM to store all that data. + + + But &hGetContents; is different. The &String; it returns is evaluated + lazily. At the moment you call &hGetContents;, nothing is actually + read. Data is only read from the &Handle; as the elements (characters) + of the list are processed. As elements of the &String; are no longer + used, Haskell automatically frees that memory. All of this happens + completely transparently to you. And since you have what looks like -- + and, really, is -- a pure &String;, you can pass it to pure (non-&IO;) + code. + + + Let's take a quick look at an example. Back in + , you saw an imperative program + that converted the entire content of a file to uppercase. Its + imperative algorithm was similar to what you'd see in many other + languages. Here now is the much simpler lazy algorithm: + + &toupper-lazy1.hs:all; + + Notice that &hGetContents; handled all of the + reading for us. Also, take a look at processData. + It's a pure function since it has no side-effects and always returns + the same result each time it is called. It has no need to know -- + and no way to tell -- that its input is being read lazily from a file + in this case. It can work perfectly well with a 20-character literal + or a 500GB data dump on disk. + + + You can even verify that with &ghci;: + + &toupper-lazy1.ghci:all; + + This program was a bit verbose to make it clear that there was pure + code in use. Here's a bit more concise version, which we will build + on in the next examples: + + &toupper-lazy2.hs:all; + + + readFile and writeFile + + Haskell programmers use &hGetContents; as a filter quite often. They + read from one file, do something to the data, and write the result + out elsewhere. This is so common that there are some shortcuts for + doing it. Two are &readFile; and &writeFile; addfile ./examples/ch06/readfile.ghci hunk ./examples/ch06/readfile.ghci 1 +--# all +:t readFile +:t writeFile addfile ./examples/ch06/toupper-lazy1.ghci hunk ./examples/ch06/toupper-lazy1.ghci 1 +--# all +:l toupper-lazy1.hs +processData "Hello, there! How are you?" +:t processData +:t processData "Hello!" hunk ./examples/ch06/toupper-lazy1.hs 11 - let result = map toUpper inpStr + let result = processData inpStr hunk ./examples/ch06/toupper-lazy1.hs 13 + hClose inh + hClose outh + +processData :: String -> String +processData = map toUpper addfile ./examples/ch06/toupper-lazy2.hs hunk ./examples/ch06/toupper-lazy2.hs 1 +{-- snippet all --} +-- ch06/toupper-lazy1.hs + +import System.IO +import Data.Char(toUpper) + +main = do + inh <- openFile "input.txt" ReadMode + outh <- openFile "output.txt" WriteMode + inpStr <- hGetContents inh + hPutStr outh (map toUpper inpstr) + hClose inh + hClose outh +{-- /snippet all --} + }