Here’s the ST
API:
data ST s a
runST :: (forall s. ST s a) -> a
newSTRef :: a -> ST s (STRef s a)
readSTRef :: STRef s a -> ST s a
writeSTRef :: STRef s a -> a -> ST s ()
Let’s drop forall s
and see what happens:
data ST s a
runST :: ST s a -> a
newSTRef :: a -> ST s (STRef s a)
readSTRef :: STRef s a -> ST s a
writeSTRef :: STRef s a -> a -> ST s ()
Well, now ST
can be eliminated by calling runST
after any other operation (e.g. runST . newSTRef
):
newSTRef :: a -> STRef s a
readSTRef :: STRef s a -> a
writeSTRef :: STRef s a -> a -> ()
At the type level, writeSTRef
looks useless, but it still has the side effect of writing a value to an STRef
.
Now the order of evaluation matters, and purity goes out the window:
let ref = newSTRef "1"
_ = writeSTRef ref "2"
in readSTRef ref -- Does this evaluate to "1" or "2"?
forall s
is the linchpin that enforces sequential reads/writes in the ST
monad.