readme and examples n stuff

main
annieversary 2022-01-21 13:16:03 +00:00
parent a67ad2d6ad
commit 64637fa92d
7 changed files with 135 additions and 102 deletions

View File

@ -1,5 +1,5 @@
* effers * effers
effect handlers in rust ergonomic effect handlers in rust
** how to use ** how to use
*** defining effects *** defining effects
@ -7,24 +7,30 @@ effects are defined with traits
#+begin_src rust #+begin_src rust
trait Printer { trait Printer {
fn print(&mut self, s: &str); fn print(&self, s: &str);
fn available() -> bool;
} }
trait Logger { trait Logger {
fn debug(&mut self, s: &str); fn debug(&mut self, s: &str);
fn info(&mut self, s: &str); fn info(self, s: &str);
} }
#+end_src #+end_src
all the trait functions (that are used in programs) must take ~&mut self~ as a parameter 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 *** defining a program
programs are defined as a normal function, with the added ~program~ attribute 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
#+begin_src rust #+begin_src rust
#[program(Smth => Printer(print as p), Logger(debug, info))] #[effers::program(MyCoolProgram =>
fn smth(val: u8) -> u8 { Printer(print(&self) as p, available as printer_available),
p("hey hi hello"); 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"); debug("this is a debug-level log");
info("this is a info-level log"); info("this is a info-level log");
@ -33,22 +39,47 @@ fn smth(val: u8) -> u8 {
} }
#+end_src #+end_src
the first token (~Smth~) will be the name of the program. effects are listed after the ~=>~ token **** name
the first token (~MyCoolProgram~) will be the name of the program. this is optional, and can be skipped:
#+begin_src rust
#[program(
Printer(print(&self) as p, available as printer_available),
Logger(debug(&mut self), info(self))
)]
#+end_src
if skipped, the default name will be the program function's name (~my_program~) in PascalCase (~MyProgram~)
**** listing effects **** listing effects
effects are listed by writing the trait's name, followed by a parenthesized list of the functions that will be used effects are listed by writing the trait's name, followed by a parenthesized list of the functions that will be used
functions can be given an alias using the ~as~ keyword ***** 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 *** defining effect handlers
effect handlers are defined by declaring a struct, and implementing the trait on it effect handlers are defined by declaring a struct, and implementing the corresponding trait on it
#+begin_src rust #+begin_src rust
struct IoPrinter; struct IoPrinter;
impl Printer for IoPrinter { impl Printer for IoPrinter {
fn print(&mut self, s: &str) { fn print(&self, s: &str) {
println!("{}", s) println!("{}", s)
} }
fn available() -> bool {
true
}
} }
struct FileLogger; struct FileLogger;
@ -56,88 +87,21 @@ impl Logger for FileLogger {
fn debug(&mut self, s: &str) { fn debug(&mut self, s: &str) {
println!("debug: {}", s) println!("debug: {}", s)
} }
fn info(&mut self, s: &str) { fn info(self, s: &str) {
println!("info: {}", s) println!("info: {}", s)
} }
} }
#+end_src #+end_src
*** running programs *** running programs
programs are run by providing the corresponding handlers in order, and finally calling the ~run~ method, providing it the required parameters 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
#+begin_src rust #+begin_src rust
let result: u8 = Smth.add(IoPrinter).add(FileLogger).run(3); let result: u8 = MyCoolProgram.add(IoPrinter).add(FileLogger).run(3);
assert_eq!(result, 6); assert_eq!(result, 6);
#+end_src #+end_src
** full example ** examples
#+begin_src rust - [[./examples/main.rs][main: general use case]]
use effers::program; - [[./examples/clone.rs][clone: how cloning and copying programs works]]
- [[./examples/module.rs][module: effects from other modules are supported]]
#[program(Smth => Printer(print as p), Logger(debug, info))]
fn smth(val: u8) -> u8 {
p("hey hi hello");
debug("this is a debug-level log");
info("this is a info-level log");
val + 3
}
fn main() {
// maybe smth like this?
let result: u8 = Smth.add(IoPrinter).add(FileLogger).run(3);
assert_eq!(result, 6);
let other_result: u8 = Smth
.add(IoPrinter)
.add(NetworkLogger {
credentials: "secret password".to_string(),
})
.run(8);
assert_eq!(other_result, 11);
}
// effects
trait Printer {
fn print(&mut self, s: &str);
}
trait Logger {
fn debug(&mut self, s: &str);
fn info(&mut self, s: &str);
}
struct IoPrinter;
impl Printer for IoPrinter {
fn print(&mut self, s: &str) {
println!("{}", s)
}
}
struct FileLogger;
impl Logger for FileLogger {
fn debug(&mut self, s: &str) {
println!("debug: {}", s)
}
fn info(&mut self, s: &str) {
println!("info: {}", s)
}
}
struct NetworkLogger {
credentials: String,
}
impl Logger for NetworkLogger {
fn debug(&mut self, s: &str) {
println!(
"debug through network: {}; with password {}",
s, self.credentials
)
}
fn info(&mut self, s: &str) {
println!(
"info through network: {}; with password {}",
s, self.credentials
)
}
}
#+end_src

View File

