Haskell Compiled IO-Action and Flushing
I come across strange behavior with IO, in compiled Haskell code. Here's what's going on:
-- MyScript.hs
main = do
putStr "Enter your name: "
a <- getLine
putStrLn (a ++ " - that a nice name!")
I run this in GHCi by calling main
and it works as one would expect by first printing it Enter your name:
out and then doing what it does next. However, when I compile it using GHC (with and without --make
), it first asks for a string and then prints everything at once, like:
$ ./MyScript
Jimmy Johnson
Enter your name: Jimmy Johnson - That a nice name!
To clarify, I want it to happen in the following sequence:
$ ./MyFixedScript
Enter your name: Jimmy Johnson
Jimmy Johnson - That a nice name!
Can someone explain why this is happening the way it is and how to order the IO in the way I would expect.
Note that I tried to change the first line of the statement do
to _ <- putStr "Enter your name: "
, but that still doesn't work.
source to share
The IO actions are happening in the correct order, the problem is how the input and output channels work. The string is "Enter your name: "
written to the output buffer putStr
before getLine
, but the buffer is not necessarily flushed. Adding hFlush stdout
after putStr
will flush the buffer.
import System.IO
-- MyScript.hs
main = do
putStr "Enter your name: "
hFlush stdout
a <- getLine
putStrLn (a ++ " - that a nice name!")
source to share
I have the same problem today and it seems to work well when I used putStrLn
it but stopped when I changed it to putStr
. As other people have said, this is not Haskell or GHC related, but how IO is blurry. According to the System.IO
documentation, there are 3 buffering modes:
- string-buffering: The entire output buffer is flushed whenever a newline is printed, a buffer overflow occurs, a System.IO.hFlush is issued, or the handle is closed.
- block-buffering: The entire buffer is flushed out whenever it overflows, System.IO.hFlush is issued, or the handle is closed.
- no-buffering: Output is written immediately and never buffered.
The default buffering mode is called system dependent, but it appears that the normal program is in mode line-buffering
while GHCI is in mode no-buffering
. This is because the use will putStrLn
either putStr
be flush or not.
To solve your problem, you can use hFlush stdout
to clear explictitey (see Cirdec's answer) or change the buffering mode once by executing hSetBuffering stdout NoBuffering
. Obvioulsly mode is NoBuffering
not optimal, but probably sufficient for a small toy program.
source to share