對於我在Haskell中的矢量圖形庫,我必須攜帶一個相當大的狀態:行筆畫參數,顏色,剪輯路徑等。我知道兩種這樣做的方法。引用來自Haskell-cafe的評論:「我建議你要麼使用具有可變狀態的讀取器monad,要麼使用具有不可變狀態的狀態monad」。在Haskell中快速更新大狀態
這是我的問題:更新一個大的不可變狀態是一個性能殺手。使用大量STRefs就像在Haskell中編寫C:它是冗長而醜陋的。
這裏是不可改變狀態:
data GfxState = GfxState {
lineWidth :: Double,
lineCap :: Int,
color :: Color,
clip :: Path,
...
}
setLineWidth :: Double -> State GfxState()
setLineWidth x = modify (\state -> state { lineWidth = x })
據我所知,「狀態{=的lineWidth X}」創建一個新的GfxState,讓舊的被垃圾收集。當狀態很大並經常更新時,這會導致性能下降。
這裏是可變的狀態:
data GfxState s = GfxState {
lineWidth :: STRef s Double,
lineCap :: STRef s Int,
color :: STRef s Color,
clip :: STRef s Path,
...
many more STRefs
}
setLineWidth :: GfxState s -> Double -> ST s()
setLineWidth state x = writeSTRef (lineWidth state) x
現在,我得到(GfxState S)和(ST S)和(STRef S)所有的地方,這是冗長,混亂,跳動的精神寫簡短而富有表現力的代碼。我可以使用C + FFI來讀取和更新大狀態,但由於我經常遇到這種模式,我希望有更好的方法。
做你正在做的事情就像在haskell中編寫C一樣,因爲我看到你暗示的庫接口是非常必要的接口。 `setLineWidth`?以更實用的方式製作界面將使實現更具功能性。 – luqui 2010-11-24 10:55:25
對於第一個版本,使用「state {lineWidth = x}」更新狀態應該與舊狀態共享,所以我不希望它創建一個全新的狀態。您可能希望至少使狀態的「原子」元素變得嚴格(例如,lineWidth變爲!Double,並且lineCap變爲!Int),我懷疑這可能會妨礙性能。 – 2010-11-24 11:59:11
@stephen,以及*值*與舊記錄共享。但是如果你有一個有100個字段的記錄,每個記錄更新將複製100個指針。 – luqui 2010-11-24 12:42:11