2014-12-05 25 views
3

我在編譯好的Haskell代碼中遇到了IO的奇怪行爲。這裏是發生了什麼:Haskell編譯IO-Action命令和Flushing

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

我GHCI運行通過調用main和它的工作正如人們所預料,第一印刷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: ",但這仍然無效。

+1

這是由於緩衝而不是io動作的排序。 – sabauma 2014-12-05 20:50:13

回答

12

IO操作按正確順序發生,問題在於輸入和輸出管道的工作方式。字符串"Enter your name: "getLine之前被putStr寫入輸出緩衝區,但緩衝區未必被刷新。在putStr沖刷緩衝區後,添加hFlush stdout

import System.IO 

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

哇,我有一個想法,可能是這種情況,但我不相信Haskell實際上會服從一種有狀態的東西,如沖洗stdout ... – AJFarmar 2014-12-05 20:49:52

+1

@AJFarmar:在這種情況下,Haskell只是給你一個非常直接的界面與其他編程語言相同的底層管道抽象。由於它藏在'IO'內,它可以完全複製行爲。 – 2014-12-05 22:10:31

+0

我想GHCi會自動刷新? – AJFarmar 2014-12-06 08:35:11

4

今天我有相同的問題,而且看起來效果不錯,我用putStrLn時,但停止時,我把它改成putStr。正如其他人所說,這與Haskell或GHC沒有關係,但IO如何被刷新。根據System.IOdocumentation有3緩衝模式:

  • 線緩衝:整個輸出緩衝器被刷新每當一個新行被輸出時,緩衝器溢出,一個System.IO.hFlush被髮出,或手柄關閉。
  • 塊緩衝:只要溢出,發出System.IO.hFlush或句柄關閉,就會寫出整個緩衝區。
  • 無緩衝:輸出立即寫入,永遠不會存儲在緩衝區中。

默認緩衝模式,據說besystem依賴,但似乎正常的程序是line-buffering模式,而GHCI是no-buffering模式。這解釋,因爲使用putStrLnputStr將刷新或不。

要解決您的問題,您可以使用hFlush stdout來刷新顯式描述(請參閱Cirdec答案),或者通過執行hSetBuffering stdout NoBuffering來更改緩衝模式。 Obviously的NoBuffering模式不是最優的,但對於小型玩具程序來說可能就足夠了。

+0

'hSetBuffering'的句柄是第一個參數。 'hSetBuffering :: Handle - > BufferMode - > IO()'所以在'stdout'上設置'NoBuffering'就是'hSetBuffering stdout NoBuffering'。 – Cirdec 2014-12-06 23:05:53

+0

@Cirdec我修好了 – mb14 2014-12-07 13:49:49