Opened 8 years ago

Closed 8 years ago

Last modified 7 years ago

#1355 closed bug (invalid)

Interactive I/O with ghc doesn't prompt until after reading

Reported by: ok@… Owned by:
Priority: normal Milestone:
Component: Prelude Version: 6.2
Keywords: interactive stdio late prompt Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Test Case:
Blocked By: Blocking:
Related Tickets: Differential Revisions:

Description

My students complained that the interactive program they were to write using Haskell would not issue its first prompt until after the first line of input had been entered. I enclose a tiny Haskell program that exhibits this behaviour in 6.2 (SPARC Solaris) and 6.4 (Intel Linux) and am told that it also happens in 6.6. I also show that it works fine in Hugs, HBC, and NCH98. (Mangled when I tried pasting it in. Stupid web forms.)

Attachments (1)

transcript (1.5 KB) - added by guest 8 years ago.
Non-mangled transcript: works in hugs, nhc, hbc, but not in ghc

Download all attachments as: .zip

Change History (7)

Changed 8 years ago by guest

Non-mangled transcript: works in hugs, nhc, hbc, but not in ghc

comment:1 Changed 8 years ago by simonmar

  • Resolution set to invalid
  • Status changed from new to closed

You're expecting unbuffered output, but GHC does line-buffering by default when outputting to a terminal. If you want no buffering, you have to ask for it: hSetBuffering stdout NoBuffering, or else use explicit flushing: hFlush stdout.

comment:2 Changed 8 years ago by Frederik

This was sent by "ok <ok@…>" to the glasgow-haskell-bugs mailing list, but didn't show up in the bug report (and I agree with it):

You're expecting unbuffered output,

WRONG! Not a bit of it. I am expecting C-like behaviour where standard output is line buffered, BUT reading from stdin implies an automatic flush of stdout.

but GHC does line-buffering by default

when outputting to a terminal.

This is not relevant. The behaviour I expect is provided by EVERY other Haskell implementation I tried, and is also provided by every Prolog system I tried (all of which buffer stdout), and by every Scheme system I tried (again, no unbuffered output is involved or needed), by the Common Lisp system I just tried (again using buffered output). And you might care to try typing

(print "Prompt> "; TextIO.inputLine TextIO.stdIn);

at a Standard ML system, which again does exactly what I expect.

If ML, Scheme, Lisp, Prolog, and above all C (whose library GHC presumably builds on) can all get this right, why is it hard for Haskell to get it right? But wait, it ISN'T hard for Haskell to get it right. ALL the other Haskell systems I tried DID get it right, remember?

It's GHC that is the odd one out.

If you want no buffering, you have to ask

for it: hSetBuffering stdout NoBuffering,

No, I *don't* want no buffering. It's such a simple thing: just before going to the operating system for more characters, you say "if this is stdin and stdin is a terminal and stdout is a terminal, flush stdout." By checking at startup

terminal = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)

? STDIN_FILENO : -1;

then the test is a single comparison:

if (fd == terminal) fflush(stdout); n = read(fd, buffer, sizeof buffer);

instead of just the call to read().

This is what C has given me since before C89. This is the behaviour I get from every other Haskell system I have been able to try. The fact that GHC does not do it really is a *BUG* in GHC.

or else use explicit flushing:

hFlush stdout.

Thank you for the workaround which I shall pass on to my students. However, the fact that GHC does not do this automatically is a pain in the next, an incompatibility with other Haskell implementations, an incompatibility of expectation with the C stdio which Haskell IO so sedulously emulates, and is something which is easier to fix than to dismiss.

comment:3 Changed 8 years ago by simonmar

I'm aware that everyone else does this, and I'm aware that GHC is different. Also, the predecessor to GHC's current I/O system had the behaviour you want.

Nevertheless, I did it this way on purpose. Why? Because I think it's an ugly special case, and I didn't want to add it to the code. If you expect stdin/stdout to have this odd interactive behaviour, you might start expecting it to happen on sockets too, and it won't. The behaviour of buffering in GHC is simple and works the same on stdin/stdout as it does on all other Handles. There's nothing in the Haskell 98 spec that requires us to have magic flushing of stdout.

Still, you wouldn't be the first person to complain about this (it's question 6.1 in the FAQ!). I guess I wouldn't object if someone were to submit a patch to implement this special case, as long as it didn't affect performance.

comment:4 Changed 7 years ago by simonmar

  • Architecture changed from Multiple to Unknown/Multiple

comment:5 Changed 7 years ago by simonmar

  • Operating System changed from Multiple to Unknown/Multiple

comment:6 Changed 7 years ago by guest

I agree with the bug report, it's bitten me a few times, and note that using hFlush requires importing System.IO which should not be required in these very simple programs.

main = do { putStr "Enter your name: "; name <- getLine; print $ "hello ++ name }

is one the first programs anyone writes in any language and it shouldn't require a bunch of web searching to find out what is wrong and how to fix it.

Note: See TracTickets for help on using tickets.