[Haskell-cafe] Lazy Lists and IO

Ronald Guida ronguida at mindspring.com
Wed Jul 11 00:58:34 EDT 2007


Hi Everyone,

A few weeks ago, I started learning Haskell (and functional
programming) on my own from the wealth if information on the internet.
I recently read the paper "Why Functional Programming Matters" [1] and
it led me to wonder how to input a lazy list.

Suppose I have a function "f" that reads a lazy list, such that "f"
only consumes as much of the list as it needs to.  Laziness allows me
to apply "f" to an infinite list without creating an infinite loop.

Now I want to connect the console to "f", such that the list of inputs
to "f" comes from the console, one item at a time.

To create a specific example, lets suppose I want to read and
accumulate the contents of a list, and I want to stop when the sum
reaches or exceeds a specified cutoff.

I can write this as a function that reads elements from an infinite list:

 > accumUntilCutoff :: Integer -> [Integer] -> (Integer, [Integer])
 > accumUntilCutoff cutoff xs = accumHelper cutoff 0 id xs
 >     where
 >       accumHelper :: {- cutoff          -} Integer ->
 >                      {- sum so far      -} Integer ->
 >                      {- elements so far -} ([Integer] -> [Integer]) ->
 >                      {- remaining list  -} [Integer] ->
 >                      {- outputs         -} (Integer, [Integer])
 >       accumHelper cutoff sum elems [] = (sum, elems [])
 >       accumHelper cutoff sum elems (x:xs) =
 >           if sum < cutoff
 >             then accumHelper cutoff (sum + x) (elems . (x:)) xs
 >             else (sum, elems [])

Example:
   GHCi> accumUntilCutoff 20 [1..]
         ==> (21,[1,2,3,4,5,6])
   GHCi> accumUntilCutoff 20 ([1..6] ++ repeat undefined)
         ==> (21,[1,2,3,4,5,6])
     [This demonstrates that accumUntilCutoff is lazy)

Alternatively, I can write a function that (1) prompts the user to
enter numbers until the running sum reaches the cutoff, and then (2)
returns the results in the IO monad.

 > readUntilCutoff :: Integer -> IO (Integer, [Integer])
 > readUntilCutoff cutoff = readHelper cutoff 0 id
 >     where
 >       readHelper :: {- cutoff          -} Integer ->
 >                     {- sum so far      -} Integer ->
 >                     {- elements so far -} ([Integer] -> [Integer]) ->
 >                     {- outputs         -} IO (Integer, [Integer])
 >       readHelper cutoff sum elems = do
 >         if sum < cutoff
 >           then do
 >             putStr "Enter an integer: "
 >             ln <- getLine
 >             let x = read ln
 >             readHelper cutoff (sum + x) (elems . (x:))
 >           else return $ (sum, elems [])

Example:
   GHCi> readUntilCutoff 20
   Enter an integer: 1
   Enter an integer: 2
   Enter an integer: 3
   Enter an integer: 4
   Enter an integer: 5
   Enter an integer: 6
         ==> (21,[1,2,3,4,5,6])

Here's my puzzle:

I am dis-satisfied with the fact that I have to embed IO code in the
middle of accumulation code.  Is there some way to separate
"readUntilCutoff" into two parts (e.g. functions), such that one part
would look extremely similar to "accumUntilCutoff", while the other
part would handle the user interaction associated with getting the
next number?

Thank you
-- Ron

Reference:
[1] http://www.cs.chalmers.se/~rjmh/Papers/whyfp.html



More information about the Haskell-Cafe mailing list