So far in my blog posts, we have encountered Monads and have studied them, in order to understand how they behave and how they work. But we have not seen real examples of real Monads in the real professional programming world. We should start studying Monads that do something useful, that is, Monads that hide really useful computations inside them. State, IO, and other side effects are examples of computations that are needed in the real world of professional programming and that are not purely functional. Thus, we need Monads in order to hide these side effects and their deviation from the pure functionality.
Ok, let us start by studying state. Although everyone seems to agree on what state is, Haskell provides a Monad that handles state in a somewhat disappointing manner. The State Monad keeps state inside a computation, but after the computation is over, the state is no longer kept. In this blog post, we will study this disappointing State Monad.
As I mentioned, the State Monad s only useful inside a computation. In the program that follows, I provide examples of functions that return a State Monad. Inside each function, you can keep and read and change and advance state. When the function terminates, all this is lost. So, while you can use the State Monad in order to create temporary counters inside a computation, you cannot use the State Monad in order to create a stateful counter that you can advance between different calls.
Limiting and disappointing as the State Monad can be, it still packs great power. You see, the State Monad handles a state s and a value a. A value of type State s a is a function from initial state s to final value a and final state s: (a,s). What is really important is that the state and the value do not have to be simple entities. They can be tuples, for example. So, the state and value can hold a complex structure of entities, for example, a few or more counters. There is a “get” directive that sets the value equal to the state, a “put x” directive that sets the state equal to x, and a “return x” directive that sets the value to x. There are also functions that help in the use of the State Monad, like runState, evalState, and execState. runState takes an initial state and a seqeunce of actions and returns the final state. evalState and execState return one of the two values that runState returns: evalState returns the final result and execState returns the final state.
As far as bind (>>=) for the State Monad is concerned, it passes the value (as opposed to the state) of its first argument to its second argument.
The following program demonstrates the functionality of the State Monad. It can be broken into smaller independent programs, but I think it will be easier to get a full glimpse into the State Monad’s functionality this way.
In this program, I create different states (and their corresponding values). Each different state is created, referenced and updated in a single function, because this is all the State Monad functionality can do. The state ceases to exist once the function is finished. These concepts are demonstrated with the first four functions (1 to 4).
The last three functions (5 to 7 ) as well as the first function (1) are used to demonstrate the functionality of the bind (>>=) as it is programmed in the State Monad.
It will be very instructive for the reader to change the placement of the myState <- get instruction in the first function. Indeed, the first function can have more than one statement like that, or its placement can change and it can be placed from immediately after do, to immediately before the return statement. The effect of this change on the outputs that depend on the first and seventh functions can be seen by running the program.
module Main where import Control.Monad.State -- A value of type (State s a) is a function from initial state s to final value a and final state s: (a,s) -- "get": value <- state and state does not change -- "put x": value does not change and state <- x -- "return x": value <- x and state does not change -- evalState takes an initial state and returns a final value -- execState takes an initial state and returns a final state -- >>= passes the value (and not the state) of its first argument to its second argument -- (>>=) :: State s a -> (a -> State s b) -> State s b -- The state is the first String and the value is the second String myFunction1 :: State String String myFunction1 = do put "100" put "200" put "300" put "400" myState <- get put "5000" put "6000" put "7000" put "8000" return myState -- The state is the first Int and the value is the second Int myFunction2 :: State Int Int myFunction2 = do myState <- get return myState -- The state is the Int and the value is the String myFunction3 :: State Int String myFunction3 = do put 11 put 12 return "AAA" return "BBB" -- The state is the String and the value is the Int myFunction4 :: State String Int myFunction4 = do put "AAA" put "BBB" return 11 return 12 -- The state is the second String and the value is the Int myFunction5 :: String -> State String Int myFunction5 xs = do put xs return (length xs) -- The state is the String and the value is the Int myFunction6 :: State String Int myFunction6 = do put "50,000,000" let a = evalState ((return "4,000,000") >>= myFunction5) "300,000" let b = execState ((return "4,000,000") >>= myFunction5) "300,000" put b return a myFunction7 :: State String Int myFunction7 = do put "50,000,000" let a = evalState (myFunction1 >>= myFunction5) "300,000" let b = execState (myFunction1 >>= myFunction5) "300,000" put b return a main :: IO () main = do putStrLn "Program begins." putStrLn "Tests that study the behavior of the state." print (evalState myFunction1 "AAA") print (execState myFunction1 "AAA") print (evalState myFunction2 100) print (execState myFunction2 100) print (evalState myFunction3 100) print (execState myFunction3 200) print (evalState myFunction4 "100") print (execState myFunction4 "200") putStrLn "Tests that study the behavior as a Monad." print (evalState myFunction6 "1,000") print (execState myFunction6 "20,000") print (evalState myFunction7 "1,000") print (execState myFunction7 "20,000") putStrLn "Program ends."
The output of the previous program is:
Program begins. Tests that study the behavior of the state. "400" "8000" 100 100 "BBB" 12 12 "BBB" Tests that study the behavior as a Monad. 9 "4,000,000" 3 "400" Program ends.