我已經閱讀了幾本關于 Haskell 的書籍,但并沒有撰寫太多代碼,而且我對 Haskell 在特定情況下所做的事情感到有些困惑。假設我正在使用getLine以便用戶可以按一個鍵繼續,但我真的不想以任何有意義的方式解釋那個人的輸入。我相信這是一種有效的方法:
main = do
_ <- getLine
putStrLn "foo"
我了解這是做什么的基本要點。getLine 回傳一個IO String,而 putStrLn 接受一個String并回傳IO(),所以如果我理論上想列印用戶在控制臺中輸入的內容,我基本上會使用 Monad 類中的>>=運算子。就我而言,我相信我的代碼等同于getLine >> putStrLn "foo",因為我丟棄了 getLine 的回傳值。
但是,如果我這樣做呢?
main = do
let _ = getLine
putStrLn "foo"
在這種情況下,我們正在設定一種 lambda 來處理需要IO String的東西,對吧?我可以撰寫一個printIOString函式來列印用戶的輸入,這樣就可以了。但是,當我實際上沒有使用該IO String時,程式的行為很奇怪...... getLine 甚至沒有提示我輸入;該程式只是列印出“foo”。
我不確定這里的“去糖”語法是什么,或者這是否會闡明 Haskell 在幕后所做的事情。
uj5u.com熱心網友回復:
讓我們用幾個更復雜的例子來熱身。
main = do
x
x
x
putStrLn "foo"
where
x = do
getLine
你期望這會做什么?我不了解你,但我期望程式得到三行然后列印一些東西。如果我們對第二個do塊進行脫糖,我們得到
main = do
x
x
x
putStrLn "foo"
where x = getLine
由于這是另一個脫糖,它的行為相同,在列印前得到三行。如果您沒有發現第一個直觀的答案,那么還有另一種思路可以得出相同的答案。“參考透明”是 Haskell 的定義特性之一,確切的意思是你可以用它的定義替換對某事物(即變數名)的“參考”,所以前面的程式應該和之前的程式完全一樣
main = do
getLine
getLine
getLine
putStrLn "foo"
如果我們x = getLine認真對待這個等式。好的,所以我們有一個讀取三行并列印的程式。這個如何?
main = do
x
x
putStrLn "foo"
where x = getLine
獲取兩行并列印。和這個?
main = do
x
putStrLn "foo"
where x = getLine
取一行,然后列印。希望你能看到這是怎么回事......
main = do
putStrLn "foo"
where x = getLine
獲取零行然后列印,即立即列印!我用where而不是let讓開頭的例子更明顯一點,但你幾乎總是可以where用它的表親替換一個塊let而不改變它的含義:
main = let x = getLine in do
putStrLn "foo"
由于我們不參考x,我們甚至不需要命名它:
main = let _ = getLine in do
putStrLn "foo"
這就是您撰寫的代碼的脫糖。
uj5u.com熱心網友回復:
第一種情況像您預期的那樣脫糖:
main = getLine >>= \_ -> putStrLn "foo"
這相當于
main = getLine >> putStrLn "foo"
在第二種情況下,
main = do
let _ = getLine
putStrLn "foo"
被脫糖為
main = let _ = getLine in putStrLn "foo"
由于_ = getLine不需要該值來評估let運算式的 RHS,編譯器可以隨意忽略它,并且永遠不會執行 IO 效果,這就是不再提示您輸入 CLI 輸入的原因。
即使這兩種情況都忽略了getLine差異的結果是第一種情況getLine在IO背景關系中評估,而第二種情況評估getLine為純值。在IO副作用中必須一起執行和排序,但在純背景關系中,編譯器可以自由地忽略未使用的值。
我不建議這樣做,因為它不是很地道,但你可以寫類似
printIOString :: IO String -> IO ()
printIOString ios = ios >>= putStrLn
并像使用它一樣printIOString getLine
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/437092.html
上一篇:如何從STDIN讀取1個位元組?
