| effers-derive | ||
| examples | ||
| src | ||
| .gitignore | ||
| Cargo.toml | ||
| README.org | ||
effers
ergonomic effect handlers in rust
how to use
defining effects
effects are defined with traits
trait Printer {
fn print(&self, s: &str);
fn available() -> bool;
}
trait Logger {
fn debug(&mut self, s: &str);
fn info(self, s: &str);
}
functions can take self, &self, &mut self, or no self parameter. at this point self parameters with a specified type (like self: Box<Self>) are not supported
defining a program
programs are defined as a normal function, with the added program attribute, which specifies (optional) a name for the program, and (required) the list of effects and corresponding functions that are used
#[effers::program(MyCoolProgram =>
Printer(print(&self) as p, available as printer_available),
Logger(debug(&mut self), info(self))
)]
fn my_program(val: u8) -> u8 {
if printer_available() {
p("hey hi hello");
}
debug("this is a debug-level log");
info("this is a info-level log");
val + 3
}
name
the first token (MyCoolProgram) will be the name of the program. this is optional, and can be skipped:
#[program(
Printer(print(&self) as p, available as printer_available),
Logger(debug(&mut self), info(self))
)]
if skipped, the default name will be the program function's name (my_program) in PascalCase (MyProgram)
listing effects
effects are listed by writing the trait's name, followed by a parenthesized list of the functions that will be used
listing effect functions
due to limitations of proc-macros, it's unknown what kind of self parameter the function takes, if any, and so it has to be explicitly specified (if you have ideas on how to fix this, please open a PR!): here's how each type is specified:
fn print();:printfn print(self);:print(self)fn print(mut self);:print(self)fn print(&self);:print(&self)fn print(&mut self);:print(&mut self)
effect function aliases
functions can be given an alias using the as keyword (print(&self) as p) so that the function can be called by a different name inside the program
defining effect handlers
effect handlers are defined by declaring a struct, and implementing the corresponding trait on it
struct IoPrinter;
impl Printer for IoPrinter {
fn print(&self, s: &str) {
println!("{}", s)
}
fn available() -> bool {
true
}
}
struct FileLogger;
impl Logger for FileLogger {
fn debug(&mut self, s: &str) {
println!("debug: {}", s)
}
fn info(self, s: &str) {
println!("info: {}", s)
}
}
running programs
programs are run by providing the corresponding handlers in the order listed in the program definition, and finally calling the run method, providing it the required parameters
let result: u8 = MyCoolProgram.add(IoPrinter).add(FileLogger).run(3);
assert_eq!(result, 6);