我的代碼使用了可以描述為指標的資源;為簡單起見,我將在此處使用void指標。資源必須在計算完成后關閉,因此該Control.Exception.bracket函式是確保代碼在發生錯誤時不會泄漏的自然選擇:
run :: (Ptr () -> IO a) -> IO a
run action = bracket acquireResource closeResource action
-- no eta reduction for clarity
這種模式的缺點是資源在action完成后總是會關閉。AFAIU 這意味著不可能做類似的事情
cont <- run $ \ptr -> do
a <- someAction ptr
return (\x -> otherActionUsingResource ptr a x)
cont ()
資源在執行時已經關閉cont。現在我的方法是使用 ForeignPtr 代替:
run' :: (ForeignPtr () -> IO a) -> IO a
run' action = do
ptr <- acquireResource
foreignPtr <- newForeignPtr closeResourceFunPtr ptr
action foreignPtr
現在看來,這大致相當于第一個版本,除了輕微的打字差異和資源關閉延遲。但是,我確實想知道這是否屬實,或者我是否錯過了什么。某些錯誤條件是否會導致這兩個版本產生不同的結果?以這種方式使用 ForeignPtr 是否安全?
uj5u.com熱心網友回復:
如果您想這樣做,我建議您避免這樣做run',這會使您看起來像是要關閉資源。而是做這樣的事情。
acquire :: IO (ForeignPtr ())
acquire action = mask $ \unmask -> do
ptr <- unmask acquireResource
newForeignPtr closeResourceFunPtr ptr
這類事情的挑戰在于您將其留給用戶和/或垃圾收集器來確保資源被釋放。基于原始Ptr的代碼明確了生命周期。現在不是了。許多人認為明確的生命周期對于關鍵資源來說更好。什么ForeignPtr給了你,由 GC 自動完成,這些人認為糟糕的設計。所以仔細想想!malloc這是您最終只想釋放的廉價資源(例如一點點ed 記憶體)嗎?還是您真正想確定的昂貴的東西(例如檔案描述符)?
旁注:Ptr ()并且ForeignPtr ()不是很地道。通常型別引數應該是一個 Haskell 型別,表示所指向的任何內容。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/490525.html
下一篇:如何使用型別類傳達“小于”約束?
