Haskell编译了IO-Action命令和Flushing

时间:2014-12-05 20:42:28

标签: haskell io stdout monads

我在编译的Haskell代码中遇到了IO的奇怪行为。这是发生了什么:

-- MyScript.hs
main = do
    putStr "Enter your name: "
    a <- getLine
    putStrLn (a ++ " - that's a nice name!")

我通过调用main在GHCi中运行它,它可以正常工作,首先打印Enter your name:,然后再做任何事情。但是,当我使用GHC编译它时(有和没有--make),它首先提示输入一行,然后一次打印所有内容,如下所示:

$ ./MyScript
Jimmy Johnson
Enter your name: Jimmy Johnson - That's a nice name!

为了澄清,我希望它按以下顺序发生:

$ ./MyFixedScript
Enter your name: Jimmy Johnson
Jimmy Johnson - That's a nice name!

有人可以解释为什么会发生这种情况,以及如何按照我期望的方式对IO进行排序。

另请注意,我已尝试将do语句的第一行更改为_ <- putStr "Enter your name: ",但仍然无效。

2 个答案:

答案 0 :(得分:12)

IO操作以正确的顺序发生,问题在于输入和输出管道的工作方式。字符"Enter your name: "putStr之前由getLine写入输出缓冲区,但缓冲区不必被刷新。在hFlush stdout之后添加putStr将刷新缓冲区。

import System.IO

-- MyScript.hs
main = do
    putStr "Enter your name: "
    hFlush stdout
    a <- getLine
    putStrLn (a ++ " - that's a nice name!")

答案 1 :(得分:4)

我今天遇到了完全相同的问题,当我使用putStrLn时,它似乎运行良好但在我将其更改为putStr时停止了。正如其他人所说,它与Haskell或GHC无关,而是IO如何被刷新。根据{{​​1}} documentation,有3种缓冲模式:

  • line-buffering:只要输出换行符,缓冲区溢出,发出System.IO.hFlush或句柄关闭,就会刷新整个输出缓冲区。
  • 块缓冲:只要溢出,发出System.IO.hFlush或关闭句柄,就会写出整个缓冲区。
  • no-buffering:输出立即写入,永远不会存储在缓冲区中。

默认缓冲模式被认为是系统相关的,但似乎正常程序处于System.IO模式,而GHCI处于line-buffering模式。这是因为使用no-bufferingputStrLn会刷新或不刷新。

要解决您的问题,您可以使用putStr来清除explictitey(请参阅Cirdec答案)或通过hFlush stdout更改缓冲模式一次。显然hSetBuffering stdout NoBuffering模式不是最佳模式,但对于小型玩具程序来说可能已经足够了。