
main = do let mylist = [1..10000] writeFile "myfile.txt" (unlines (map show mylist))
This part does run in O(1) space and isn't problematic.
-- ... mylist' <- (map read . lines) `fmap` readFile "myfile.txt" let result = sum mylist' print result
Indeed, this works by interleaving the I/O and the computation of the result. It is a convenient way to program, because you can use ordinary list operations on the I/O stream. We know it is not "safe": a programmer can observe the fact that implicit I/O is going on. It is also not a good way to program: you have no way to catch errors and take appropriate action if the implicit I/O should fail in some way. Should we really be encouraging the writing of programs that have no error checking?
With lazy IO, this program too should run in constant space, and the functions that consume the large intermediate structure can remain unchanged!
So, I think a lazy programming language should support not only lazy evaluation, but also lazy IO! When it is appropriate to abstract away from the details of when IO happens, lazy IO is useful. It can facilitate separation of concerns and modularity, just like lazy evaluation. Appropriate lazy IO should not compromise the purity of the language or be an obstacle to optimistic evaluation.
I look forward to seeing a form of lazy I/O that doesn't suffer from these problems! (although I suspect I still won't use it :-p) Cheers, Simon