add docs to state
This commit is contained in:
parent
dcf7edf6a3
commit
8c554a1ee8
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod state;
|
||||
117
src/state.rs
117
src/state.rs
@ -1,28 +1,137 @@
|
||||
// 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` or `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(move |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(move |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(move |oldstate| (oldstate, oldstate + 1))
|
||||
// Set the value to the old state, multiply the state by 2
|
||||
.bind(move |oldvalue| WithState::of(move |oldstate| (oldstate, oldstate * 2)))
|
||||
// "do-nothing", return the old value and keep the state the same
|
||||
.bind(move |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(move |oldstate| (String::from(""), oldstate + 1));
|
||||
let s2 : WithState<f32, i32> = WithState::of(move |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(move |oldstate| (1.0, oldstate));
|
||||
|
||||
let s2 = s.map(move |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| {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user