2014-02-22 20 views
2

據我所知,Haskell中的do塊只是單子綁定操作符的某種語法糖。例如,人們可以使用唯一的一元綁定語法表達do塊

main = do f <- readFile "foo.txt" 
      print f 
      print "Finished" 

轉換爲

main = readFile "foo.txt" >>= print >> print "Finished" 

是否所有do塊轉換的綁定語法?怎麼樣,例如,該塊在那裏f多次使用:

main = do f <- readFile "foo.txt" 
      print $ "prefix " ++ f 
      print $ f ++ " postfix" 

假設我們是在IO單子,這是不可能簡單地執行兩次readFile計算。這個例子(如果可能的話)如何使用綁定語法來表示?

我認爲使用Control.Monad是沒有解決方案,因爲它在內部使用do塊。

我認爲這有可能表達這種使用箭頭(使用&&&) - 也許這是隻有箭頭可以作爲單子的推廣的情況下?

請注意,這個問題不是關於上面的特例,而是關於一次性計算結果在一次性表達式中使用多次的一般情況,如print

+1

通常這會讓你的代碼更漂亮。'Control.Applicative'有一堆你可以用來混淆'm a'類型的東西的操作符,然後你可以把'>> ='輸出到輸出中。通常這更漂亮。有時候,免費點並不漂亮;我們可以寫'main = readFile「foo.txt」>> = flip mapM_ [(「prefix」++),(++「postfix」)]。 (打印)。翻轉身份證「,但那不會很清楚。 – AndrewC

+0

@AndrewC感謝您的建議!在這種特殊情況下,我只關心如何轉換do語法,並且我打算保持簡單。一般來說,我完全同意你的觀點,應用是這裏的正確工具。此外,有沒有什麼具體的原因,爲什麼你不使用'forM_'而不是'flip mapM_'? –

+1

因爲我忘了! :)讓我們假裝這是爲了避免導入'Control.Monad'的另一個原因。麻煩的是,如果你不導入Control.Monad,你會錯過可愛的(> =>):: Monad m =>(a-> mb) - >(b-> mc) - >( a - > mc)'。 – AndrewC

回答

9

是的,它們都可以轉換爲綁定語法;實際上,它們是由編譯器內部轉換的。

我希望這個翻譯你的例子給你提示:

main = readFile "foo.txt" >>= \f -> 
     (print $ "prefix " ++ f) >> 
     (print $ f ++ " postfix") 
+0

非常感謝,Lambda表達式是我失蹤的關鍵部分! –

3

Report給出了從做語法到內核哈斯克爾全文翻譯:

做表情滿足這些身份,這可能作爲內核的翻譯,用於刪除空的空格之後:

do {e}    = e 
do {e;stmts}   = e >> do {stmts} 
do {p <- e; stmts} = let ok p = do {stmts} 
          ok _ = fail "..." 
         in e >>= ok 
do {let decls; stmts} = let decls in do {stmts} 

省略號「...」代表編譯器生成的錯誤消息,傳遞失敗,最好給出模式匹配失敗位置的某些指示;在Prelude中定義的函數>>,>> =和失敗是Monad類中的操作; ok是一個新的標識符。

所以,你的例子翻譯是這樣的:

do f <- readFile "foo.txt" 
    print $ "prefix " ++ f 
    print $ f ++ " postfix" 
= 
let ok f = do print $ "prefix " ++ f 
       print $ f ++ " postfix" 
    ok _ = fail "..." 
in readFile "foo.txt" >>= ok 
= 
let ok f = (print $ "prefix " ++ f) >> do print $ f ++ " postfix" 
    ok _ = fail "..." 
in readFile "foo.txt" >>= ok 
= 
let ok f = (print $ "prefix " ++ f) >> (print $ f ++ " postfix") 
    ok _ = fail "..." 
in readFile "foo.txt" >>= ok 

這個版本沒有do塊,但看起來不是很自然。但是我們可以應用等式推理和我們知道的任何優化。因此,例如,觀察到的ok _ = fail "..."條款是死代碼,我們可以inlike ok像這樣:

= 
readFile "foo.txt" >>= \f -> 
(print $ "prefix " ++ f) >> 
(print $ f ++ " postfix") 

所有do塊可以以這種方式機械地翻譯成代碼,無需do

相關問題