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}