Shared State
Handlers can share mutable state via run_sync_stateful / run_stateful. The state is passed as a &mut S first argument to every handler.
Basic example
use corophage::prelude::*;
#[effect(u64)]
struct Counter;
#[effectful(Counter)]
fn count_up() -> u64 {
let a = yield_!(Counter);
let b = yield_!(Counter);
a + b
}
let mut count: u64 = 0;
let result = count_up()
.handle(|s: &mut u64, _: Counter| {
*s += 1;
Control::resume(*s)
})
.run_sync_stateful(&mut count);
assert_eq!(result, Ok(3)); // 1 + 2
assert_eq!(count, 2); // handler was called twiceMultiple effects with shared state
All handlers in a stateful run share the same &mut S:
use corophage::prelude::*;
use std::marker::PhantomData;
#[effect(())]
pub struct Log<'a>(pub &'a str);
#[derive(Default)]
#[effect(S)]
pub struct GetState<S> {
_marker: PhantomData<S>,
}
#[effect(())]
pub struct SetState<S>(pub S);
#[effectful(Log<'static>, GetState<u64>, SetState<u64>)]
fn my_program() -> u64 {
yield_!(Log("starting"));
let val = yield_!(GetState::default());
yield_!(SetState(val * 2));
yield_!(GetState::default())
}
#[derive(Debug)]
struct AppState { x: u64 }
let mut state = AppState { x: 42 };
let result = my_program()
.handle(|_s: &mut AppState, Log(msg)| {
println!("{msg}");
Control::resume(())
})
.handle(|s: &mut AppState, _: GetState<u64>| Control::resume(s.x))
.handle(|s: &mut AppState, SetState(x)| {
s.x = x;
Control::resume(())
})
.run_sync_stateful(&mut state);
assert_eq!(result, Ok(84));
assert_eq!(state.x, 84);Alternatives
If your handlers don't need shared state, use .run_sync() / .run() instead. You can also use RefCell or other interior mutability patterns to share state without run_stateful.