Skip to content

chapiteau-team/rust2fun

Repository files navigation

rust2fun (pronounced: rʌstafʌn)

Crates.io docs.rs build

A library for functional programming in Rust.

Build

By default, the library is built with the std feature enabled. To disable it, use the --no-default-features flag.

Usage

Add this to your Cargo.toml:

[dependencies]
rust2fun = "0.2.1"

and import the prelude:

use rust2fun::prelude::*;

Supported features

Combinators:

Type classes:

Data types:

Examples

  1. Function print_user_credit_card accepts user(s) wrapped in any effect (Option, Result, Vec, etc.) and prints corresponding credit card(s).
fn get_credit_card(user: User) -> CreditCard {
    // Get credit card for user
}

fn print_credit_card(card: CreditCard) {
    // Print credit card details
}

fn print_credit_card_of<F>(user: F)
    where
        F: Functor<CreditCard, Param=User>,
        F::Target<CreditCard>: Functor<(), Param=CreditCard>,
{
    user.map(get_credit_card).map(print_credit_card);
}

...usage:

fn user(id: u32) -> Option<User> {
    // Get user from database
}

fn all_users() -> Vec<User> {
    // Get all users from database
}

print_credit_card_of(user(1));
print_credit_card_of(all_users());
  1. Validation accumulating all errors.

Assuming we have the following validation rules that need to be applied to create a new credit card:

fn validate_number(number: CreditCardNumber) -> ValidatedNev<CreditCardNumber, Error> {
    // Validating credit card number
}

fn validate_expiration(date: Date) -> ValidatedNev<Date, Error> {
    // Validating expiration date
}

fn validate_cvv(cvv: Code) -> ValidatedNev<Code, Error> {
    // Validating CVV code
}

...we can create a new credit card by applying all validation rules and collecting all errors in a vector Vec, non-empty vector NEVec (like in the example) or other semigroup (e.g. String, u32, etc.):

fn validate_credit_card(
    number: CreditCardNumber,
    expiration: Date,
    cvv: Code,
) -> ValidatedNev<CreditCard, Error> {
    ValidatedNev::pure(CreditCard::new)
        .ap3(validate_number(number),
             validate_expiration(expiration),
             validate_cvv(cvv))
}

...alternatively, this can be done using the map3 method:

fn validate_credit_card(
    number: CreditCardNumber,
    expiration: Date,
    cvv: Code,
) -> ValidatedNev<CreditCard, Error> { 
    MapN::map3(validate_number(number),
               validate_expiration(expiration),
               validate_cvv(cvv),
               CreditCard::new)
}
  1. bind! notation for monads (like do notation in Haskell or for comprehension in Scala):

Assuming we have the following functions defined:

fn get_opening_prices() -> Vec<(AssetId, i32)> {
  // Get opening prices from an external service
}

fn get_closing_prices() -> Vec<(AssetId, i32)> {
  // Get closing prices from an external service
}

fn get_asset_name(id: AssetId) -> Option<String> {
  // Recover asset name for the given id
}

...we can use bind! notation to calculate daily profit for each asset:

let profits: Vec<(String, i32)> = bind! {
    for (id_open, opening_price) in get_opening_prices();
    for (id_close, closing_price) in get_closing_prices();
    let diff = closing_price - opening_price;
    for name in OptionToVec.apply(get_asset_name(id_open)),
        if id_open == id_close && diff > 0;
    (name, diff)
};

Release notes

0.1.0 (2023-01-22)

  • Initial release: combinators, Semigroupal, Invariant, Functor, Apply, Applicative, FlatMap, Monad

0.2.0 (2023-09-10)

  • The project got its logo (thanks olasinitsyna)
  • Moved macros imports to the prelude
  • Added noopX and tupleX sets of functions
  • Added type classes: Semigroup, Monoid, Bifunctor + Higher2 (thanks lrind)
  • Added data types: NEVec, Validated
  • Added bind! notation
  • Multiple fixes and improvements

0.2.1 (2023-09-21)

  • Fixed Semigroupal and Apply behavior (thanks GoldsteinE for the report)
  • Added type classes: Pure, AndThen
  • Refactored mapX and apX functions