2

This is an example from https://wiki.haskell.org/All_About_Monads It's an example of using State monad to thread the StdGen value through a sequence of random numbers generating commands. If I understand what the last return does correctly it should just create a new monad with x as a value. But then what does the put g' actually do? Why wouldn't the g' actually be lost?

getAny :: (Random a) => State StdGen a
getAny = do g <- get
            (x,g') <- return $ random g
            put g'
            return x
Gherman
  • 6,768
  • 10
  • 48
  • 75
  • This state computation can be composed with others, so it is not 'lost'. – pdexter Jun 19 '16 at 15:13
  • @pdexter, I mean being lost between `put g'` and `return x`. `return x` should creae a new instance without `g'` in it, right? Then where is `g'`? Maybe I understand what `return` does in the do-notation incorrectly? I thought it was just executing monad's `return` strategy. – Gherman Jun 19 '16 at 15:22
  • `State StdGen a` represents a computation with a state vale of type `StdGen`. Just pretend there's a mutable, imperative-style variable of type `StdGen`, which is readable with the `get` action and writable with the `put x` action (`x` being the new value). The `(x,g') <- return $ random g` is IMHO an anti-pattern which is best spelled `let (x,g') = random g`. – chi Jun 19 '16 at 15:57

2 Answers2

3

Let's suppose our state is being stored in a file. Here is what getAny is doing as expressed in a Javascript/Python-like language:

function getAny() {
  var g = readStateFromFile("some-path")  // the get call

  var xg  = random(g)    // returns an array with two elements
  var x = xg[0]
  var newg = xg[1]

  writeStateToFile("some-path", newg)  // same as the put function call
  return x
}

Here random(g) has to return two values, so I am having it return an array.

Now consider what happens during this sequence of calls:

 a = getAny()       same as:    do a <- getAny
 b = getAny()                      b <- getAny
 c = getAny()                      c <- getAny

for a = getAny():

  • the state is read from the file
  • it's given to random which returns two values
  • the second value is written to the file
  • the first value is returned and stored in the variable a

and then for b = getAny():

  • the state just written to the file is read back in
  • it is fed to random() producing a value and new state
  • the new state is written to the file
  • the new value is returned and stored in the variable b

etc...

Now to answer your questions:

what does the put g' actually do?

It updates the state with a new value.

Why wouldn't the g' actually be lost?

newg is just a local variable, so it's value would be lost unless we saved it somewhere.

ErikR
  • 51,541
  • 9
  • 73
  • 124
1

I think you are confused by

(x, g') <- return $ random g

This indeed create a new monadic action State StdGen (a, StdGen), which is executed to extract its result (a, StdGen).

The confusion arises with good reason, because the code is actually equivalent to

let (x, g') = random g

where no monadic action is built, leading to more straightforward code. This transformation is correct in any monad, not just the State one.

Anyway, the technical part: the (x, g') <- return $ random g snippet means

(x, g') <- State (\g'' -> (random g, g''))

where we can see that the monadic action takes the current state g'' (which has the same value as g), and then does not modify it (the (..., g'') part) while returning the generated value random g alongside it ((random g, ...) part).

This is a bit silly, since we do not even need to read g'' since we are using random g!

So, we are using

do g       <- State (\g'' -> (g'', g''))
   (x, g') <- State (\g'' -> (random g, g''))
   ...

when we could instead use

do (x, g') <- State (\g'' -> (random g'', g''))
   ...

which is called in the library

do (x, g') <- gets random
   ...

Okay, the confusion appears to be in do put g' ; return x. This is desugared into bind-notation as follows

{ definitions }
put g'   = State $ \s -> ((), g')
return x = State $ \s -> (x , s )

do put g ; return x 
= { definitions, desugaring }
   (State $ \s -> ((), g'))
   >>= 
   (\_ -> State $ \s -> (x , s ))
= { definition of >>= }
   State $ \s -> let (v,s') = (\s -> ((), g')) s 
                 in runState ((\_ -> State $ \s -> (x , s )) v) s'
= { beta-reduction (application) }
   State $ \s -> let (v,s') = ((), g')
                 in runState (State $ \s -> (x , s )) s'
= { beta-reduction (let) }
   State $ \s -> runState (State $ \s -> (x , s )) g'
= { runState (State y) = y }
   State $ \s -> (\s -> (x , s )) g'
= { beta-reduction }
   State $ \s -> (x , g')

So, the effect of do put g' ; return x is to modify the state to g' (overwriting the previous one s) and to yield x as a final value of the computation (alongside g').

chi
  • 111,837
  • 3
  • 133
  • 218
  • I am sorry for being not clear enough. By return I ment the last line. I understand what the first two lines do. Thanks for this improvement too though. That last `return x` should create a new State monad with no `g'` in it, right? – Gherman Jun 19 '16 at 16:12
  • @German Note that a `State StdGen a` has no state inside, but is a state _modifier_, roughly a function `\g -> (someValueDependingOn g, newG g)`. So the `return x` indeed does not involve `g'` _but_ will get composed by the `do` notation with the `put` and the rest above, so that the whole `do` block is something like `\g -> (x, g')`, where `x,g'` are computed in terms of `g`. So `g'` is _not_ lost: the last return does not cancel the effects of the previous actions. – chi Jun 19 '16 at 16:17
  • So the `return` in the do-notation actually is not the monads's `return` strategy but rather it also calls monad's bind strategy? I thought those returns are the same. – Gherman Jun 19 '16 at 16:22
  • The return is indeed the return of the monad. But the `do a;b;c;d` uses bind to connect `a,b,c,d`. If `d` is return, `a,b,c` still matter. Note that according to the monad laws, `do x <- action; return x` is equivalent to `action` -- the return has no effect, but the action is still relevant. – chi Jun 19 '16 at 16:25
  • Which means it's both strategies. Thank you! If you make these two comments into an answer, I can accept it. – Gherman Jun 19 '16 at 16:37