Haskell is a pure language, which is however ultimately run on "impure" hardware, after a sequence of intermediate translations. This means that if you take any Haskell code and start observing its implementation, digging towards lower and lower levels, you'll eventually find something "impure".
The type State s a
and its related functions are pure. Going down, the implementation of State s a
is based on the pure type s -> (a,s)
, and that does not immediately reveal any impurity. But if we dig further, and try to observe how ->
and (,)
is implemented, we then meet impure code and the actual bare-metal bytes, pointers, and registers.
Now, since the intermediate representation s -> (a,s)
is still pure, we consider that part of Haskell. It is indeed an interface which the library user can access, and such interface is exposed clearly in the documentation.
The type ST s a
and its related functions are also pure. However, if we look at its implementation we immediately see that it is implemented on top of impure primitives like RealWorld
. The implementation code might look as Haskell, but it's not really Haskell. It is fragile, tricky, hackish, non portable code that the developers of GHC use because they know it's going to be compiled in the right way on that specific version of that specific compiler. This is not exposed as the library interface, and indeed is not mentioned in the library docs.
If the user programs in Haskell, using the exposed library interfaces only, the user is guaranteed to work in a pure environment. However, if the user tries to circumvent the safety protections, accessing the low-level unsafe primitives though modules that not meant to be used except by (roughly) the compiler developers to implement safe interfaces, the user loses those guarantees.
Misusing RealWorld
can lead to unpredictable behavior. We could, in principle, write code to save the current world, activate a mechanical arm to punch the user in the face, and then restore the saved world. In reality, the face of the user will not be magically healed here. The compiler developers are careful not to misuse RealWorld
in this way. Regular users should avoid RealWorld
entirely, sticking to pure code (i.e., to Haskell).
If you are learning about State
and ST
, I would advise against reading how they are implemented. That will expose complex low-level details you should completely ignore. Instead, try looking for tutorials, or examples, and learn how those monads are used in practice.