8 releases
Uses new Rust 2024
new 0.1.7 | Mar 24, 2025 |
---|---|
0.1.6 | Mar 23, 2025 |
#535 in Rust patterns
510 downloads per month
12KB
237 lines
algoroutine
Light-weight algebraic effect (algebraic goroutine) in Rust.
Using go!
macro just as Haskell's do notation while we use algebraic effects
instead of monads to avoid monad's composition issue.
Only one-shot (linear) algebraic effect based on coroutine is supported.
Therefore we need nightly compiler to transform code.
Main macros and combinators
go!
macro to run effects and coroutines, just like?
and.await
.Coroutine.map
to map results.Coroutine.and_then
to chain operations.
Handler
Currently handlers can only be built by hand. There's no easy way to combine.
Example
Logging and mutable states
let prepare = #[coroutine] |_: Option<i32>| {
go!(Log("preparing".into()) => Effect);
return ResultCode(0);
};
let logic = #[coroutine] |_: Option<i32>| {
go!(Log("start".into()) => Effect); // inject Log into Effect type
go!(State::Set(Some(9)));
let s = go!(State::Get);
go!(Log(format!("Got {:?}", s)));
let res = go!(prepare, None);
if !res.is_ok() {
go!(Log(format!("error code: {}", res.0)));
}
return res;
};
// Now we can use different ways to interpret `Log` and `State` effects!
let mut handler: Handler<Option<i32>> = Handler::new();
let ans = handler.handle(None, logic);
dbg!(ans);
To inject effects, we don't use coproduct
, just From
!
See examples/logging_and_states for details.
Exception
let div = #[coroutine]
|(a, b): (i32, i32)| {
if b == 0 {
go!(Exception::Raise("divided by 0".into()) => Exception);
}
return a / b;
};
let logic = #[coroutine]
|_: Context| {
println!("Start!");
let ans = go!(div, (4, 0) => Exception); // will stop executing rest continuation
println!("div result: {}", ans);
println!("end.");
return 0;
};
let mut handler = ExceptionHandler::new();
let ans = handler.handle(Context::None, logic);
dbg!(ans);
See examples/exception for details.
Event loop
let logic = #[coroutine]
|| {
println!("begin...");
println!("first let's wait for 3 secs...");
go!(Timeout::of(time::Duration::from_secs(3)) => Effect);
println!("next we try to wait 5 secs...");
go!(Timeout::of(time::Duration::from_secs(5)));
println!("end.");
};
See examples/event_loop for details.
Fun facts
goroutine
andgo
syntax is cool (but golang's type system is terrible)algebraic effect
is coolalgebraic goroutine (algoroutine)
is probably bad ;)
TODO
- Macros for declare effect types
- More auto-injective traits
- Remove boxes for better performance