A library for functional programming in Rust.
By default, the library is built with the std feature enabled. To disable it, use the --no-default-features flag.
Add this to your Cargo.toml:
[dependencies]
rust2fun = "0.2.0"and import the prelude:
use rust2fun::prelude::*;- function composition (with compose macro)
- pipelines (with pipe macro)
- currying
- argument flipping (with flip macro)
- constant functions
- id (I combinator)
- apply (A combinator)
- apply_to (T combinator)
- substitution (S combinator)
- converge
- on
- if_else
- fix (Y combinator)
- no operation
- tuple constructors
- Semigroup
- Monoid
- Semigroupal
- Invariant
- Functor
- Bifunctor
- Apply
- Applicative
- FlatMap
- Monad + ( bind! notation )
- NEVec (non-empty vector)
- Validated
- ValidatedNev
- Function
print_user_credit_cardaccepts 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());- 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) -> Result<CreditCardNumber, Error> {
// Validating credit card number
}
fn validate_expiration(date: Date) -> Result<Date, Error> {
// Validating expiration date
}
fn validate_cvv(cvv: Code) -> Result<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).into(),
validate_expiration(expiration).into(),
validate_cvv(cvv).into())
}...alternatively, this can be done using the map3 method:
fn validate_credit_card(
number: CreditCardNumber,
expiration: Date,
cvv: Code,
) -> ValidatedNev<CreditCard, Error> {
let number: ValidatedNev<_, _> = validate_number(number).into();
let expiration = validate_expiration(expiration).into();
let cvv = validate_cvv(cvv).into();
Apply::map3(number, expiration, cvv, CreditCard::new)
}bind!notation for monads (likedonotation in Haskell orforcomprehension 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 get_asset_name(id_open).into_iter().collect::<Vec<_>>(),
if id_open == id_close && diff > 0;
(name, diff)
};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
noopXandtupleXsets of functions - Added type classes: Semigroup, Monoid, Bifunctor + Higher2 (thanks lrind)
- Added data types: NEVec, Validated
- Added
bind!notation - Multiple fixes and improvements