Let's read the runST type. I added an explicit quatifier for a .
runST :: forall a . (forall s. ST sa) -> a
He reads the following contract:
- the caller selects a fixed type
a - the caller provides the argument
x - argument
x must be of type ST sa for any choice of s . In other words, s will be chosen by runST rather than the caller.
See a similar example:
runFoo :: forall a . (forall s. s -> [(s,a)]) -> [a] runFoo x = let part1 = x "hello!" -- here s is String -- part1 has type [(String, a)] part2 = x 'a' -- here s is Char -- part2 has type [(Char, a)] part3 = x (map snd part2) -- here s is [a] (!!!) -- part3 has type [([a],a)] in map snd part1 ++ map snd part2 ++ map snd part3 test1 :: [Int] test1 = runFoo (\y -> [(y,2),(y,5)]) -- here a is Int test2 :: [Int] test2 = runFoo (\y -> [("abc" ++ y,2)]) -- ** error -- I can't choose y :: String, runFoo will choose that type!
Above, note that a fixed (before Int ) and that I cannot impose any type restrictions on y . Moreover:
test3 = runFoo (\y -> [(y,y)]) -- ** error
Here I am not fixing a in advance, but I'm trying to choose a=s . I am not allowed to do this: runFoo allowed to choose s in terms of a (see part3 above), so a needs to be fixed in advance.
Now, for your example. The problem is
runST (newSTRef ...)
Here newSTRef returns ST s (STRef s Int) , so it tries to select a = STRef s Int . Since a depends on s , this choice is not valid.
This "trick" is used by the ST monad to prevent references to the "escape" from the monad. That is, it is guaranteed that after runST all links now become inaccessible (and, possibly, they can be garbage collected). Because of this, the altered state that was used to calculate ST was selected, and the result of runST indeed a pure value. This is, after all, the main purpose of the ST monad: it is intended to resolve a (temporary) mutable state that should be used in pure computation.