readme and examples n stuff
parent
a67ad2d6ad
commit
64637fa92d
136
README.org
136
README.org
|
@ -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
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue