簡體   English   中英

Haskell do notation 中使用 trace 函數的惰性求值

[英]Lazy evaluation in Haskell's do notation using the trace function

我想知道為什么這個“調試消息 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)。

do表示法沒有什么特別的魔法。

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

就是一樣

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

根據 monad 恆等律之一, return a >>= f = fa ,所以

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)是強制的,因為>>= for IO在其左操作數中是嚴格的。 然后return ()被執行,什么都不做。 它的值被忽略,並執行最后的動作, return (trace "debug message 1" ()) 這也沒有任何作用( return不做任何有趣的事情)。 由於main動作的結束是程序的結束,因此永遠不會檢查此返回值,因此永遠不會強制執行,因此不會對其進行評估。 有人認為main應該要求有類型IO ()來強調它的返回值永遠不會被使用。 (我相信他們對此是錯誤的,因為永遠運行的程序確實應該具有IO VoidIO a類型,但這是一個挑剔。)

我的猜測是因為“懶惰的評估”。

請注意,您不會返回任何內容。 換句話說,尚未查詢“返回”(沒有返回),也沒有用。 return語句中,您不在“一元”上下文中。 因此沒有理由對其進行評估,您只需將“調用樹”作為結果傳遞即可。

換句話說,它一直保留在“呼叫樹”上,直到有人想要接它為止。

對於第二種情況,調用trace是微不足道的。 執行單子,直到它達到了“ 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
$ 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM