0

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??

kwonryul
  • 481
  • 3
  • 10
  • 2
    No, `MutVar` is *not* impure: it does not hold the *value* of the reference, but the reference itself. – Willem Van Onsem Apr 03 '23 at 10:08
  • 4
    Note there are important differences between `State` and `ST` -- see, for instance, [*How to understand the state type in Haskell's ST monad*](https://stackoverflow.com/q/34494893/2751851). Also, `State#` and `MutVar#` are not part of the safe `ST` API offered by `Control.Monad.ST` and `Data.STRef`. – duplode Apr 03 '23 at 10:52
  • 2
    The main assumption of _State / ST would be designed to help Haskell have "variable - like" things_ is wrong. It is a common mistake to think the `State` monad is a hacky way to have mutable state in Haskell. It isn't true. `State` monad is a regular pure function. It happens that its type makes it a good candidate to implement the monad interface and have a "mutable state" user experience. But it is just that, the user feels it like a mutable state. With respect the `ST` monad, you do have an underliying mutable variable, but the exposed interface is still pure – lsmor Apr 03 '23 at 14:11

1 Answers1

2

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.

chi
  • 111,837
  • 3
  • 133
  • 218
  • Thank you for your detailed and sincere response – kwonryul Apr 03 '23 at 13:45
  • After getting a good reply, I got another question. Does ```ST s``` monad act like ```IO``` monad? They all seem to wrap impure(? or maybe raw) codes into pure codes. – kwonryul Apr 03 '23 at 14:13
  • 1
    @kwonryul Yes, there are some similarities between `ST s` and `IO`. Very roughly, you can think of `ST s` being a clone of `IO`, with its own primitives for mutable state (using references), its own interface, no input/ouput, and (most importantly) a way to escape from the monad through the `runST` function. A function `f :: Int -> Int` can internally use `runST` and run a stateful `ST s` computation -- the type system guarantees `f` is a pure function. `f` can not use `IO` in a similar way. – chi Apr 03 '23 at 14:28
  • Thanks. Have a nice day :). It really helps me further studying Haskell – kwonryul Apr 03 '23 at 14:35