Can someone show a simple example where state monad can be better than passing state directly?
bar1 (Foo x) = Foo (x + 1)
vs
bar2 :: State Foo Foo
bar2 = do
modify (\(Foo x) -> Foo (x + 1))
get
Can someone show a simple example where state monad can be better than passing state directly?
bar1 (Foo x) = Foo (x + 1)
vs
bar2 :: State Foo Foo
bar2 = do
modify (\(Foo x) -> Foo (x + 1))
get
As an example to my comment above, you can write code using the
State
monad likeNote that using additional operators from
Control.Lens
also lets you writeincrCnt
andlogMsg
aswhich is another benefit of using
State
in combination with thelens
library, but for the sake of comparison I'm not using them in this example. To write the equivalent code above with just argument passing it would look more likeAt this point it isn't too bad, but once we get to the next step I think you'll see where the code duplication really comes in:
Another benefit of wrapping this up in a
Monad
instance is that you can use the full power ofControl.Monad
andControl.Applicative
with it:Which allows for much more flexibility when processing values calculated at runtime compared to static values.
The difference between manual state passing and using the
State
monad is simply that theState
monad is an abstraction over the manual process. It also happens to fit several other widely used more general abstractions, likeMonad
,Applicative
,Functor
, and a few others. If you also use theStateT
transformer then you can compose these operations with other monads, such asIO
. Can you do all of this withoutState
andStateT
? Of course you can, and there's no one stopping you from doing so, but the point is thatState
abstracts this pattern out and gives you access to a huge toolbox of more general tools. Also, a small modification to the types above makes the same functions work in multiple contexts:These will now work with
App
, or withStateT MyState IO
, or any other monad stack with aMonadState
implementation. It makes it significantly more reusable than simple argument passing, which is only possible through the abstraction that isStateT
.In my experience, the point of many Monads doesn't really click until you get into larger examples, so here is an example use of
State
(well,StateT ... IO
) to parse an incoming request to a web service.The pattern is that this web service can be called with a bunch of options of different types, though all except for one of the options have decent defaults. If I get a incoming JSON request with an unknown key value, I should abort with an appropriate message. I use the state to keep track of what the current config is, and what the remainder of the JSON request is, along with a bunch of accessor methods.
(Based on code currently in production, with the names of everything changed and the details of what this service actually does obscured)
State passing is often tedious, error-prone, and hinders refactoring. For example, try labeling a binary tree or rose tree in postorder:
Here I had to manually label states in the right order, pass the correct states along, and had to make sure that both the labels and child nodes are in the right order in the result (note that naive use of
foldr
orfoldl
for the child nodes could have easily led to incorrect behavior).Also, if I try to change the code to preorder, I have to make changes that are easy to get wrong:
Examples:
Contrast the state monad solution:
Not only is this code more succinct and easier to write correctly, the logic that results in pre- or postorder labeling is far more transparent.
PS: bonus applicative style: