diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..266c62a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod state; diff --git a/src/state.rs b/src/state.rs index 58bac65..8fa2eaa 100644 --- a/src/state.rs +++ b/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 (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 = 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 = 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 = 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 = WithState::of(move |oldstate| (String::from(""), oldstate + 1)); + let s2 : WithState = 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 = 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| {