views:

287

answers:

3

Hi,

I went through some tutorials on the State monad and I think I got the idea.

For example, as in this nice tutorial:

import Data.Word

type LCGState = Word32

lcg :: LCGState -> (Integer, LCGState)
lcg s0 = (output, s1) 
  where s1 = 1103515245 * s0 + 12345
        output = fromIntegral s1 * 2^16 `div` 2^32


getRandom :: State LCGState Integer
getRandom = get >>= \s0 -> let (x,s1) = lcg s0
                           in put s1 >> return x

OK, so I can use getRandom:

*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 1              
(16838,1103527590)

But I still need to pass the seed to the PRNG every time I call it. I know that the PRNG available in Haskell implementations does not need that:

Prelude> :module Random
Prelude Random> randomRIO (1,6 :: Int)
(...) -- GHC prints some stuff here
6
Prelude Random> randomRIO (1,6 :: Int)
1

So I probably misunderstood the State monad, because what I could see in most tutorials doesn't seem to be "persistent" state, but just a convenient way to thread state.

So... How can I have state that is automatically initialized (possible from some function that uses time and other not-very-predictable data), like the Random module does?

Thanks a lot!

+3  A: 
secretStateValue :: IORef SomeType
secretStateValue = unsafePerformIO $ newIORef initialState
{-# NOINLINE secretStateValue #-}

Now access your secretStateValue with normal readIORef and writeIORef, in the IO monad.

bdonlan
Evil, but it is the same trick that the standard `Random` module uses...
ephemient
Speaking of which, `Random` uses `atomicModifyIORef` instead of a `readIORef` + `writeIORef` pair; it seems like a good idea to do so.
ephemient
Indeed, if there's going to be accesses from multiple threads using atomicModifyIORef is a better idea.
bdonlan
+4  A: 

So I probably misunderstood the State monad, because what I could see in most tutorials doesn't seem to be "persistent" state, but just a convenient way to thread state.

The state monad is precisely about threading state through some scope.

If you want top level state, that's outside the language (and you'll have to use a global mutable variable). Note how this will likely complicated thread safety of your code -- how is that state initialized? and when? And by which thread?

Don Stewart
+4  A: 

randomRIO uses the IO monad. This seems to work nicely in the interpreter because the interpreter also works in the IO monad. That's what you are seeing in your example; you can't actually do that at the top-level in code -- you would have to put it in a do-expression like all monads anyway.

In general code you should avoid the IO monad, because once your code uses the IO monad, it is tied to external state forever -- you can't get out of it (i.e. if you have code that uses the IO monad, any code that calls it also has to use the IO monad; there is no safe way to "get out" of it). So the IO monad should only be used for things like accessing the external environment, things where it is absolutely required.

For things like local self-contained state, you should not use the IO monad. You can use the State monad as you have mentioned, or you can use the ST monad. The ST monad contains a lot of the same features as the IO monad; i.e. there is STRef mutable cells, analogous to IORef. And the nice thing about ST compared to IO is that when you are done, you can call runST on an ST monad to get the result of the computation out of the monad, which you can't do with IO.

As for "hiding" the state, that just comes as part of the syntax of do-expressions in Haskell for monads. If you think you need to explicitly pass the state, then you are not using the monad syntax correctly.

Here is code that uses IORef in the IO Monad:

import Data.IORef
foo :: IO Int -- this is stuck in the IO monad forever
foo = do x <- newIORef 1
         modifyIORef x (+ 2)
         readIORef x
-- foo is an IO computation that returns 3

Here is code that uses the ST monad:

import Control.Monad.ST
import Data.STRef
bar :: Int
bar = runST (do x <- newSTRef 1
                modifySTRef x (+ 2)
                readSTRef x)
-- bar == 3

The simplicity of the code is essentially the same; except that in the latter case we can get the value out of the monad, and in the former we can't without putting it inside another IO computation.

newacct
Thanks! That was very helpful.Regarding your comment, "in the former we can't without putting it inside another IO computation" -- I suppuse that's why the IO monad needs to be the innermost when you combine several monads (using monad transformers), then?
Jay
Now that I think of it, a PRNG would necessarily use the IO monad, right? It needs entropy, so it will need data from "somewhere". It actually *needs* at least one function that is not referentially transparent (otherwise it wouldn't be useful, for example, for cryptographic code)! :-)
Jay