Consider the following Haskell program. I am trying to program in a "stream style" where functions operate on streams (implemented here simply as lists). Things like normalStreamFunc work great with lazy lists. I can pass an infinite list to normalStreamFunc and effectively get out another infinite list, but with a function mapped onto each value. Things like effectfulStreamFunc do not work so well. The IO action means that I need to evaluate the entire list before I can pull off individual values. For example, the output of the program is this:
a
b
c
d
"[\"a\",\"b\"]"
but what I want is a way to write effectfulStreamFunc so that the program produces this:
a
b
"[\"a\",\"b\"]"
leaving the remaining actions unevaluated. I can imagine a solution using unsafePerformIO, but let's say I am taking that off the table. Here is the program:
import IO
normalStreamFunc :: [String] -> [String]
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs
effectfulStreamFunc :: [String] -> IO [String]
effectfulStreamFunc [] = return []
effectfulStreamFunc (x:xs) = do
putStrLn x
rest <- effectfulStreamFunc xs
return (reverse(x):rest)
main :: IO ()
main = do
let fns = ["a", "b", "c", "d"]
es <- effectfulStreamFunc fns
print $ show $ take 2 es
Update:
Thank you all for the helpful and thoughtful feedback. I had not seen the sequence
operator before, that is helpful to know about. I had thought of a (less elegant) way to pass around IO (String) values instead of Strings, but for the style of programming that is of limited usefulness, since I want to other stream functions to act on the strings themselves, not on actions that can produce a string. But, based on thinking through the other responses, I think I see why this is unsolvable in general. In the simple case I presented, what I really wanted was the sequence
operator, since I was thinking that the stream ordering implied an ordering on the actions. In fact, no such ordering is necessarily implied. This becomes clearer to me when I think about a stream function that takes two streams as input (e.g. pairwise addition two streams). If both "incoming" streams performed IO, the ordering of those IO actions is undefined (unless, of course, we define it by sequencing it ourselves in the IO monad). Problem solved, thank you all!