Haskell IO执行顺序

4

我有以下代码:

import Control.Monad (unless)
import System.IO (isEOF, hFlush, stdout)

main :: IO ()
main = unlessFinished $ do
        putStr "$ "
        hFlush stdout
        getLine >>= putStrLn
        main
    where
    unlessFinished action = isEOF >>= flip unless action

当我编译并运行此代码时,它会在空白行的开头显示光标,只有在我按下[Enter]后,它才输出$和我写的任何内容。
似乎在调用putStr "$ "之前调用了getLine,尽管IO单子保证其操作按照代码中顺序连续调用(或者我理解的是这里所写的)。那么为什么它不能正确工作呢?

1
顺便说一句,直到我自己运行了代码,我才真正相信你。然后我在我的小隔间里大声说:“哇啊……!”。很好地将你的问题提炼成如此简单、易懂和惊人的形式! - Daniel Wagner
谢谢。这也让我感到惊讶。实际上,我开始相信这并不是Haskell的“错”,而是一些其他的shell/终端/操作系统的问题,这是我之前不知道的坑。 :) - Sventimir
1个回答

10
实际上,putStrhFlush 操作确实在 getLine 操作之前执行 -- 但是,isEOF 在它们之前执行,并且只有在知道输入是否为 EOF,也就是直到你输入一行之后才会返回。你可以考虑将 isEOF 移动到 getLine 的右边,像这样:
main :: IO ()
main = do
    putStr "$ "
    hFlush stdout
    unlessFinished $ do
        getLine >>= putStrLn
        main
    where
    unlessFinished action = isEOF >>= flip unless action

非常简单易懂的解释!我已经在想,为什么我自己没想到呢。结果证明,Haskell 对我来说仍然有比我预期更多的陷阱。非常感谢。 :) - Sventimir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接