ST
是monad中允許有限類型的副作用,即可變引用和可變數組。因此,它允許您實現從外部世界看到的純粹功能,但內部使用突變。
這與State
不同,它只通過將計算中的狀態作爲額外的輸入和輸出進行線程化來消除突變。當實施一些強制性算法時,差異很重要,因爲它們有時需要突變纔能有效實施。例如,在State
monad中使用常規數組,您只能通過複製來修改它,而使用ST
則可以在原地進行真正的變異。
爲什麼我們有兩個ST
和IO
的原因是ST
提供了更強的保障比IO
,即:
ST
不允許任意副作用,例如像訪問文件系統。
- 我們可以保證副作用
ST
確實允許不能越過runST
的範圍,所以它可以被看作是從外界純粹的。
我們可以保證副作用無法逃避的原因與類型變量s
有關。因爲在s
中任何ST動作必須是多態的,所以您不能編寫允許任何可變引用進入或離開runST
範圍的代碼,因爲類型檢查器會抱怨它不能保證您的動作的s
和參考或陣列是相同的除非他們來自相同的runST
範圍。
作爲使用ST
單子與可變陣列的一個例子,這裏是Erathostenes的篩的實施方案:
import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed
primesUpto :: Int -> [Int]
primesUpto n = [p | (p, True) <- assocs $ sieve n]
sieve :: Int -> UArray Int Bool
sieve n = runSTUArray $ do
sieve <- newArray (2, n) True
forM_ [2..n] $ \p -> do
isPrime <- readArray sieve p
when isPrime $ do
forM_ [p*2, p*3 .. n] $ \k -> do
writeArray sieve k False
return sieve
runSTUArray
是runST
一種特殊形式,其允許用戶使用突變構建的陣列在凍結之前,將它作爲一個不可變的數組返回。 newArray
,readArray
和writeArray
做你所期望的。
正如你所看到的,sieve
的類型簽名表明它是一個純函數,它是。然而,它會在內部大量使用變異來有效地實現它。
如果你想看看這些,通常比數組更容易使用矢量。 – alternative