effect handlers in rust
Find a file
2022-01-21 13:16:03 +00:00
effers-derive readme and examples n stuff 2022-01-21 13:16:03 +00:00
examples readme and examples n stuff 2022-01-21 13:16:03 +00:00
src readme and examples n stuff 2022-01-21 13:16:03 +00:00
.gitignore start and finish lmao 2022-01-19 20:56:39 +00:00
Cargo.toml start and finish lmao 2022-01-19 20:56:39 +00:00
README.org readme and examples n stuff 2022-01-21 13:16:03 +00:00

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();: print
  • fn 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);