Hello World Bug - silent stdout errors
Background
This entertaining talk explains the issue: https://www.irill.org/events/ghm-gnu-hackers-meeting/videos/jim-meyering-goodbye-world-the-perils-of-relying-on-output-streams-in-c
hello.hs
main = putStrLn "Hello world"
Run it
$ runhaskell hello.hs > /dev/full ; echo $?
hello.hs: <stdout>: hPutChar: resource exhausted (No space left on device)
1
That looks good! We tried to save the output to a file but the disk was full, so we got an error message, and a process exit code indicating failure.
Run it compiled
$ ghc hello.hs
$ ./hello > /dev/full ; echo $?
0
Not good! The error was silently ignored, and additionally the process lied when it reported a successful exit status.
Why did it behave differently when compiled? When runhaskell
is used, the buffering of stdout is NoBuffering
therefore the putStrLn call fails. But when compiled, stdout is in LineBuffering
mode and therefore the putStrLn call succeeds.
The fix:
hello2.hs
import System.IO
main = do
putStrLn "Hello world"
hClose stdout
Run it compiled
$ ghc hello2.hs
$ ./hello2 > /dev/full ; echo $?
hello: <stdout>: hClose: resource exhausted (No space left on device)
1
Looks good! But there's a catch:
$ runhaskell hello2.hs ; echo $?
Hello world
ghc: <stdout>: hFlush: illegal operation (handle is closed)
1
Now our program fails to run correctly with runhaskell
:(
It seems that runhaskell
is running some hidden code after main finished. It is not clear to me how to write a correct "Hello World" that works both compiled and with runhaskell
.
Summary
One of the greatest things about Haskell is that short, clear and concise programs can also be correct.
It is my opinion that the original "hello.hs" should Just Work™. Haskell programmers shouldn't have to know that they need to always close stdout before exiting. Especially since it's not even clear how to do it properly...
Thank you!
Trac metadata
Trac field | Value |
---|---|
Version | 7.10.3 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |