
[snip]
Certainly you can observe non-strictness, that's not the point. The point is that you can also observe more than just non-strictness using interact, and I don't think that is desirable. For example:
interact (\xs -> let z = length xs in "Hello World\n")
Now, Haskell is a pure language, so it shouldn't matter whether the implementation evaluates z or not, as long as it is careful not to violate the non-strict semantics by turning a terminating program into a non-terminating one. A parallel Haskell implementation might happily spawn off another thread to evaluate z for example. An optimistic implementation might evaluate z for a fixed amount of time before continuing with the main thread of evaluation.
BUT in the presence of lazy I/O this simply isn't true any more. Why? Because z is not pure; evaluating it has a side-effect. And yet it has type Int. Am I the only one who thinks this is wrong?
I agree with you completely. It is wrong for all the same reasons that unsafePerformIO is wrong, except that it is worse than that because unsafePerformIO has "unsafe" in the title, and people are discouraged from using it without due care. By contrast, "interact" and "getContents" are presented as being nice friendly functions that everyone should use. If I had my way, getContents would be called unsafeGetContents and interact would be called unsafeInteract. Even better would be for them to both be removed from Haskell completely :-)) But then, if I had my way, I would abandon lazy evaluation correctly and fork a strict-by-default language derived from Haskell. I have spent sufficient time analysing lazy evaluation during my PhD to become convinced that it is the wrong evaluation strategy in 90\% of cases, and that in the 10\% of cases where it is needed, the program would be much clearer if this was stated explicitly in the program text. Haskell is a good language, pureness is good, type classes are good, monads are good - but laziness is holding it back. [end rant] -Rob