#operator #macro #notation #functional #monads #emulating #io

rust_io

Macro implementation for [rust_io] defining several operators to be used emulating Haskel [do notation]

6 releases (breaking)

0.6.0 Jun 10, 2023
0.5.0 Jun 9, 2023
0.4.0 Jun 9, 2023
0.3.0 Jun 9, 2023
0.1.0 Jun 9, 2023

#1166 in Rust patterns

Apache-2.0

38KB
784 lines

Rust_IO

Macro implementation for [rust_io] defining several operators to be used emulating Haskel [do notation]

Use

Simple example of how to use rust_io! macro and RustIO monad

#[cfg(test)]
mod tests {
    
    use rust_io::{rust_io, RustIO};
    use rust_io::{Lift};

    #[test]
    fn rio() {
        let rio_program: RustIO<String, String> = rust_io! {
             _ <- RustIO::of(String::from("1981"));
             v <- RustIO::from_option(Some(String::from("hello")));
             t <- RustIO::from_option_func(|| Some(String::from(" pure")));
             z <- RustIO::from_func(|| String::from(" functional"));
             x <- RustIO::from_result(Ok(String::from(" world")));
             i <- RustIO::of(String::from("!!"));
             y <- RustIO::from_result_func(|| Ok(String::from("!!")));

             yield v + &t + &z + &x + &i + &y;
        };
        println!("${:?}", rio_program.is_empty());
        println!("${:?}", rio_program.is_ok());
        assert_eq!(rio_program.get(), "hello pure functional world!!!!");
    }
}

Program

Example of a RustIO program using many of the operators available in the Monad.

    #[test]
    fn features() {
        let rio_program: RustIO<String, String> =
            RustIO::from_option(Some(String::from("hello")))
                .when(|v| v.len() > 3, |v| v + &" world!!".to_string())
                .at_some_point(|v| get_eventual_result(v))
                .map(|v| v.to_uppercase())
                .flat_map(|v| RustIO::of(v + &"!!!".to_string()))
                .filter(|v| v.len() > 10)
                .delay(Duration::from_secs(1))
                .on_error(|v| println!("Error program: ${}", v))
                .map_error(|t| String::from("Error B"))
                .on_success(|v| println!("Success program: ${}", v))
                .peek(|v| println!("${}", v));

        println!("${:?}", rio_program.is_empty());
        println!("${:?}", rio_program.is_ok());
        assert_eq!(false, rio_program.is_empty());
        assert_eq!(true, rio_program.is_ok());

    }

Operators

Trait definition of all operators available with RustIO.

///Specification to be implemented by a monad.
/// [lift] a value into a default structure.
/// Operators to create monad:
/// [of][from_func][from_option_func][from_result_func][from_option][from_result][merge]
/// Operators to transform monads
/// [map][fold][map_error]
/// Operators to compose monads
/// [flat_map][zip]
/// Operators to filter monads
/// [filter]
/// Operators to filter and transform monad in one transaction
/// [when][when_rio]
/// Operators to recover from side-effects
/// [recover][recover_with][eventually]
/// To slow the monad execution
/// [delay]
/// To unwrap the value from monad.
/// [get][get_or_else]
/// Check the state of the monad
/// [is_ok][is_failed][is_empty]
/// Async task executions
/// [parallel][fork]
pub trait Lift<A, T> {
    fn lift(a: A) -> Self;

    fn of(a: A) -> Self;

    fn from_func(f: fn() -> A) -> Self;

    fn from_option_func(f: fn() -> Option<A>) -> Self;

    fn from_result_func(f: fn() -> Result<A, T>) -> Self;

    fn from_option(a: Option<A>) -> Self;

    fn from_result(a: Result<A, T>) -> Self;

    fn merge<F: FnOnce(A, A) -> Self>(a: Self, b: Self, op: F) -> Self;

    fn get(self) -> A;

    fn failed(self) -> T;

    fn get_or_else(self, default: A) -> A;

    fn is_ok(&self) -> bool;

    fn is_failed(&self) -> bool;

    fn is_empty(&self) -> bool;

    fn map<F: FnOnce(A) -> A>(self, op: F) -> Self;

    fn map_error<F: FnOnce(T) -> T>(self, op: F) -> Self;

    fn flat_map<F: FnOnce(A) -> Self>(self, op: F) -> Self;

    fn at_some_point<F: FnOnce(A) -> Self>(self, op: F) -> Self where A: Clone, F: Clone;

    fn at_some_point_while<P: FnOnce() -> bool, F: FnOnce(A) -> Self>(self, predicate: P, op: F) -> Self where A: Clone, P: Clone, F: Clone;

    fn at_some_point_until<P: FnOnce() -> bool, F: FnOnce(A) -> Self>(self, predicate: P, op: F) -> Self where A: Clone, P: Clone, F: Clone;

    fn when<P: FnOnce(&A) -> bool, F: FnOnce(A) -> A>(self, predicate: P, op: F) -> Self;

    fn when_rio<P: FnOnce(&A) -> bool, F: FnOnce(A) -> Self>(self, predicate: P, op: F) -> Self;

    fn zip<Z1: FnOnce() -> Self, Z2: FnOnce() -> Self, F: FnOnce(A, A) -> Self>(a: Z1, b: Z2, op: F) -> Self;

    fn filter<F: FnOnce(&A) -> bool>(self, op: F) -> Self;

    fn fold<F: FnOnce(A) -> A>(self, default: A, op: F) -> Self;

    fn recover<F: FnOnce() -> A>(self, op: F) -> Self;

    fn recover_with<F: FnOnce() -> Self>(self, op: F) -> Self;

    fn delay(self, time: Duration) -> Self;

    fn parallel<Task: FnOnce() -> Self, F: FnOnce(Vec<A>) -> Self>(tasks: Vec<Task>, op: F) -> Self;

    /// Provide [A:'static] in the definition it can extend the lifetime of a specific type
    fn fork<F: FnOnce(A) -> A>(self, op: F) -> Self where A: 'static, F: 'static;

    /// Provide [A:'static] in the definition it can extend the lifetime of a specific type
    fn join(self) -> Self;

    fn daemon<F: FnOnce(&A) -> ()>(self, op: F) -> Self;

    fn peek<F: FnOnce(&A) -> ()>(self, op: F) -> Self;

    fn on_error<F: FnOnce(&T) -> ()>(self, op: F) -> Self;

    fn on_success<F: FnOnce(&A) -> ()>(self, op: F) -> Self;
}

Dependencies

~5–18MB
~204K SLoC