@ -66,6 +66,7 @@ pub fn intermediate_structs(args: &Args, prog_name: &Ident) -> Vec<IntermediateS
let last_letter = letters.clone().last(); let last_letter = letters.clone().last();
structs.push(IntermediateStruct::new( structs.push(IntermediateStruct::new(
quote! { quote! {
#[derive(Clone, Copy)]
struct #id<#generics>(#last, #last_letter); struct #id<#generics>(#last, #last_letter);
}, },
id, id,

View File

@ -57,6 +57,7 @@ fn rewrite_item_into_struct(func: ItemFn, args: Args) -> TokenStream {
let final_impl = final_impl(intermediate_structs.last().unwrap(), func, &args); let final_impl = final_impl(intermediate_structs.last().unwrap(), func, &args);
let out = quote! { let out = quote! {
#[derive(Clone, Copy)]
struct #prog_name; struct #prog_name;
#inters_tokens #inters_tokens

71
examples/clone.rs Normal file
View File

@ -0,0 +1,71 @@
use effers::program;
#[program(Incrementer(increment(&self)), Printer(print(&self)))]
fn prog(val: u8) -> u8 {
let x = increment(val);
let y = increment(x);
print(x);
x + y
}
pub trait Incrementer {
fn increment(&self, v: u8) -> u8;
}
#[derive(Clone, Copy)]
pub struct TestInc;
impl Incrementer for TestInc {
fn increment(&self, v: u8) -> u8 {
v + 3
}
}
trait Printer {
fn print(&self, s: u8);
}
#[derive(Clone, Copy)]
struct Printer1;
impl Printer for Printer1 {
fn print(&self, s: u8) {
println!("1: {}", s)
}
}
#[derive(Clone)]
struct Printer2 {
prefix: String,
}
impl Printer for Printer2 {
fn print(&self, s: u8) {
println!("2: {} {}", self.prefix, s)
}
}
fn main() {
// if a program only has Clone effects, the program also becomes clone
// same applies for Copy
// a is Copy since TestInc is Copy
let a = Prog.add(TestInc);
let b = a.add(Printer1);
let c = a.add(Printer2 {
prefix: "this is a number".to_string(),
});
// both TestInc and Printer1 are Copy,
// therefore b is copy, and we can call it as much as we want
let first_result = b.run(0);
assert_eq!(first_result, 9);
let second_result = b.run(2);
assert_eq!(second_result, 13);
// since Printer2 is not Copy, but it is Clone,
// c is Clone but not Copy
let first_result = c.clone().run(0);
assert_eq!(first_result, 9);
let second_result = c.run(2);
assert_eq!(second_result, 13);
}

View File

@ -1,8 +1,11 @@
use effers::program; use effers::program;
#[program(Smth => Printer(print(&self) as p, check as check_printer), Logger(debug(&mut self), info(self)))] #[program(MyCoolProgram =>
fn smth(val: u8) -> u8 { Printer(print(&self) as p, available as printer_available),
if check_printer() { Logger(debug(&mut self), info(self))
)]
fn my_program(val: u8) -> u8 {
if printer_available() {
p("hey hi hello"); p("hey hi hello");
} }
@ -19,9 +22,9 @@ fn other_program() {
fn main() { fn main() {
// call the first program twice // call the first program twice
let result: u8 = Smth.add(IoPrinter).add(FileLogger).run(3); let result: u8 = MyCoolProgram.add(IoPrinter).add(FileLogger).run(3);
assert_eq!(result, 6); assert_eq!(result, 6);
let other_result: u8 = Smth let other_result: u8 = MyCoolProgram
.add(IoPrinter) .add(IoPrinter)
.add(NetworkLogger { .add(NetworkLogger {
credentials: "secret password".to_string(), credentials: "secret password".to_string(),
@ -35,7 +38,7 @@ fn main() {
trait Printer { trait Printer {
fn print(&self, s: &str); fn print(&self, s: &str);
fn check() -> bool; fn available() -> bool;
} }
trait Logger { trait Logger {
fn debug(&mut self, s: &str); fn debug(&mut self, s: &str);
@ -47,7 +50,7 @@ impl Printer for IoPrinter {
fn print(&self, s: &str) { fn print(&self, s: &str) {
println!("{}", s) println!("{}", s)
} }
fn check() -> bool { fn available() -> bool {
true true
} }
} }

View File

@ -1,7 +1,5 @@
use effers::program;
// Effects can be referenced from inside a module // Effects can be referenced from inside a module
#[program(inc::Incrementer(increment(&self)))] #[effers::program(inc::Incrementer(increment(&self)))]
fn prog(val: u8) -> u8 { fn prog(val: u8) -> u8 {
let x = increment(val); let x = increment(val);
let y = increment(x); let y = increment(x);
@ -11,15 +9,10 @@ fn prog(val: u8) -> u8 {
mod inc { mod inc {
pub trait Incrementer { pub trait Incrementer {
fn increment(&self, v: u8) -> u8; fn increment(&self, v: u8) -> u8;
fn check() -> bool;
} }
pub struct TestInc; pub struct TestInc;
impl Incrementer for TestInc { impl Incrementer for TestInc {
fn check() -> bool {
false
}
fn increment(&self, v: u8) -> u8 { fn increment(&self, v: u8) -> u8 {
v + 3 v + 3
} }

View File

@ -33,7 +33,7 @@ mod test {
} }
} }
#[program(Printer(print as p))] #[program(Printer(print(&self) as p))]
fn ohter() { fn ohter() {
let _s = p("hey hi hello"); let _s = p("hey hi hello");
} }