prop_check_rs/state.rs
1use std::fmt::{Debug, Formatter};
2use std::rc::Rc;
3
4/// The `State` monad represents a stateful computation.
5pub struct State<S, A> {
6 pub(crate) run_f: Rc<dyn Fn(S) -> (A, S)>,
7}
8
9impl<S, A> Debug for State<S, A> {
10 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
11 write!(f, "Fn")
12 }
13}
14
15impl<S, A> Clone for State<S, A>
16where
17 S: 'static,
18 A: Clone + 'static,
19{
20 fn clone(&self) -> Self {
21 Self {
22 run_f: self.run_f.clone(),
23 }
24 }
25}
26
27impl<S, A> State<S, A>
28where
29 S: 'static,
30 A: Clone + 'static,
31{
32 /// Creates a new State with a constant value.
33 ///
34 /// This method creates a State that, when run, will return the provided value
35 /// and the unchanged state.
36 ///
37 /// # Arguments
38 /// - `a` - The value to be returned by the State
39 ///
40 /// # Returns
41 /// - `State<S, A>` - A new State that returns the provided value
42 ///
43 /// # Examples
44 /// ```
45 /// use prop_check_rs::state::State;
46 /// let state = State::<i32, String>::value("hello".to_string());
47 /// let (value, new_state) = state.run(42);
48 /// assert_eq!(value, "hello");
49 /// assert_eq!(new_state, 42); // State is unchanged
50 /// ```
51 pub fn value(a: A) -> State<S, A> {
52 Self::new(move |s| (a.clone(), s))
53 }
54
55 /// Creates a new State with a custom state transformation function.
56 ///
57 /// This is the core constructor for State, allowing you to define exactly how
58 /// the state should be transformed and what value should be produced.
59 ///
60 /// # Arguments
61 /// - `f` - A function that takes a state and returns a tuple of (value, new_state)
62 ///
63 /// # Returns
64 /// - `State<T, B>` - A new State that applies the provided function
65 ///
66 /// # Type Parameters
67 /// - `T` - The type of the state
68 /// - `B` - The type of the value produced
69 /// - `F` - The type of the function
70 pub fn new<T, B, F>(f: F) -> State<T, B>
71 where
72 F: Fn(T) -> (B, T) + 'static, {
73 State { run_f: Rc::new(f) }
74 }
75
76 /// Creates a new State with a constant value (alias for value).
77 ///
78 /// This method is functionally identical to `value` but is named to align with
79 /// the functional programming concept of "pure" or "return".
80 ///
81 /// # Arguments
82 /// - `b` - The value to be returned by the State
83 ///
84 /// # Returns
85 /// - `State<S, B>` - A new State that returns the provided value
86 ///
87 /// # Type Parameters
88 /// - `B` - The type of the value to be returned
89 pub fn pure<B>(b: B) -> State<S, B>
90 where
91 B: Clone + 'static, {
92 Self::new(move |s| (b.clone(), s))
93 }
94
95 /// Executes the State with the provided initial state.
96 ///
97 /// This method runs the State computation with the given state and returns
98 /// both the resulting value and the final state.
99 ///
100 /// # Arguments
101 /// - `s` - The initial state
102 ///
103 /// # Returns
104 /// - `(A, S)` - A tuple containing the resulting value and the final state
105 pub fn run(self, s: S) -> (A, S) {
106 (self.run_f)(s)
107 }
108
109 /// Transforms the value produced by this State using a function.
110 ///
111 /// This method allows you to transform the value produced by a State without
112 /// affecting how the state itself is transformed.
113 ///
114 /// # Arguments
115 /// - `f` - A function that transforms the value
116 ///
117 /// # Returns
118 /// - `State<S, B>` - A new State that produces the transformed value
119 ///
120 /// # Type Parameters
121 /// - `B` - The type of the transformed value
122 /// - `F` - The type of the transformation function
123 pub fn map<B, F>(self, f: F) -> State<S, B>
124 where
125 F: Fn(A) -> B + 'static,
126 B: Clone + 'static, {
127 self.flat_map(move |a| Self::pure(f(a)))
128 }
129
130 /// Chains this State with a function that returns another State.
131 ///
132 /// This method allows for sequential composition of stateful computations.
133 /// The function `f` is applied to the value produced by this State, and the
134 /// resulting State is then run with the updated state.
135 ///
136 /// # Arguments
137 /// - `f` - A function that takes the value from this State and returns a new State
138 ///
139 /// # Returns
140 /// - `State<S, B>` - A new State representing the sequential composition
141 ///
142 /// # Type Parameters
143 /// - `B` - The type of the value produced by the resulting State
144 /// - `F` - The type of the function
145 pub fn flat_map<B, F>(self, f: F) -> State<S, B>
146 where
147 F: Fn(A) -> State<S, B> + 'static,
148 B: Clone + 'static, {
149 State::<S, B>::new(move |s| {
150 let (a, s1) = self.clone().run(s);
151 f(a).run(s1)
152 })
153 }
154
155 /// Combines this State with another State, producing both values.
156 ///
157 /// This method runs this State, then runs the provided State with the
158 /// updated state, and returns both values as a tuple.
159 ///
160 /// # Arguments
161 /// - `sb` - Another State to run after this one
162 ///
163 /// # Returns
164 /// - `State<S, (A, B)>` - A new State that produces both values as a tuple
165 ///
166 /// # Type Parameters
167 /// - `B` - The type of the value produced by the second State
168 pub fn and_then<B>(self, sb: State<S, B>) -> State<S, (A, B)>
169 where
170 A: Clone,
171 B: Clone + 'static, {
172 self.flat_map(move |a| sb.clone().flat_map(move |b| Self::pure((a.clone(), b))))
173 }
174
175 /// Creates a State that returns the current state as its value.
176 ///
177 /// This method is useful for accessing the current state without modifying it.
178 ///
179 /// # Returns
180 /// - `State<T, T>` - A State that returns the current state as its value
181 ///
182 /// # Type Parameters
183 /// - `T` - The type of the state
184 pub fn get<T>() -> State<T, T>
185 where
186 T: Clone, {
187 Self::new(move |t: T| (t.clone(), t))
188 }
189
190 /// Creates a State that replaces the current state with a new value.
191 ///
192 /// This method is useful for setting the state to a specific value,
193 /// regardless of its current value.
194 ///
195 /// # Arguments
196 /// - `t` - The new state value
197 ///
198 /// # Returns
199 /// - `State<T, ()>` - A State that sets the state to the provided value
200 ///
201 /// # Type Parameters
202 /// - `T` - The type of the state
203 pub fn set<T>(t: T) -> State<T, ()>
204 where
205 T: Clone + 'static, {
206 Self::new(move |_| ((), t.clone()))
207 }
208
209 /// Creates a State that modifies the current state using a function.
210 ///
211 /// This method allows you to transform the state based on its current value.
212 ///
213 /// # Arguments
214 /// - `f` - A function that transforms the state
215 ///
216 /// # Returns
217 /// - `State<T, ()>` - A State that modifies the state using the provided function
218 ///
219 /// # Type Parameters
220 /// - `T` - The type of the state
221 /// - `F` - The type of the transformation function
222 pub fn modify<T, F>(f: F) -> State<T, ()>
223 where
224 F: Fn(T) -> T + 'static,
225 T: Clone + 'static, {
226 let s = Self::get();
227 s.flat_map(move |t: T| Self::set(f(t)))
228 }
229
230 /// Executes a sequence of States and collects their results into a vector.
231 ///
232 /// This method runs each State in the provided vector in sequence, threading
233 /// the state through each computation, and collects all the resulting values.
234 ///
235 /// # Arguments
236 /// - `sas` - A vector of States to execute in sequence
237 ///
238 /// # Returns
239 /// - `State<S, Vec<A>>` - A State that produces a vector of all the values
240 pub fn sequence(sas: Vec<State<S, A>>) -> State<S, Vec<A>> {
241 Self::new(move |s| {
242 let mut s_ = s;
243 // Pre-allocate capacity
244 let mut acc = Vec::with_capacity(sas.len());
245
246 // Iterate without moving ownership
247 for x in sas.iter() {
248 let (a, s2) = x.clone().run(s_);
249 s_ = s2;
250 acc.push(a);
251 }
252 (acc, s_)
253 })
254 }
255}
256
257impl<S, A> Default for State<S, A>
258where
259 S: Default + 'static,
260 A: Default + Clone + 'static,
261{
262 fn default() -> Self {
263 Self::new(|_| (A::default(), S::default()))
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270 use std::env;
271
272 fn init() {
273 env::set_var("RUST_LOG", "info");
274 let _ = env_logger::builder().is_test(true).try_init();
275 }
276
277 pub mod laws {
278 use super::*;
279
280 #[test]
281 fn test_left_identity_law() {
282 init();
283 let n = 10;
284 let s = 11;
285 let f = |x| State::<i32, i32>::pure(x);
286 let result = State::<i32, i32>::pure(n).flat_map(f).run(s.clone()) == f(n).run(s);
287 assert!(result);
288 }
289
290 #[test]
291 fn test_right_identity_law() {
292 init();
293 let x = 10;
294 let s = 11;
295 let result = State::<i32, i32>::pure(x)
296 .flat_map(|y| State::<i32, i32>::pure(y))
297 .run(s.clone())
298 == State::<i32, i32>::pure(x).run(s);
299 assert!(result);
300 }
301
302 #[test]
303 fn test_associativity_law() {
304 init();
305 let x = 10;
306 let s = 11;
307 let f = |x| State::<i32, i32>::pure(x * 2);
308 let g = |x| State::<i32, i32>::pure(x + 1);
309 let result = State::<i32, i32>::pure(x).flat_map(f).flat_map(g).run(s.clone()) == f(x).flat_map(g).run(s);
310 assert!(result);
311 }
312 }
313
314 #[test]
315 fn pure() {
316 init();
317 let s = State::<u32, u32>::pure(10);
318 let r = s.run(10);
319 assert_eq!(r, (10, 10));
320 }
321
322 #[test]
323 // #[should_panic]
324 fn should_panic_when_running_with_null_state() {
325 let state: State<Option<i32>, i32> = State::<Option<i32>, i32>::new(|s| (10, s));
326 let result = state.run(None);
327 assert_eq!(result, (10, None));
328 }
329}