Haskell 提供了一個方便的函式forever,可以無限期地重復單子效應。它可以定義如下:
forever :: Monad m => m a -> m b
forever ma = ma >> forever ma
但是,在標準庫中,函式的定義不同:
forever :: Monad m => m a -> m b
forever a = let a' = a *> a' in a'
let 系結用于強制“此處顯式共享,因為它可以防止空間泄漏而不管優化如何”(來自對實作的評論)。
你能解釋一下為什么第一個定義可能存在空間泄漏嗎?
uj5u.com熱心網友回復:
執行引擎從一個指向你的回圈的指標開始,并在需要找出接下來IO要執行的操作時懶惰地擴展它。根據您對 的定義forever,就“存盤在記憶體中的物件”而言,這就是回圈的幾次迭代:
1.
PC
|
v
forever
|
v
ma
2.
PC
|
v
(>>) --> forever
| /
v L------/
ma
3.
PC
|
v
(>>) --> forever
| /
v L-----/
ma
4.
PC
|
v
(>>) --> (>>) --> forever
| / /
v L-----/----------/
ma
5 and 6.
PC
|
v
(>>) --> (>>) --> (>>) --> forever
| / / /
v L-----/--------/----------/
ma
結果是,隨著執行的繼續,您會獲得越來越多的(>>)單元格副本。在正常情況下,這沒什么大不了的;沒有對第一個單元格的參考,所以當垃圾收集發生時,已經執行的前綴被丟棄。但是如果我們不小心傳遞了一個無限回圈ma怎么辦?
1.
PC
|
v
forever
|
v
forever
|
v
ma
2.
PC
|
v
(>>) -> forever
| /
v L------/
forever
|
v
ma
3.
return here
when done
|
v
(>>) --> forever
| /
v L-------/
PC --> forever
|
v
ma
4.
return here
|
v
(>>) --> forever
| /
v L-------/
PC --> (>>) --> forever
| /
v L-------/
ma
like, 12ish.
return here
|
v
(>>) --> forever
| /
v L-------/
(>>) --> (>>) --> (>>) --> (>>) --> (>>) --> forever <-- PC
| / / / / /
v L-----/--------/--------/--------/----------/
ma
這次我們不能對前綴進行垃圾回收,因為一個“堆疊幀”向上,我們有一個指向頂層的指標forever,它仍然指向第一個(>>)!哎呀。更高級的定義通過記憶體回圈來解決這個問題。在那里,forever ma的物件看起來更像這樣:
/----\
v |
(*>) --/
|
v
ma
Now no extra (*>)'s need to get allocated (nor garbage collected) as execution proceeds -- even if we nest them. The execution pointer will simply move around and around within this graph.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/427289.html
