Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One more time...can I have an example of state monad that does what I want?

I'm trying to understand the actual need for the reader and/or state monads. All the examples I've seen (including many on stackoverflow as I've hunted for suitable examples I can use and in various books and blog articles) are of the form (pseudo code)

 f = do
        foo <- ask
        do something with foo

 g = do
        foo <- ask
        do something else using foo

 h = runReader
       (
           f
           g
       )

In other words, call two functions and (presumably) maintain some state from one call to the next. However, I don't find this example particularly convincing as (I think) I could just have f return some state and then pass that state on to g.

I'd love to see an example, using a single integer (say) as the state to be preserved where, rather than two sequential calls to f and then g from a central place, rather there's a call to f which then internally calls g and then have changed state (if state monad) available back in the main routine.

Most (well actually all) the examples I have seen spend a tremendous amount of time focusing on the definition of the monad and then show how to set up a single function call. To me, it would be the ability to do nested calls and have the state carried along for the ride to demonstrate why it's useful.

like image 267
David Avatar asked Jul 04 '14 15:07

David


People also ask

What is a state Monad?

The state monad is a built in monad in Haskell that allows for chaining of a state variable (which may be arbitrarily complex) through a series of function calls, to simulate stateful code.

How does the state Monad work?

The State Monad This context chains two operations together in an intuitive way. First, it determines what the state should be after the first operation. Then, it resolves the second operation with the new state. So for our Tic Tac Toe game, many of our functions will have a signature like State GameState a .

What is the reader Monad?

The Reader monad (also called the Environment monad). Represents a computation, which can read values from a shared environment, pass values from function to function, and execute sub-computations in a modified environment. Using Reader monad for such computations is often clearer and easier than using the State monad.

What is Haskell state?

The Haskell type State describes functions that consume a state and produce both a result and an updated state, which are given back in a tuple. The state function is wrapped by a data type definition which comes along with a runState accessor so that pattern matching becomes unnecessary.


3 Answers

Here's a non-trivial example of a stateful subroutine calling another stateful subroutine.

import Control.Monad.Trans.State

f :: State Int ()
f = do
    r <- g
    modify (+ r)

g :: State Int Int
g = do
    modify (+ 1)
    get

main = print (execState f 4)

In this example, the initial state begins at 4 and the stateful computation begins at f. f internally calls g, which increments the state to 5 and then returns the current state (still 5). This restores control to f, which binds the value 5 to r and then increments the current state by r, giving a final state of 10:

>>> main
10
like image 158
Gabriella Gonzalez Avatar answered Nov 15 '22 06:11

Gabriella Gonzalez


Almost everything you can do with monads you can do without them. (Well, some are special like ST, STM, IO, etc., but that's a different story.) But:

  • they allow you to encapsulate many common patterns, like in this case stateful computations, and hide details or boiler-plate code that would be otherwise needed; and
  • there are plethora of functions that work on any (or many) monads, which you can just specialize for a particular monad you're using.

To give an example: Often one needs to have some kind of a generator that supplies unique names, like when generating code etc. This can be easily accomplished using the state monad: Each time newName is called, it outputs a new name and increments the internal state:

import Control.Monad.State
import Data.Tree
import qualified Data.Traversable as T

type NameGen = State Int

newName :: NameGen String
newName = state $ \i -> ("x" ++ show i, i + 1)

Now let's say we have a tree that has some missing values. We'd like to supply them with such generated names. Fortunately, there is a generic function mapM that allows to traverse any traversable structure with any monad (without the monad abstraction, we wouldn't have this function). Now fixing the tree is easy. For each value we check if it's filled (then we just use return to lift it into the monad), and if not, supply a new name:

fillTree :: Tree (Maybe String) -> NameGen (Tree String)
fillTree = T.mapM (maybe newName return)

Just imagine implementing this function without monads, with explicit state - going manually through the tree and carrying the state around. The original idea would be completely lost in boilerplate code. Moreover, the function would be very specific to Tree and NameGen.

But with monads, we can go even further. We could parametrize the name generator and construct even more generic function:

fillTreeM :: (Monad m) => m String -> Tree (Maybe String) -> m (Tree String)
fillTreeM gen = T.mapM (maybe gen return)

Note the first parameter m String. It's not a constant String value, it's a recipe for generating a new String within m, whenever it's needed.

Then the original one can be rewritten just as

fillTree' :: Tree (Maybe String) -> NameGen (Tree String)
fillTree' = fillTreeM newName

But now we can use the same function for many very different purposes. For example, use the Rand monad and supply randomly generated names.

Or, at some point we might consider a tree without filled out nodes invalid. Then we just say that wherever we're asked for a new name, we instead abort the whole computation. This can be implemented just as

checkTree :: Tree (Maybe String) -> Maybe (Tree String)
checkTree = fillTreeM Nothing

where Nothing here is of type Maybe String, which, instead of trying to generate a new name, aborts the computation within the Maybe monad.

This level of abstraction would be hardly possible without having the concept of monads.

like image 44
Petr Avatar answered Nov 15 '22 07:11

Petr


I'm trying to understand the actual need for the reader and/or state monads.

There are many ways to understand monads in general, and these monads in particular. In this answer, I focus on one understanding of these monads which I believe might be helpful for the OP.

The reader and state monads provide library support for very simple usage patterns:

  • The reader monad provides support for passing arguments to functions.
  • The state monad provides support for getting results out of functions and passing them to other functions.

As the OP correctly figured out, there is no great need for library support for these things, which are already very easy in Haskell. So many Haskell programs could use a reader or state monad, but there's no point in doing so, so they don't.

So why would anyone ever use a reader or state monad? I know three important reasons:

  1. Realistic programs contain many functions that call each other and pass information back and forth. Sometimes, many functions take arguments that are just passed on to other functions. The reader monad is a library for this "accept arguments and pass them on" pattern. The state monad is a library for the similar "accept arguments, pass them on, and pass the results back as my result" pattern.

    In this situation, a benefit of using the reader or state monad is that the arguments get passed on automatically, and we can focus on the more interesting jobs of these functions. A cost is that we have to use monadic style (do notation etc.).

  2. Realistic programs can use multiple monads at once. They need arguments that are passed on, arguments that are returned, error handling, nondeterminism, ...

    In this situation, a benefit of using the reader or state monad transformer is that we can package all of these monads into a single monad transformer stack. We still need monadic style, but now we pay the cost once (use do notation everywhere) and get the benefit often (multiple monads in the transformer stack).

  3. Some library functions work for arbitrary monads. For example, the sequence :: [m a] -> m [a] takes a list of monadic actions, runs all of them in sequence, and returns the collected result.

    A benefit of using the reader or state (or whatever) monad is that we can use these very generic library functions that work for any monad.

Note that points 1 and 2 only show up in realistic, somewhat large programs. So it is hard to give a small example for this benefit of using monads. Point 3 shows up in small library functions, but is harder to understand, because these library functions are often very abstract.

like image 30
Toxaris Avatar answered Nov 15 '22 07:11

Toxaris