-- Haskell98! -- Emulating typical getContents code without any unsafe operations module GetContentsLess where import IterateeM import Control.Monad.Trans import Prelude hiding (head, take) -- ------------------------------------------------------------------------ -- A sample program that uses getContents -- -- It is borrowed from -- http://joshkos.blogspot.com/2008/07/reprise.html -- and slightly embellished. -- It reads a pair of integers from the standard input, evaluates -- a pure function on these two arguments, prints the result, -- and awaits more input. -- The function to evaluate over two arguments: max_cycle_len cycleLengthTable = map cycleLength [1..] cycleLength 1 = 1 cycleLength n | even n = succ $ cycle_length (n `div` 2) | otherwise = succ $ cycle_length (3*n + 1) cycle_length n = cycleLengthTable !! (n-1) -- index starts with 0 max_cycle_len i j = maximum . map cycle_length $ [min i j .. max i j] -- The driver that invokes the function and prints the results. -- The driver receives the lazy list of integers, the result of -- parsing the lazy stream produced by getContents driver1 (i:j:rest) = print (max_cycle_len i j) >> driver1 rest driver1 _ = return () main1 = getContents >>= driver1 . map read . words {- *GetContentsLess> main1 1 2 2 3 4 5 8 6 9 7 8 9 10 11 17 20 12 15 -} -- ------------------------------------------------------------------------ -- The same program but implemented safely, using IterateeM -- We use the same processing function max_cycle_len. -- We merely replace the driver and the main function. -- We show two versions of driver1. Like driver1, both new versions read two -- integers from the stream of integers, invoke max_cycle_len, print -- the result. -- The version below is explicit, making its correspondence -- to driver1 obvious driver2 = do i <- head j <- head lift (print (max_cycle_len i j)) driver2 -- A different, more sugared version, converting a stream of integers -- into a stream of pairs of integers driver3 = take 2 stream2list >>= runI >>= check where check [i,j] = lift (print (max_cycle_len i j)) >> driver3 check _ = return () -- Two tests of the drivers, using the enumerators receiving their input -- from strings. -- The inferred type of td2 is -- td2 :: IO (Iteratee String IO (Iteratee Int IO a)) -- which shows clearly the nesting of streams. td2 = run =<< enum_pure_1chunk "1 2 3 4 5 6 7 8 9 10 11 12" (enum_words . map_stream read $ driver2) -- reading the odd number of characters before the EOF td2' = run =<< enum_pure_1chunk "1 2 3 4 5 6 7 8 9 10 11" (enum_words . map_stream read $ driver2) td3 = run =<< enum_pure_1chunk "1 2 3 4 5 6 7 8 9 10 11 12" (enum_words . map_stream read $ driver3) -- The main function: convert a stream of characters to the stream -- of integers. It is instructive to compare with main1. Just like the -- latter, we effect the conversion by applying a sequence of transformers. -- In main1, the stream transformers were composed via (.). The compose -- as functions still, but we build the transformers inside out rather -- than outside in. -- In both cases, the composition takes one line: -- enum_words . map_stream read $ driver2 -- versus -- driver1 . map read . words -- Incidentally, the code for `enum_words' is quite similar to the code -- for `words', see IterateeM.hs -- The input can come for the terminal main2 = run =<< enum_file "/dev/tty" (enum_words . map_stream read $ driver2) -- Or from a string _and_ the terminal main3 = run =<< (enum_pure_1chunk "1 2 3 " >>> enum_file "/dev/tty") (enum_words . map_stream read $ driver2)