ST is a monad in which a limited type of side effects are allowed, namely mutable links and mutable arrays. Thus, it allows you to implement functions that are pure, as can be seen from the outside world, but which use the mutation inside.
This differs from State in that it only fakes a mutation by streaming state through your calculations as additional inputs and outputs. The difference is important when implementing some imperative algorithms, because sometimes it is required that the mutation be effectively implemented. For example, using a regular array in the State monad, you can only change it by creating a copy, whereas with ST you can have a true mutation in place.
The reason we have ST and IO is because ST provides higher guarantees than IO , namely:
ST does not allow arbitrary side effects, for example, access to the file system.- We can guarantee that the side effects of
ST really do not avoid the runST volume, and therefore it can be considered as pure from the outside world.
The reason we can guarantee that side effects cannot go away is because of a variable of type s . Since any ST action must be polymorphic in s , you cannot write code that allows any mutable links to enter or leave the runST area, as the type checker will complain that it cannot guarantee that s your action and link value or arrays are the same if they do not belong to the same region of runST .
An example of using the ST monad with variable arrays is the implementation of the eratosten sieve:
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 is a specialized form of runST that allows you to build an array using the mutation inside before freezing it and returning it as an immutable array. newArray , readArray and writeArray do what you expect.
As you can see, a signature like sieve indicates that it is a pure function, and it is. However, he actively uses the mutation inside to effectively implement it.
hammar Nov 19 '11 at 20:57 2011-11-19 20:57
source share