raskell 0.1.1

Haskell-style functional programming for Rust
Documentation
use raskell::prelude::*;

#[derive(Debug, PartialEq)]
enum UserError {
    InvalidSession,
    Inactive,
    TooYoung,
    NoNickname,
}

#[derive(Debug, PartialEq)]
enum StoreError {
    Unavailable,
}

impl From<StoreError> for UserError {
    fn from(_: StoreError) -> Self {
        UserError::InvalidSession
    }
}

#[derive(Debug)]
struct User {
    name: String,
    age: u8,
    active: bool,
}

fn validate_session() -> Result<(), UserError> {
    if false {
        return Err(UserError::InvalidSession);
    }

    Ok(())
}

fn get_user() -> Result<User, StoreError> {
    if false {
        return Err(StoreError::Unavailable);
    }

    Ok(User {
        name: "Meetzli".into(),
        age: 18,
        active: true,
    })
}

fn find_nickname(user: &User) -> Result<Option<String>, UserError> {
    Ok(Some(user.name.to_lowercase()))
}

fn main() {
    let greeting: Result<String, UserError> = hdo! {
        validate_session();

        // `<-?` converts `StoreError` into `UserError` through `From`.
        user <-? get_user();

        guard user.active throw UserError::Inactive;
        guard user.age >= 18 throw UserError::TooYoung;

        Some(nickname) <- find_nickname(&user) throw UserError::NoNickname;

        let shout = nickname.to_uppercase();

        pure format!("hello, {shout}!")
    };

    println!("{greeting:?}");

    // Type ascription pins the bound value when the expression carries no type.
    let doubled: Result<i32, UserError> = hdo! {
        Some(x): Option<i32> <- Err(UserError::Inactive) throw UserError::TooYoung;

        pure x * 2
    };

    println!("{doubled:?}");

    // A `Vec` bind is a list comprehension.
    let pythagorean: Vec<(i32, i32, i32)> = hdo! {
        a <- (1..=20).collect::<Vec<_>>();
        b <- (a..=20).collect::<Vec<_>>();
        c <- (b..=20).collect::<Vec<_>>();

        guard a * a + b * b == c * c;

        pure (a, b, c)
    };

    println!("{pythagorean:?}");
}