add docs to state

This commit is contained in:
andreas 2022-09-06 20:18:50 +02:00
parent dcf7edf6a3
commit 16a1d6b183
Signed by: andreas
GPG Key ID: D97194E55873280A
2 changed files with 113 additions and 4 deletions

1
src/lib.rs Normal file
View File

@ -0,0 +1 @@
pub mod state;

View File

@ -1,28 +1,136 @@
// State monad
/** State Monad
Represents a computation using `State` to produce a value of type `Value` and a new state.
Computations can be combined using [bind](WithState::bind) or [zip](WithState::zip).
*/
pub struct WithState<'c, Value, State>(Box<dyn FnOnce(State) -> (Value, State) + 'c>);
impl <'c, Value, State> WithState<'c, Value, State> {
/**
Constructs a new state monad out of the contained computation.
The input computation takes the previous state, and returns a
tuple containing the computed value and the new state.
```
use statemonad::state::WithState;
// wrap a computation that returns the initial_state + 1, and changes the state to its double.
let s : WithState<i32, i32> = WithState::of(|init_state| (init_state + 1, init_state * 2));
// run the state monad with initial state 2
let (val,state) = s.run(2);
assert_eq!(val, 3);
assert_eq!(state, 4);
```
*/
pub fn of(f: impl FnOnce(State) -> (Value, State) + 'c) -> WithState<'c, Value, State> {
WithState(Box::new(f))
}
/**
Runs the state monad, running all contained computations in order
and returns the final computed value and state.
```
use statemonad::state::WithState;
// create a state monad that computes the value as the initial state * 2, and doesn't modify the state.
let s : WithState<f32, i32> = WithState::of(|init_state| (init_state as f32 * 2.0, init_state));
// run the state monad with an initial state 1, getting the final value and final state.
let (val, state) = s.run(1);
assert_eq!(val, 2.0);
assert_eq!(state, 1);
```
*/
pub fn run(self, s: State) -> (Value, State) {
self.0(s)
}
/**
Binds two state computations together.
Expects a function from the previously computed value, to a new state monad.
# Examples
```
use statemonad::state::WithState;
// initialize the value to the initial state, add 1 to the state
let s : WithState<i32, i32> = WithState::of(|oldstate| (oldstate, oldstate + 1))
// Set the value to the old state, multiply the state by 2
.bind(|oldvalue| WithState::of(|oldstate| (oldstate, oldstate * 2)))
// "do-nothing", return the old value and keep the state the same, closure has to be `move` due to referencing the outside value.
.bind(|oldvalue| WithState::of(move |oldstate| (oldvalue, oldstate)));
let (computed_val,final_state) : (i32,i32) = s.run(0);
assert_eq!(computed_val, 1); // value was initially 0, then set to state when state was 1
assert_eq!(final_state, 2); // state was originally 0, then added 1, then multiplied by 2
```
*/
pub fn bind<'cn, NewValue>(self, f: impl FnOnce(Value) -> WithState<'cn, NewValue, State> + 'cn) -> WithState<'cn, NewValue, State>
where 'c: 'cn, State : 'cn, Value : 'cn {
WithState::<'cn, NewValue, State>::of(move |n| {
let (x,sp) = self.run(n);
f(x).run(sp)
WithState::<'cn, NewValue, State>::of(move |old_value| {
let (new_value,new_state) = self.run(old_value);
f(new_value).run(new_state)
})
}
/**
Combine two state monad computations together.
Creates a new state monad with the result value being a tuple containing both input state monads.
The `State` type must be the same for both state monads.
# Examples
```
use statemonad::state::WithState;
// initialize one state monad computing a string value, and one computing a float.
let s : WithState<String, i32> = WithState::of(|oldstate| (String::from(""), oldstate + 1));
let s2 : WithState<f32, i32> = WithState::of(|oldstate| (1.0, oldstate));
// zip them together
let s3 : WithState<(String,f32), i32> = s.zip(s2);
// compute both values together!
let ((val1,val2),state) = s3.run(0);
assert_eq!(val1, "");
assert_eq!(val2, 1.0);
```
*/
pub fn zip<'cn, NewValue>(self, st: WithState<'cn, NewValue, State>) -> WithState<'cn, (Value,NewValue), State>
where 'c: 'cn, State: 'cn, Value: 'cn, NewValue: 'cn {
self.bind(|aa| st.map(|bb| (aa,bb)))
}
/**
Modify the computed value without changing the state.
# Examples
```
use statemonad::state::WithState;
let s : WithState<f32, i32> = WithState::of(|oldstate| (1.0, oldstate));
let s2 = s.map(|oldval| oldval * 2.0);
let (val, state) = s2.run(0);
assert_eq!(val, 2.0);
assert_eq!(state, 0);
```
*/
pub fn map<'cn, NewValue>(self, f: impl FnOnce(Value) -> NewValue + 'cn) -> WithState<'cn, NewValue, State>
where 'c: 'cn, State : 'cn, Value : 'cn {
WithState::<'cn, NewValue, State>::of(move |n| {