[英]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 Void
或IO 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.