Opened 7 years ago

Closed 7 years ago

Last modified 6 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: Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

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 7 years ago.
Non-mangled transcript: works in hugs, nhc, hbc, but not in ghc

Download all attachments as: .zip

Change History (7)

Changed 7 years ago by guest

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

comment:1 Changed 7 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 7 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 7 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 6 years ago by simonmar

  • Architecture changed from Multiple to Unknown/Multiple

comment:5 Changed 6 years ago by simonmar

  • Operating System changed from Multiple to Unknown/Multiple

comment:6 Changed 6 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.