使用runhaskell时如何确保Haskell do块按顺序执行?
runhaskell execute getLine before putStr in my Haskell IO code? Great question! This is a classic issue tied to standard output buffering in Haskell, and it comes down to how GHC handles IO differently in interactive vs non-interactive environments.
Let's break down what's happening first. Your code looks like this:
main = do putStr "Enter Some Text: " text <- getLine writeFile "text.txt" text putStrLn "Updated!"
When you run this in GHCi, it behaves exactly as expected: it shows the prompt, waits for your input, writes to the file, then prints the success message. But with runhaskell, you see this out-of-order behavior:
runhaskell EditFile.hs hello Enter Some Text: Updated!
The Root Cause
GHC uses different buffering strategies by default depending on whether it's in an interactive session (like GHCi) or running a compiled/interpreted program non-interactively (like runhaskell):
- In GHCi, standard output uses line buffering, and it automatically flushes the buffer when waiting for input. That's why your
putStrprompt shows up right away. - With
runhaskell, standard output defaults to block buffering. SinceputStr "Enter Some Text: "doesn't end with a newline, the text gets stuck in the output buffer. The program waits for your input viagetLinefirst, and only when the buffer is flushed (either by the newline inputStrLnor when the program exits) does the prompt finally show up—along with the success message.
Fixes to Try
Here are three straightforward ways to fix this, depending on your needs:
1. Manually Flush the Buffer
The most direct fix is to force the output buffer to flush right after your putStr call. You'll need to import System.IO to use hFlush:
import System.IO main = do putStr "Enter Some Text: " hFlush stdout -- Push the prompt out of the buffer immediately text <- getLine writeFile "text.txt" text putStrLn "Updated!"
2. Use putStrLn (If a Newline Is Acceptable)
If you don't mind the prompt being on its own line, swap putStr for putStrLn. putStrLn automatically adds a newline and flushes the buffer, so your prompt will show up before input is requested:
main = do putStrLn "Enter Some Text:" text <- getLine writeFile "text.txt" text putStrLn "Updated!"
3. Set Global Line Buffering
If you have multiple places in your code where you need unbuffered or line-buffered output, you can set the buffering mode for stdout once at the start of your program:
import System.IO main = do hSetBuffering stdout LineBuffering -- Make all output line-buffered putStr "Enter Some Text: " text <- getLine writeFile "text.txt" text putStrLn "Updated!"
This ensures that any output (even without a newline) will be flushed as soon as a line is complete, or when waiting for input—just like in GHCi.
内容的提问来源于stack exchange,提问作者E2718

