2011-08-10 16 views
8

我正在編寫一些代碼(一個Metropolis-Hastings MCMC採樣器),它將使用隨機數生成器,並根據此修改數組和其他可能的結構。不錯的方式來跟蹤ST monad函數之間的幾個引用?

我最初的想法是使用ST monad,這樣我就可以使用ST數組和mersenne-random-pure64軟件包,將PureMT生成器保持爲狀態的一部分。但我希望能夠將一些工作分解成單獨的幫助函數(例如,在給定範圍內對隨機整數進行採樣,更新數組結構以及可能更復雜的事情)。爲此,我想我需要將對PureMT gen和數組的引用傳遞給所有函數,如果我需要存儲更多狀態,可能會很快變得非常難看。

我的直覺是把所有的狀態組合成一個單一的數據類型,我可以在任何地方訪問,就像我使用狀態monad定義一個新的數據類型一樣,但是我不知道ST是否可以使用monad,或者正確的方式去做。

有沒有什麼好的模式來做這種事情?我想盡可能保持一般,因爲我可能需要添加額外的狀態並在現有部件周圍構建更多的單代碼。

我試過尋找ST單子代碼的例子,但它似乎沒有在真實世界的Haskell中涵蓋,而haskell wiki的例子非常簡短。

謝謝!

回答

10

我的直覺是將所有狀態到一個單一的數據類型,我可以訪問任何地方,我將使用狀態單子通過定義一個新的數據類型的,但我不知道這是否是可能的ST monad,或正確的方式去解決它。

有沒有什麼好的模式來做這種事情?我想盡可能保持一般,因爲我可能需要添加額外的狀態並在現有部件周圍構建更多的單代碼。

的關鍵點這裏知道的是,它是你使用ST完全不相干ST引用本身只是常規值,你需要在各種地方訪問,但你不要實際上想改變它們!可變性發生在ST,但STRef值和基本上只讀。他們的名字是,指向的可變數據。

當然,對周圍環境的只讀訪問權限是monod的作用。所有函數的引用的醜陋傳遞正是它爲你所做的,但因爲你已經在ST中,所以你可以將它作爲一個monad變換器使用。舉個簡單的例子,你可以做這樣的事情:

newtype STEnv s e a = STEnv (ReaderT e (ST s) a) 
    deriving (Functor, Applicative, Monad) 

runEnv :: STEnv s e a -> ST s e -> ST s a 
runEnv (STEnv r) e = runReaderT r =<< e 

readSTEnv :: (e -> STRef s a) -> STEnv s e a 
readSTEnv f = STEnv $ lift . readSTRef . f =<< ask 

writeSTEnv :: (e -> STRef s a) -> a -> STEnv s e() 
writeSTEnv f x = STEnv $ lift . flip writeSTRef x . f =<< ask 

更多一般性,你可以abstract over the details of the reference types,並使其成爲單子一般「與可變參考環境」。

+0

這聽起來像個好主意,我忘了參考文獻實際上並沒有改變。感謝你!我也想知道,如果可能使用ST monad從它本身內部將它們無需訪問的更高級別環境中運行的獨立狀態計算與它們自己的隱藏本地狀態分開。我需要爲此使用ST monad變壓器,還是隻需要在ST monad中使用let a = runST $ ....?如果你可以(不使用變壓器),如果你試圖從封裝monad中取消引用ref(假設它們在範圍內)會發生什麼? – Tom

+0

@Tom:'ST'沒有變壓器 - 就像'IO'一樣,它總是在任何堆棧的底部。此外,任何兩個完整的'ST'計算 - 也就是在調用'runST'內部發生的事情 - 完全與對方的狀態隔離,任何混合它們的嘗試都會導致類型錯誤。你可以傳遞不透明的值,就像你可以在'ST'內部傳遞'IORef'一樣,但是你不能使用它們,也不能使用'IORef's。儘管如此,您可以像往常一樣使用'runST';那樣的結果是一如既往的純粹的價值。 –

+0

@Tom:另外,如果你像我的例子那樣使用'ReaderT',你可能可以實現幾種包圍操作而不會有太多麻煩 - 克隆當前環境以在單獨的內部'ST'中運行,運行一些東西在具有不同環境的同一個「ST」中,&c。 –

5

您可以像使用IO monad一樣使用ST monad,同時銘記您只能獲取數組和引用,並且不會獲得其他IO好東西。就像IO一樣,如果你想通過你的計算透明地對某個狀態進行線程化,你可以在它上面添加一個StateT。

+0

哦,我沒有想到,我認爲這將解決我的問題。 – Tom

相關問題