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.

+3


source to share


2 answers


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!")

      

+12


source


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.

+4


source







All Articles