1- # import " ../stdlib.typ" : info , warning , solution
1+ # import " ../stdlib.typ" : info , warning , exercise , solution
22== The State Monad
33<sec:monad:state>
44
55
6- [ `cats.data.State` ][cats.data.State]
6+ `cats.data.State`
77allows us to pass additional state around as part of a computation.
88We define `State` instances representing atomic state operations
99and thread them together using `map` and `flatMap` .
1010In this way we can model mutable state in a purely functional way,
1111without using actual mutation.
1212
13- === Creating and Unpacking State
1413
14+ === Creating and Unpacking State
1515
1616Boiled down to their simplest form,
1717instances of `State[S, A]` represent functions of type `S => (S, A)` .
1818`S` is the type of the state and `A` is the type of the result.
19+ In the example below, the state has type `Int` ,
20+ and we return a `String` result computed from the state.
1921
20- ```scala mdoc:silent
22+ ```scala mdoc
2123import cats.data.State
22- ```
2324
24- ```scala mdoc:silent
2525val a = State[Int, String]{ state =>
2626 (state, s"The state is $state")
2727}
2828```
2929
30+ # info (title : [State and IndexedStateT])[
31+ You may have noticed the console output
32+ reports a type of `IndexedStateT`
33+ when we create an instance of `State` .
34+ Just like with `Writer` and `Reader` ,
35+ `State` is defined as a type alias of a more complicated type `IndexedStateT` :
36+
37+ ```scala
38+ type State[S, A] = IndexedStateT[Eval, S, S, A]
39+ ```
40+
41+ We can ignore this more complicated type.
42+ ]
43+
3044In other words, an instance of `State` is a function
3145that does two things:
3246
@@ -51,22 +65,16 @@ val justTheState = a.runS(10).value
5165val justTheResult = a.runA(10).value
5266```
5367
54- === Composing and Transforming State
5568
69+ === Composing and Transforming State
5670
5771As we've seen with `Reader` and `Writer` ,
5872the power of the `State` monad comes from combining instances.
5973The `map` and `flatMap` methods thread the state from one instance to another.
6074Each individual instance represents an atomic state transformation,
6175and their combination represents a complete sequence of changes:
6276
63- ```scala mdoc:invisible:reset-object
64- import cats.data.State
65- val a = State[Int, String]{ state =>
66- (state, s"The state is $state")
67- }
68- ```
69- ```scala mdoc:silent
77+ ```scala mdoc:silent:nest
7078val step1 = State[Int, String]{ num =>
7179 val ans = num + 1
7280 (ans, s"Result of step1: $ans")
@@ -83,15 +91,15 @@ val both = for {
8391} yield (a, b)
8492```
8593
94+ When we run this program
95+ we get the result of applying each step in sequence.
96+ State is threaded from step to step
97+ even though we don't interact with it in the for comprehension.
98+
8699```scala mdoc
87100val (state, result) = both.run(20).value
88101```
89102
90- As you can see, in this example the final state
91- is the result of applying both transformations in sequence.
92- State is threaded from step to step
93- even though we don't interact with it in the for comprehension.
94-
95103The general model for using the `State` monad
96104is to represent each step of a computation as an instance
97105and compose the steps using the standard monad operators.
@@ -104,45 +112,43 @@ Cats provides several convenience constructors for creating primitive steps:
104112 - `modify` updates the state using an update function.
105113
106114```scala mdoc
107- val getDemo = State.get[Int]
108- getDemo.run(10).value
115+ State.get[Int].run(10).value
109116
110- val setDemo = State.set[Int](30)
111- setDemo.run(10).value
117+ State.set[Int](30).run(10).value
112118
113- val pureDemo = State.pure[Int, String]("Result")
114- pureDemo.run(10).value
119+ State.pure[Int, String]("Result").run(10).value
115120
116- val inspectDemo = State.inspect[Int, String](x => s"${x}!")
117- inspectDemo.run(10).value
121+ State.inspect[Int, String](x => s"${x}!").run(10).value
118122
119- val modifyDemo = State.modify[Int](_ + 1)
120- modifyDemo.run(10).value
123+ State.modify[Int](_ + 1).run(10).value
121124```
122125
123126We can assemble these building blocks using a for comprehension.
124127We typically ignore the result of intermediate stages
125- that only represent transformations on the state:
128+ that only represent transformations on the state.
126129
127- ```scala mdoc:silent:reset-object
130+ ```scala mdoc:silent:nest
128131import cats.data.State
129- import State._
130- ```
132+ import State.*
131133
132- ```scala mdoc
133134val program: State[Int, (Int, Int, Int)] = for {
134135 a <- get[Int]
135136 _ <- set[Int](a + 1)
136137 b <- get[Int]
137138 _ <- modify[Int](_ + 1)
138139 c <- inspect[Int, Int](_ * 1000)
139140} yield (a, b, c)
141+ ```
140142
143+ As we expect,
144+ the result is the composition of the individual stages.
145+
146+ ```scala mdoc
141147val (state, result) = program.run(1).value
142148```
143149
144- === Exercise: Post-Order Calculator
145150
151+ # exercise ([Post-Order Calculator])
146152
147153The `State` monad allows us to implement
148154simple interpreters for complex expressions,
0 commit comments