5

I'm trying so hard to wrap my head around the State Monad, and I do not understand the following:

Given the implementation of return and (>>=), when you say State $ \s ->...., where does s come from? I mean, when you start performing >>= ... >>=, doesn't it mean that somewhere in your beginning of the chain you somehow have to provide for that initial parameter?

newtype State s a=State { runState::s->(a,s) }

 instance Monad (State s) where
        return a=State $ \s->(a,s)
        (>>=) m g=State $ \s -> let (a,s')= runState m s in
                                   runState (g a) s'

In (>>=) you say State $ \s -> runState m s, and I do not get when is that initial (\s -> ...) argument (with a REAL argument) called?

Can someone explain, please?

Later Edit:

Can someone show me how would the initial state be set, let's say if it needs to get a value using getLine?

main::IO()
main=do
  argument<-getLine
  --how do i set initial state with argument?
  m >> f1 >> f2 >> f3
4
  • 1
    It is the initial state, so one creates functions where the state is passed (and sometimes modified) and passed to the next state. Commented May 20, 2019 at 13:18
  • 1
    We thus take two states, and create a new State. A State is in fact not the sate, but a change of state. We thus create a new change of state that first passes s through the first one, and the result (a, s') contains the modified state s' that is then passed to the next one. Commented May 20, 2019 at 13:20
  • 4
    Think of a State s a value as a "state transformer", a machine which can modify the state, when fed with one, and produce a value of type a in the process. The concrete, real, initial state is passed when one finally decides to run the monadic computation using runState myStateValue myInitialState. This is a typical workflow with monads: you use a lot of >>=/return to build a value of type M a, and at the very last step you use runM to turn that M a into some usable type, breaking the monadic abstraction.
    – chi
    Commented May 20, 2019 at 13:54
  • 1
    you write let x = runState st argument inside your IO do block, or runState (do { x <- get ; return (x+1) }) 0. but that is a new question.
    – Will Ness
    Commented May 20, 2019 at 14:11

3 Answers 3

5

when you say State $ \s ->...., where does s come from ?

It will come from the invocation, when runState will supply the initial state value to the state-monadic value, to run the combined computation it describes:

st = do { x <- get ; return (x+1) }

x = runState st 0    -- x is (1,0)

I also sense another possible misunderstanding on your part: you write: "when is that initial (\s -> ...) argument called?" There's no "initial" lambda: the lambdas are all nested inside!

do { a <- as; b <- bs; c <- foo b; return c }

translates as

as >>= (\a -> bs >>= (\b -> foo b >>= (\c -> return c)))

so it's not "initial", that's one combined all-enclosing lambda that is called with the initial state!

And then it will call

let (a,s1) = runState as s0

etc. with that "initial" as in the do block.

2

the do block does not perform any stateful computation - it only assembles some smaller stateful computations into one bigger stateful computation. At the do level, the actual state does not exist.

It would be simpler and maybe even more accurate if the monad was called "a stateful computation". Or "a function that takes state of type S and returns another state of the same type alongside its actual result". Then you could imagine >>= as "combines two functions of the aforementioned type into one, such that the state returned by the first one is be passed as a parameter to the second one".

7
  • 3
    my preferred wording is, "a do block does not perform, it describes a computation to be performed." :)
    – Will Ness
    Commented May 20, 2019 at 13:51
  • @WillNess do is not different than any other function :) Would you say that find "describes finding a value in a foldable" or would you say that find "finds a value in a foldable"? :-)
    – fdreger
    Commented May 20, 2019 at 14:03
  • 1
    do is quite different from any other function, namely it is not a function at all.
    – chepner
    Commented May 20, 2019 at 14:12
  • find's source code describes finding a value in a Foldable indeed; trouble is it's not a Haskell value at all. Each line in a do block (the portion to the right of <-, if present) is a Haskell value. that's the difference.
    – Will Ness
    Commented May 20, 2019 at 14:17
  • 1
    @ReinHenrichs "find's source code is not a Haskell value" is what I meant.
    – Will Ness
    Commented May 21, 2019 at 4:21
1

State is just a wrapper around functions of type s -> (a, s). runState doesn't actually "run" anything; it just gives back the function wrapped by the State constructor. You can, however, compare runState to the ($) operator for functions.

($)             f  x = f x
runState (State f) s = f s

That makes (=<<) = flip (>>=) similar to (<<<) = (.); just rather than taking two functions and returning a third function, it takes a function (that returns a State) and a State and produces a second State.

However, we'll make a direct comparison of (>>=) to (>>>) = flip (.) so that the types align better. (Similarly, you could compare (.) to (=<<).)

-- f :: t -> a
-- g ::      a -> b
f >>> g =         \t -> let a       = ($)      f     t
                        in            ($)      g     a
-- m :: State s a
-- g ::         a -> State s b
m >>= g = State $ \s -> let (a, s') = runState m     s
                        in            runState (g a) s'

Not the answer you're looking for? Browse other questions tagged or ask your own question.