
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