2009-12-19 133 views
56

讓我一直困惑的一件事是,是否可以使用IORef。在決定是否使用IORef作爲任務時,是否應該遵循任何指導原則?什麼時候在IORef上使用State Monad?什麼時候可以使用IORef?

+0

http://stackoverflow.com/questions/10298664/avoiding-iorefs-in-pure-code – 2012-05-21 20:49:05

回答

69

狀態及其相對ST都產生可作爲單元運行的「單片」狀態計算。他們基本上將可變狀態視爲中間數據,這是產生結果所需的中間數據,但本身不應該對程序的其他部分感興趣。另一方面,在IORef中放入的內容不是要運行的「計算」 - 它只是一個框,其中包含一個簡單的值,可以在IO內以相當任意的方式使用。這個盒子可以放在數據結構中,通過程序的(IO部分)傳遞,每當方便時都會替換它的內容,被函數等關閉。事實上,變量和像C這樣的語言指針可以用IORefs建模,爲任何希望維護他/她能夠用任何語言編寫C代碼的專家的C程序員提供了很大的幫助......這是一定要小心使用的東西。

不過,這是在次非常笨重,如果不是完全不可能的,孤立在一個單一的代碼塊用一塊可變狀態的所有交互 - 國家的一些作品只是必須到處傳遞,把裏面數據結構等。在這種情況下,盒子方法可能是唯一的選擇。在48小時教程中寫下你自己的計劃的chapter introducing mutable state(強烈推薦,順便說一下)提供了一個例子。 (請參閱鏈接,瞭解爲什麼使用IORefs(而不是State或ST)來模擬Scheme解釋器的特定設計中的Scheme環境,爲什麼它最合適)

總之,那些環境需要以任意方式嵌套,保持在用戶交互實例之間(在Scheme REPL輸入的(define x 1)應該可能導致用戶稍後能夠鍵入x並返回1作爲值),放入對象中建模Scheme函數(因爲Scheme函數關閉了它們創建的環境)等等。

總而言之,如果一項任務似乎完全適合它,State將傾向於提供最乾淨的解決方案。如果需要多個不同的狀態,ST可能會提供幫助。但是,如果有狀態計算難以或不可能鎖定在自己的代碼片段中,那麼狀態需要在複雜程序的大部分生命週期中堅持可修改的形式,那麼IORefs可能只是適當的東西。

然後,如果需要一種可以通過IO代碼以受控方式傳遞並與之交互的可變狀態,爲什麼不檢查STM和它的TVars!它們在併發性方面要好得多,事實上,使得解決併發性相關的任務變得非常簡單。但這並不是真的與這個問題有關,所以我會拒絕詳細說明。:-)

+0

非常翔實的文章。感謝您花時間爲我寫這篇文章。 <3 – Rayne 2009-12-19 07:20:40

+0

另外,不要親自帶走非上漲。人們只是因爲他的恩賜而喜上眉梢。 :p – Rayne 2009-12-19 07:21:11

+0

很高興你喜歡它,謝謝你讓我知道。這是我在Haskell上的第一張(有點)更長的作品,寫起來很有趣。 :-) – 2009-12-19 22:04:00

1

當狀態被本地化並且不需要與環境交互時,我使用STRef

12

嗯。當你需要一些可變狀態但是在單線程環境中時,你會使用IORef。或者當你想要一個可變字段放入一個更大的結構體中,而該結構體又由一個同步變量持有時。

一般來說,使用MVars。他們有更強大的語義。

3

就個人而言,我會說這是很好,當您已經使用IO使用IORef■當和唯一。否則,總是State,除非您需要ST的卓越性能。可以使用State monad的多個狀態線程以及一些輔助函數 - 只需將狀態設置爲元組或記錄,然後定義函數以分別設置,獲取或更新每個字段。

特別是,使用StateT s IO通常沒有多少意義。如果你已經在IO,你已經有了可變狀態,所以你不妨使用它 - 例如ReaderT (IORef s) IO

+0

這讓我睜大了眼睛。對於一個初學者來說,'StateT s IO'是一個明顯的,*明顯較差的解決方案。 – 2014-01-21 01:23:36

+0

在'ST'狀態被修改爲「in-place」的'State'和'ST'之間沒有區別嗎? – Alexey 2015-12-25 13:05:48

+0

@Alexey:是的,'State'和'ST'是不同的,但都能解決基本相同的問題。 – 2015-12-26 16:18:22