I've uploaded this change (with a much more useful error message) as D327 on Phabricator.  I have done some very limited testing, and it passes GHC's validation (on Linux), but I would really appreciate if some people who use a lot of lazy IO could test this against their programs to make sure it doesn't produce errors in any cases when it shouldn't.

Thanks,
David

On Mon, Jul 21, 2014 at 4:16 PM, David Feuer <david.feuer@gmail.com> wrote:
Currently,

withFile "foo" hGetContents >>= putStrLn

prints out an empty line, the better to confuse newbies.

I propose modifying the lazyRead function in GHC.IO.Handle.Text that
currently reads

lazyRead :: Handle -> IO String
lazyRead handle =
   unsafeInterleaveIO $
        withHandle "hGetContents" handle $ \ handle_ -> do
        case haType handle_ of
          ClosedHandle     -> return (handle_, "")
          SemiClosedHandle -> lazyReadBuffered handle handle_
          _ -> ioException
                  (IOError (Just handle) IllegalOperation "hGetContents"
                        "illegal handle type" Nothing Nothing)

to something like

lazyRead :: Handle -> IO String
lazyRead handle =
   unsafeInterleaveIO $
        withHandle "hGetContents" handle $ \ handle_ -> do
        case haType handle_ of
          ClosedHandle     -> return (handle_, error "Forcing the
result of a lazy read led to an attempt to read from a closed
handle.")
          SemiClosedHandle -> lazyReadBuffered handle handle_
          _ -> ioException
                  (IOError (Just handle) IllegalOperation "hGetContents"
                        "illegal handle type" Nothing Nothing)

Ideally that error should instead be something to throw an imprecise
exception, but I don't know how to use those yet. I can't personally
see a way for this to break sane, working code, but the folks on #ghc
thought it should be discussed and debated on list.

David Feuer