readme
parent
bf8cdab3b8
commit
ee90435633
218
README.org
218
README.org
|
@ -1,111 +1,143 @@
|
||||||
* effers
|
* effers
|
||||||
** implementation details
|
effect handlers in rust
|
||||||
we could do something with proc macros maybe?
|
|
||||||
where like, it scans through the tagged function, then it generates like a type?
|
** how to use
|
||||||
then that type you can call a function that provides it an effect runner
|
*** defining effects
|
||||||
|
effects are defined with traits
|
||||||
|
|
||||||
#+begin_src rust
|
#+begin_src rust
|
||||||
// the `Smth =>` part is optional
|
trait Printer {
|
||||||
#[program(Smth => Printer(print as p), Logger(debug, info))]
|
fn print(&mut self, s: &str);
|
||||||
fn smth(val: u8) -> u8 {
|
}
|
||||||
p("hey hi hello");
|
trait Logger {
|
||||||
|
fn debug(&mut self, s: &str);
|
||||||
debug("this is a debug-level log");
|
fn info(&mut self, s: &str);
|
||||||
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);
|
|
||||||
let other_result: u8 = Smth
|
|
||||||
.add(IoPrinter)
|
|
||||||
.add(NetworkLogger {
|
|
||||||
credentials: "secret password".to_string()
|
|
||||||
})
|
|
||||||
.run(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Printer {
|
|
||||||
fn print();
|
|
||||||
}
|
|
||||||
trait Logger {
|
|
||||||
fn debug(&mut self);
|
|
||||||
fn info(&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IoPrinter;
|
|
||||||
impl Printer for IoPrinter {
|
|
||||||
fn print(s: &str) { println!(s) }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FileLogger;
|
|
||||||
impl Logger for FileLogger {
|
|
||||||
fn debug(&mut self) { ... }
|
|
||||||
fn info(&mut self) { ... }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NetworkLogger { credentials: String }
|
|
||||||
impl Logger for NetworkLogger {
|
|
||||||
fn debug(&mut self) { ... }
|
|
||||||
fn info(&mut self) { ... }
|
|
||||||
}
|
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
the macro will substitute the function to:
|
all the trait functions (that are used in programs) must take ~&mut self~ as a parameter
|
||||||
|
|
||||||
|
*** defining a program
|
||||||
|
|
||||||
|
programs are defined as a normal function, with the added ~program~ attribute
|
||||||
|
|
||||||
#+begin_src rust
|
#+begin_src rust
|
||||||
struct Smth;
|
#[program(Smth => Printer(print as p), Logger(debug, info))]
|
||||||
struct SmthWithPrinter<P: Printer>(Smth, P);
|
fn smth(val: u8) -> u8 {
|
||||||
struct SmthWithPrinterLogger<P: Printer, L: Logger>(Smth, P, L);
|
p("hey hi hello");
|
||||||
impl Smth {
|
|
||||||
fn add<P: Printer>(self, p: P) -> SmthWithPrinter<P> {
|
|
||||||
SmthWithPrinter(self, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<P: Printer> SmthWithPrinter<P> {
|
|
||||||
fn add<L: Logger>(self, l: L) -> SmthWithPrinterLogger<P, L> {
|
|
||||||
SmthWithPrinterLogger(self.0, self.1, l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<P: Printer, L: Logger> SmthWithPrinterLogger<P, L> {
|
|
||||||
fn run(self, val: u8) -> u8 {
|
|
||||||
let l = self.2; // we probably don't do this though, just do an actual replacement
|
|
||||||
|
|
||||||
P::print("hey hi hello");
|
debug("this is a debug-level log");
|
||||||
|
info("this is a info-level log");
|
||||||
|
|
||||||
l.debug("this is a debug-level log");
|
val + 3
|
||||||
l.info("this is a info-level log");
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
3
|
the first token (~Smth~) will be the name of the program. effects are listed after the ~=>~ token
|
||||||
|
|
||||||
|
**** listing effects
|
||||||
|
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
|
||||||
|
|
||||||
|
*** defining effect handlers
|
||||||
|
effect handlers are defined by declaring a struct, and implementing the trait on it
|
||||||
|
|
||||||
|
#+begin_src rust
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
this could be rewritten to allow users to take any path when writing adding the effect handlers
|
*** running programs
|
||||||
maybe not, cause we might want to have two of the same kind of effect, which would make us unable to tell them apart
|
programs are run by providing the corresponding handlers in order, and finally calling the ~run~ method, providing it the required parameters
|
||||||
|
|
||||||
we can then make macros that make declaring an effect easier ig
|
#+begin_src rust
|
||||||
|
let result: u8 = Smth.add(IoPrinter).add(FileLogger).run(3);
|
||||||
|
assert_eq!(result, 6);
|
||||||
|
#+end_src
|
||||||
|
|
||||||
** questions
|
** full example
|
||||||
*** DONE how to pass the logger into the function
|
#+begin_src rust
|
||||||
CLOSED: [2022-01-18 Tue 19:41]
|
use effers::program;
|
||||||
an option is to add it to the list of params
|
|
||||||
*** DONE how to make it so the user can't call it directly
|
|
||||||
CLOSED: [2022-01-18 Tue 19:41]
|
|
||||||
solved, the function lives inside an impl block for a type
|
|
||||||
*** TODO can we make it so it's uses ~IoPrinter::print~ instead of ~io_printer.print~?
|
|
||||||
cause that would have better performance i think?
|
|
||||||
cause it's just a static function and that's known at comp time
|
|
||||||
|
|
||||||
with the other it does dynamic dispatch
|
#[program(Smth => Printer(print as p), Logger(debug, info))]
|
||||||
|
fn smth(val: u8) -> u8 {
|
||||||
|
p("hey hi hello");
|
||||||
|
|
||||||
idk if we have access to that info on the macro
|
debug("this is a debug-level log");
|
||||||
*** TODO how to deal with multiple of the same type of effect
|
info("this is a info-level log");
|
||||||
i think it Just Works if we keep the order thing. we just have to make sure they define different names in the macro invocation, otherwise we won't know which one to use
|
|
||||||
|
|
||||||
https://hackage.haskell.org/package/effet this uses tags
|
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
|
||||||
|
|
Loading…
Reference in New Issue