STRef RealWold vs IORef

I know the s of ST S a can’t escape from the ST Monad, unless it’s s ~ RealWord. You can then run it as IO using stToIO. So for example one can (in ghci)

:m + Control.Monad.ST Data.STRef

ref <- stToIO $ newSTRef (1 :: Int)

stToIO $ modifySTRef ref (+1)

stToIO $ readSTRef ref
>> 2

So is STRef RealdWorld and IORef equivalent or I am missing something ?

STRef RealWorld and IORef are both backed by a primitive MutVar# plus a shared pool of primitive operations, so their runtime behavior is indentical.

There’s also ioToST via which ST RealWorld and IO are coercible. So as far as GHC is concerned, the two are represented identically, but that is far from a precise specification of how the semantics interacts with ambient IO.

This means that the whole "ST s is pure" story is treated a bit fast and loose by GHC. For example, it currently assumes that f :: ST s a -> ST s a does not throw precise exceptions, but of course that’s untrue for stToIo $ f $ ioToST $ throwIO (mkUserError "sdflkj"): #24263: Precise exceptions: `stToIO` and `ioToST` can circumvent analysis in Note [Which scrutinees may throw precise exceptions] · Issues · Glasgow Haskell Compiler / GHC · GitLab So far, nobody has complained about this uncompositional treatment and fixing it would either necessitate a complicated analysis and/or introduce quite a few perf regressions to existing code.

1 Like

I’ve raised a related issue with the CLC, but I don’t personally have much stake in this problem:

TL;DR: ST actions are expected to be run in a single thread, but stToIO can break that expectation.

2 Likes

Wow, what a hornet’s nest.

…so the original intention was that STRef RealdWorld and IORef would be equivalent. But having just seen @jaror’s proposal, @sgraf’s one-line summary seems appropriate:

Wow, what a hornet’s nest.

So much for “progress” !

2 Likes