diff --git a/src/features/rust_io.rs b/src/features/rust_io.rs index ef24cfe..bbf0715 100644 --- a/src/features/rust_io.rs +++ b/src/features/rust_io.rs @@ -18,11 +18,10 @@ use crate::features::rust_io::RustIO::{Empty, Fut, Right, Value, Wrong}; /// Work based on original idea of crate [do-notation] #[macro_export] macro_rules! rust_io { - // return + (return $r:expr ;) => { - $crate::Lift::lift($r) + $crate::features::rust_io::Lift::lift($r) }; - // let variable bind (let $p:pat = $e:expr ; $($r:tt)*) => {{ let $p = $e; @@ -37,6 +36,11 @@ macro_rules! rust_io { // bind ($bind:ident <- $x:expr ; $($r:tt)*) => { $x.flat_map(move |$bind| { rust_io!($($r)*) }) + .with_env("hello".to_string()) + }; + + ($bind:ident <= $x:expr ; $($r:tt)*) => { + $x.environment({ rust_io!($($r)*) }) }; // return type from do-notation @@ -67,7 +71,13 @@ macro_rules! rust_io { /// [is_ok][is_failed][is_empty] /// Async task executions /// [parallel][fork] -pub trait Lift { +pub trait Lift { + + fn get_env() { + static COUNTER: u32 = 1981; + } + + fn lift(a: A) -> Self; fn of(a: A) -> Self; @@ -82,6 +92,8 @@ pub trait Lift { fn from_result(a: Result) -> Self; + fn with_env(self, e: E) -> Self; + fn merge Self>(a: Self, b: Self, op: F) -> Self; fn get(self) -> A; @@ -102,6 +114,8 @@ pub trait Lift { fn flat_map Self>(self, op: F) -> Self; + fn environment) -> Self>(self, op: F) -> Self; + fn at_some_point Self>(self, op: F) -> Self where A: Clone, F: Clone; fn at_some_point_while bool, F: FnOnce(A) -> Self>(self, predicate: P, op: F) -> Self where A: Clone, P: Clone, F: Clone; @@ -141,29 +155,35 @@ pub trait Lift { fn on_success ()>(self, op: F) -> Self; } +struct IOEnv { + env: Option, + value: A, +} + ///Data structure to be used as the monad to be implemented as [Lift] /// RustIO monad can have the list of possible states. -enum RustIO { - Right(A), - Wrong(T), - Value(A), +enum RustIO { + Right(IOEnv), + Wrong(IOEnv), + Value(IOEnv), Empty(), Fut(LocalBoxFuture<'static, A>), } /// Implementation of the Monad Lift. -impl Lift for RustIO { +impl Lift for RustIO { + fn lift(a: A) -> Self { RustIO::of(a) } /// Pure value to create RustIO monad without side-effects. fn of(a: A) -> Self { - Value(a) + Value(IOEnv { env: None, value: a }) } fn from_func(f: fn() -> A) -> Self { - Value(f()) + Value(IOEnv { env: None, value: f() }) } fn from_option_func(f: fn() -> Option) -> Self { @@ -177,14 +197,14 @@ impl Lift for RustIO { fn from_option(a: Option) -> Self { match a { None => Empty(), - Some(v) => Value(v) + Some(v) => Value(IOEnv { env: None, value: v }) } } fn from_result(a: Result) -> Self { match a { - Ok(v) => Right(v), - Err(t) => Wrong(t) + Ok(v) => Right(IOEnv { env: None, value: v }), + Err(t) => Wrong(IOEnv { env: None, value: t }) } } @@ -194,8 +214,8 @@ impl Lift for RustIO { fn get(self) -> A { match self { - Value(v) => v, - Right(t) => t, + Value(v) => v.value, + Right(t) => t.value, _ => panic!("Error, value not available"), } } @@ -203,7 +223,7 @@ impl Lift for RustIO { fn failed(self) -> T { match self { Value(_) | Right(_) | Empty() | Fut(_) => panic!("Error, value not available"), - Wrong(e) => e, + Wrong(e) => e.value, } } @@ -241,22 +261,22 @@ impl Lift for RustIO { fn map A>(self, op: F) -> Self { match self { - Value(v) => Value(op(v)), - Right(v) => Right(op(v)), + Value(v) => Value(IOEnv { env: v.env, value: op(v.value) }), + Right(v) => Right(IOEnv { env: v.env, value: op(v.value) }), _ => self, } } fn map_error T>(self, op: F) -> Self { match self { - Wrong(e) => Wrong(op(e)), + Wrong(e) => Wrong(IOEnv { env: e.env, value: op(e.value) }), _ => self } } fn flat_map Self>(self, op: F) -> Self { match self { - Value(a) | Right(a) => op(a), + Value(a) | Right(a) => op(a.value), Empty() => Empty(), Wrong(e) => Wrong(e), _ => self @@ -272,7 +292,7 @@ impl Lift for RustIO { Value(a) | Right(a) => { loop { let op_copy = op.clone(); - let a_copy = a.clone(); + let a_copy = a.value.clone(); let result = op_copy(a_copy); if result.is_ok() { break result; @@ -297,12 +317,12 @@ impl Lift for RustIO { return match self { Value(t) => { let x = t; - return if predicate(&x) { Value(op(x)) } else { Empty() }; + return if predicate(&x.value) { Value(IOEnv { env: x.env, value: op(x.value) }) } else { Empty() }; } Empty() => Empty(), Right(a) => { let x = a; - return if predicate(&x) { Right(op(x)) } else { Empty() }; + return if predicate(&x.value) { Right(IOEnv { env: x.env, value: op(x.value) }) } else { Empty() }; } Wrong(e) => Wrong(e), _ => self @@ -313,12 +333,12 @@ impl Lift for RustIO { return match self { Value(t) => { let x = t; - return if predicate(&x) { op(x) } else { Empty() }; + return if predicate(&x.value) { op(x.value) } else { Empty() }; } Empty() => Empty(), Right(a) => { let x = a; - return if predicate(&x) { op(x) } else { Empty() }; + return if predicate(&x.value) { op(x.value) } else { Empty() }; } Wrong(e) => Wrong(e), _ => self @@ -338,12 +358,12 @@ impl Lift for RustIO { return match self { Value(t) => { let x = t; - return if op(&x) { Value(x) } else { Empty() }; + return if op(&x.value) { Value(x) } else { Empty() }; } Empty() => Empty(), Right(a) => { let x = a; - return if op(&x) { Right(x) } else { Empty() }; + return if op(&x.value) { Right(x) } else { Empty() }; } Wrong(e) => Wrong(e), _ => self @@ -352,17 +372,17 @@ impl Lift for RustIO { fn fold A>(self, default: A, op: F) -> Self { match self { - Value(v) => Value(op(v)), - Right(v) => Right(op(v)), - Empty() => Value(default), + Value(v) => Value(IOEnv { env: v.env, value: op(v.value) }), + Right(v) => Right(IOEnv { env: v.env, value: op(v.value) }), + Empty() => Value(IOEnv { env: None, value: default }), _ => self } } fn recover A>(self, op: F) -> Self { match self { - Wrong(_) => Right(op()), - Empty() => Value(op()), + Wrong(t) => Right(IOEnv { env: t.env, value: op() }), + Empty() => Value(IOEnv { env: None, value: op() }), _ => self } } @@ -411,7 +431,7 @@ impl Lift for RustIO { fn fork A>(self, op: F) -> Self where A: 'static, F: 'static { match self { Value(v) | Right(v) => { - Fut(async { op(v) }.boxed_local()) + Fut(async { op(v.value) }.boxed_local()) } _ => self, } @@ -431,12 +451,12 @@ impl Lift for RustIO { return match self { Value(v) => { let x = v; - op(&x); + op(&x.value); Value(x) } Right(v) => { let x = v; - op(&x); + op(&x.value); Right(x) } _ => self @@ -447,7 +467,7 @@ impl Lift for RustIO { return match self { Wrong(v) => { let x = v; - op(&x); + op(&x.value); Wrong(x) } _ => self @@ -458,16 +478,33 @@ impl Lift for RustIO { return match self { Right(v) => { let x = v; - op(&x); + op(&x.value); Right(x) } _ => self }; } + + fn with_env(self, e: E) -> Self { + return match self { + Value(t) => Value(IOEnv { env: Some(e), value: t.value }), + Empty() => Empty(), + Right(a) => Right(IOEnv { env: Some(e), value: a.value }), + Wrong(t) => Wrong(IOEnv { env: Some(e), value: t.value }), + _ => self + }; + } + + fn environment) -> Self>(self, op: F) -> Self { + match self { + Value(a) | Right(a) => op(a.env), + _ => self + } + } } -impl RustIO { - async fn run_future_zip_tasks Self, Z2: FnOnce() -> Self>(&self, a: Z1, b: Z2) -> (RustIO, RustIO) { +impl RustIO { + async fn run_future_zip_tasks Self, Z2: FnOnce() -> Self>(&self, a: Z1, b: Z2) -> (RustIO, RustIO) { let future_zip1 = async { a() }; @@ -477,7 +514,7 @@ impl RustIO { return futures::join!(future_zip1,future_zip2); } - async fn run_future_tasks Self>(&self, tasks: Vec) -> Vec> { + async fn run_future_tasks Self>(&self, tasks: Vec) -> Vec> { let future_tasks = tasks.into_iter() .fold(vec!(), |futures, task: Task| { let future_task = vec![async { return task(); }]; @@ -486,26 +523,26 @@ impl RustIO { return join_all(future_tasks).await; } - async fn unbox_fork(self) -> RustIO { + async fn unbox_fork(self) -> RustIO { match self { Fut(fut_box) => { println!("Extracting future"); - Value(fut_box.await) + Value(IOEnv { env: None, value: fut_box.await }) } _ => Empty(), } } - async fn run_daemon ()>(self, op: F) -> RustIO { + async fn run_daemon ()>(self, op: F) -> RustIO { return match self { Value(v) => { let x = v; - async { op(&x) }.await; + async { op(&x.value) }.await; Value(x) } Right(v) => { let x = v; - async { op(&x) }.await; + async { op(&x.value) }.await; Right(x) } _ => self @@ -519,7 +556,7 @@ impl RustIO { loop { let op_copy = op.clone(); let predicate_copy = predicate.clone(); - let a_copy = a.clone(); + let a_copy = a.value.clone(); let result = op_copy(a_copy); if result.is_ok() || predicate_copy() == cond { break result; @@ -537,7 +574,7 @@ mod tests { #[test] fn rio() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { _ <- RustIO::of(String::from("1981")); v <- RustIO::from_option(Some(String::from("hello"))); t <- RustIO::from_option_func(|| Some(String::from(" pure"))); @@ -546,7 +583,7 @@ mod tests { i <- RustIO::of(String::from("!!")); y <- RustIO::from_result_func(|| Ok(String::from("!!"))); - RustIO::of(v + &t + &z + &x + &i + &y) + return (v + &t + &z + &x + &i + &y); }; println!("${:?}", rio_program.is_empty()); println!("${:?}", rio_program.is_ok()); @@ -555,7 +592,7 @@ mod tests { #[test] fn rio_map() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))) .map(|v| v.to_uppercase()); x <- RustIO::from_result(Ok(String::from(" world"))) @@ -570,7 +607,7 @@ mod tests { #[test] fn rio_flat_map() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))) .flat_map(|v| RustIO::of( v + &String::from(" world"))) .map(|v| v.to_uppercase()); @@ -584,7 +621,7 @@ mod tests { #[test] fn rio_filter() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))) .flat_map(|v| RustIO::of( v + &String::from(" world"))) .filter(|v| v.len() > 5); @@ -598,15 +635,15 @@ mod tests { #[test] fn rio_compose_two_programs() { - let rio_program_1: RustIO = rust_io! { + let rio_program_1: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))); RustIO::of(v + &" ".to_string()) }; - let rio_program_2: RustIO = rust_io! { + let rio_program_2: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("world"))); RustIO::of(v + &"!!".to_string()) }; - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- rio_program_1; i <- rio_program_2; RustIO::of(v + &i).map(|v| v.to_uppercase()) @@ -618,7 +655,7 @@ mod tests { #[test] fn rio_fold() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(None) .fold("hello world!!".to_string(), |v| v.to_uppercase()); RustIO::of(v) @@ -630,7 +667,7 @@ mod tests { #[test] fn rio_empty_recover() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(None) .recover(|| "hello world!!".to_string()); RustIO::of(v) @@ -642,7 +679,7 @@ mod tests { #[test] fn rio_error_recover() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Err("".to_string())) .recover(|| "hello world!!".to_string()); RustIO::of(v) @@ -654,7 +691,7 @@ mod tests { #[test] fn rio_option_recover_with() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(None) .recover_with(|| RustIO::from_option(Some("hello world!!".to_string()))); RustIO::of(v) @@ -666,7 +703,7 @@ mod tests { #[test] fn rio_error_recover_with() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Err("".to_string())) .recover_with(|| RustIO::from_result(Ok("hello world!!".to_string()))); RustIO::of(v) @@ -678,7 +715,7 @@ mod tests { #[test] fn rio_error() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { i <- RustIO::from_option(Some(String::from("hello"))); _ <- RustIO::from_result(Err(503)); v <- RustIO::from_option(Some(String::from("world"))); @@ -691,7 +728,7 @@ mod tests { #[test] fn rio_filter_empty() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))) .filter(|v| v.len() > 10); i <- RustIO::of(String::from("!!")); @@ -704,7 +741,7 @@ mod tests { #[test] fn rio_delay() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some("hello world!!".to_string())) .delay(Duration::from_secs(2)); RustIO::of(v) @@ -716,7 +753,7 @@ mod tests { #[test] fn rio_get_or_else() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Err("".to_string())); RustIO::of(v) }; @@ -727,7 +764,7 @@ mod tests { #[test] fn rio_merge() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::merge( RustIO::from_option(Some("hello".to_string())), RustIO::from_option(Some(" world!!".to_string())), |a,b| RustIO::from_option(Some(a + &b))); @@ -740,7 +777,7 @@ mod tests { #[test] fn rio_merge_error() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::merge( RustIO::from_option(Some("hello".to_string())), RustIO::from_option(None), |a,b| RustIO::from_option(Some(a + &b))); @@ -753,7 +790,7 @@ mod tests { #[test] fn rio_zip() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::zip( || RustIO::from_option(Some("hello".to_string())), || RustIO::from_option(Some(" world!!".to_string())), |a,b| RustIO::from_option(Some(a + &b))); @@ -766,12 +803,12 @@ mod tests { #[test] fn rio_parallel() { - let mut parallel_tasks: Vec RustIO> = vec!(); + let mut parallel_tasks: Vec RustIO> = vec!(); parallel_tasks.push(|| RustIO::from_option(Some("hello".to_string()))); parallel_tasks.push(|| RustIO::from_result(Ok(" world".to_string()))); parallel_tasks.push(|| RustIO::of("!!".to_string())); - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::parallel(parallel_tasks,|tasks| RustIO::of(tasks.into_iter().collect())); RustIO::of(v) }; @@ -782,7 +819,7 @@ mod tests { #[test] fn rio_map_error() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Err(String::from("Error A"))) .map_error(|t| String::from("Error B")); RustIO::of(v) @@ -794,7 +831,7 @@ mod tests { #[test] fn rio_when() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))) .when(|v| v.len() > 3, |v| v + &" world!!".to_string()); RustIO::of(v) @@ -806,7 +843,7 @@ mod tests { #[test] fn rio_when_rio() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))) .when_rio(|v| v.len() > 3, |v| RustIO::from_option(Some(v + &" world!!".to_string()))); RustIO::of(v) @@ -818,10 +855,10 @@ mod tests { #[test] fn rio_peek() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello world!!"))) .peek(|v| println!("${}",v)); - RustIO::of(v) + return RustIO::of(v) }; println!("${:?}", rio_program.is_empty()); println!("${:?}", rio_program.is_ok()); @@ -830,7 +867,7 @@ mod tests { #[test] fn rio_fork() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello"))) .fork(|v| { println!("Fork. Variable:{} in Thread:{:?}", v, thread::current().id()); @@ -848,7 +885,7 @@ mod tests { #[test] fn rio_daemon() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_option(Some(String::from("hello world!!"))) .daemon(|v| println!("${}",v)); RustIO::of(v) @@ -860,7 +897,7 @@ mod tests { #[test] fn rio_on_success() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Ok(String::from("hello world!!"))) .on_success(|v| println!("Success program: ${}",v)); RustIO::of(v) @@ -872,7 +909,7 @@ mod tests { #[test] fn rio_on_error() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Err(String::from("burning world!!"))) .on_error(|v| println!("Error program: ${}",v)); RustIO::of(v) @@ -884,7 +921,7 @@ mod tests { #[test] fn rio_eventually() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Ok("hello".to_string())) .at_some_point(|v| get_eventual_result( v)); RustIO::of(v) @@ -896,7 +933,7 @@ mod tests { #[test] fn rio_eventually_while() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Ok("hello".to_string())) .at_some_point_while(|| true,|v| get_eventual_result( v)); RustIO::of(v) @@ -908,7 +945,7 @@ mod tests { #[test] fn rio_eventually_until() { - let rio_program: RustIO = rust_io! { + let rio_program: RustIO = rust_io! { v <- RustIO::from_result(Ok("hello".to_string())) .at_some_point_until(|| { std::thread::sleep(Duration::from_millis(100)); @@ -921,7 +958,31 @@ mod tests { assert_eq!(rio_program.is_failed(), false); } - fn get_eventual_result(v: String) -> RustIO { + #[test] + fn rio_with_environment() { + let rio_program: RustIO = rust_io! { + v <- RustIO::from_result(Ok("hello environment world".to_string())) + .with_env("This is a config environment".to_string()); + RustIO::of(v) + }; + println!("${:?}", rio_program.is_empty()); + println!("${:?}", rio_program.is_ok()); + assert_eq!(rio_program.get(), "hello environment world"); + } + + #[test] + fn rio_environment() { + let rio_program: RustIO = rust_io! { + program <- RustIO::from_result(Ok("${}".to_string())) + .with_env("hello environment world".to_string()); + RustIO::of(program) + }; + println!("${:?}", rio_program.is_empty()); + println!("${:?}", rio_program.is_ok()); + assert_eq!(rio_program.get(), "hello environment world"); + } + + fn get_eventual_result(v: String) -> RustIO { let mut rng = thread_rng(); let n: i32 = rng.gen_range(0..100); println!("${}", n);