I assume you meant *without* comment.
As far as I know, there are no tools to help with this. However, the
usual pattern when using getContents is to pass the String directly to
pure code. That is, the part of the code in the IO monad will be pretty
small and easily debuggable.
-- John
So hPutStr is what really forced it, because it demanded the result of
(map toUpper inpStr) to be calculated. Had you just said
let foo = map toUpper inpStr
the input would not have been evaluated. Nothing is evaluated until
it must be, and hPutStr required it.
The reason that (length inpStr) produced the correct result is this:
when inpStr was evaluated as part of the hPutStr call, it was not yet
garbage-collected because inpStr remained in scope for the later call
to length. By the time length is called, the entire input had, of
course, already been consumed during the call to hPutStr and was being
held in memory. So this program would not be able to process a 1GB
input file.
Hope that clarifies.
-- John