2015-02-23 66 views
3

我想知道爲什麼這個「調試消息1」並不在這個片段中得到印:懶惰評估做記號使用跟蹤功能

import Debug.Trace 

main = do 
    return (trace "debug message 1"()) 
    trace "debug message 2" (return()) 

第二個「調試消息2」打印出來,但不是「調試消息1」。看來這兩個表達式都是一樣的。我試着將「調試消息1」綁定到一個變量,然後在另一個地方使用該變量,它確實會觸發評估並打印「調試消息1」,但我仍然不明白爲什麼會這樣發生。

如果我翻轉語句的順序,但它仍然是相同的結果:

import Debug.Trace 

main = do 
    trace "debug message 2" (return()) 
    return (trace "debug message 1"()) 

「調試消息1」從不打印(使用runhaskell)。

回答

3

我的猜測是因爲「懶惰的評價」。

請注意,您不會返回任何內容。換句話說,「迴歸」還沒有被質疑(沒有回報),也沒有用。在return聲明中,您並不處於「monadic」環境中。因此沒有理由評估它,因此您只需傳遞「調用樹」即可。

換句話說,它仍然在「調用樹」,直到有人想要拿起它。

對於第二種情況,將調用trace是微不足道的。 monad被執行直到達到「return」,並且在達到return之前,將採取所有必要的操作,包括在需要時執行調試信息。

實施例ghci

$ ghci 
GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Prelude> import Debug.Trace 
Prelude Debug.Trace> do return (trace "debug message 1"()) 
Prelude Debug.Trace> do trace "debug message 2" (return()) 
debug message 2 

同樣爲runhaskell。如果你寫了這兩種方案:

program1.hs

import Debug.Trace 

main = do return (trace "debug message 1"()) 

program2.hs

import Debug.Trace 

main = do 
    trace "debug message 2" (return()) 

然後在控制檯上寫着:

$ runhaskell program1.hs 
$ runhaskell program2.hs 
debug message 2 
$ 

不過,若你寫一個IO Bool(因此有返回值)和您以後使用該值,則跟蹤將被執行,例如:

testFun :: IO Bool 
testFun = do 
    trace "foo" $ return $ trace "Hello" True 

main :: IO() 
main = do 
    b <- testFun 
    print b 

這將導致:

$ runhaskell program3.hs 
foo 
Hello 
True 
$ 

如果您但是省略了print b,把return()相反,Haskell有在返回什麼沒有興趣,因此不打印跟蹤:

testFun :: IO Bool 
testFun = do 
    trace "foo" $ return $ trace "Hello" True 

main :: IO() 
main = do 
    b <- testFun 
    return() --we ran `testFun` but are not interested in the result 

結果是:

$ runhaskell program4.hs 
foo 
$ 
+0

如果我翻轉訂單,但同樣的事情發生雖然。 'main =執行trace「debug message 2」(return());返回(跟蹤「調試消息1」())' – CMCDragonkai 2015-02-23 12:18:55

+0

@CMCDragonkai:如果我在'ghci'中運行它,第二次,它打印調試消息。它不在此上下文中打印的原因是因爲它放在return語句之後。但是,如果您執行「do trace」調試消息2「(return())」,它將打印調試消息。 – 2015-02-23 12:19:38

+0

用'runhaskell'運行。 – CMCDragonkai 2015-02-23 12:20:15

7

關於do表示法沒有什麼特別的意思。

main = do 
    return (trace "debug message 1"()) 
    trace "debug message 2" (return()) 

只是一樣

main = return (trace "debug message 1"()) >>= 
     \_ -> trace "debug message 2" (return()) 

通過的單子身份的法律,return a >>= f = f a之一,所以

main = (\_ -> trace "debug message 2" (return())) 
     (trace "debug message 1"()) 

函數忽略其參數,這樣的說法是沒有評估;的表達式簡化爲

main = trace "debug message 2" (return()) 

第一消息完全消失了,就可以看到,剩餘trace現在是必須降低到評估main最外面的應用程序,所以將被打印此消息。

當你翻轉的順序,你有

main = do 
    trace "debug message 2" (return()) 
    return (trace "debug message 1"()) 

這是一樣的

main = trace "debug message 2" (return()) >>= 
     (\_ -> return (trace "debug message 1"())) 

這裏的情況更復雜一些。第一個trace(消息2)被強制執行,因爲>>=對於IO其左操作數嚴格。然後return()被執行,什麼都不做。其值被忽略,並且執行最後的動作return (trace "debug message 1"())。這也沒有什麼(return從來沒有做任何有趣的事情)。由於main動作的結束是程序結束,因此該返回值從不被檢查,因此也不會被強制執行,因此不會被評估。有些人認爲main應該被要求有類型IO()來強調它的返回值從未被使用過。 (我相信他們對此是錯誤的,因爲永遠運行的程序應該有類型IO VoidIO a,但這是一個挑逗。)

+2

您可以添加一個對https://www.haskell.org/onlinereport/modules.html的引用,其中指出「當程序執行時,計算主要執行,並且其結果(類型t)被丟棄。」 – 2015-02-23 15:19:23

+0

很好的答案。那麼,如果不是主要類型(由你的結論暗示)IO()是什麼? – CMCDragonkai 2015-02-24 03:12:31

+0

@CMCDragonkai,它可以是任何'a'的'IO a'。無論如何,這一結果從未被要求。 – dfeuer 2015-02-24 03:22:31