Is MutVar# impure in Haskell?

104 views Asked by At

Now I'm currently struggling understanding State ST STRef etc..

First, I investigated Monad itself, s -> (s, a).

I failed to get any meaningful relation between s -> (s, a) and "State" , so I went through newSTRef, readSTRef, writeSTRef.

They are based on MutVar#.

Okay, Haskell doesn't have any "variables". So, some kind of State or ST things would be designed to help Haskell have "variable - like" things while maintaining "PURENESS".

Which derives to, MutVar# and its corresponding APIs should be pure too.

But it seems not.

Look what I've got.

fun :: State# RealWorld -> (Int, Int, Int)
fun s# = let (# a#, var# #) :: (# State# RealWorld, MutVar# RealWorld Int #) = newMutVar# 0 s#
             (# b#, o1 #) :: (# State# RealWorld, Int #) = readMutVar# var# s#
             c# = writeMutVar# var# 10 a#
             (# d#, o2 #) :: (# State# RealWorld, Int #) = readMutVar# var# s#
             e# = writeMutVar# var# 20 c#
             (# f#, o3 #) :: (# State# RealWorld, Int #) = readMutVar# var# s# in
             (o1, o2, o3)

when we put realWorld# into fun, we get (0, 10, 20).

For the same input applied by readMutVar#, (#var, #s), we get different result.

Is it impure??

1

There are 1 answers

4
chi On

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.