# rust2fun (pronounced: rʌstafʌn)
[![Crates.io](https://img.shields.io/crates/v/rust2fun.svg)](https://crates.io/crates/rust2fun)
[![docs.rs](https://img.shields.io/docsrs/rust2fun)](https://docs.rs/rust2fun/0.2.1/rust2fun/)
![build](https://github.com/chapiteau-team/rust2fun/actions/workflows/rust.yml/badge.svg)
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`:
```toml
[dependencies]
rust2fun = "0.2.1"
```
and import the prelude:
```rust
use rust2fun::prelude::*;
```
## Supported features
### Combinators:
- function composition (with [compose](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.compose.html) macro)
- pipelines (with [pipe](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.pipe.html) macro)
- currying
- [curry2](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.curry2.html) macro
- [curry3](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.curry3.html) macro
- etc
- argument flipping (with [flip](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.flip.html) macro)
- constant functions
- [constant](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.constant.html) macro
- [constant1](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.constant1.html) macro (K combinator)
- [constant2](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.constant2.html) macro
- etc
- [id](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.id.html) (I combinator)
- [apply](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.apply.html) (A combinator)
- [apply_to](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.apply_to.html) (T combinator)
- [substitution](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.substitution.html) (S combinator)
- [converge](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.converge.html)
- [on](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.on.html) (Psi combinator)
- [if_else](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.if_else.html)
- [fix](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.fix.html) (Y combinator)
- no operation
- [noop](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.noop.html)
- [noop1](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.noop1.html)
- [noop2](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.noop2.html)
- etc
- tuple constructors
- [tuple2](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.tuple2.html)
- [tuple3](https://docs.rs/rust2fun/0.2.1/rust2fun/combinator/fn.tuple3.html)
- etc
### Type classes:
- [Semigroup](https://docs.rs/rust2fun/0.2.1/rust2fun/semigroup/trait.Semigroup.html)
- [Monoid](https://docs.rs/rust2fun/0.2.1/rust2fun/monoid/trait.Monoid.html)
- [Semigroupal](https://docs.rs/rust2fun/0.2.1/rust2fun/semigroupal/trait.Semigroupal.html)
- [Invariant](https://docs.rs/rust2fun/0.2.1/rust2fun/invariant/trait.Invariant.html)
- [Functor](https://docs.rs/rust2fun/0.2.1/rust2fun/functor/trait.Functor.html)
- [Bifunctor](https://docs.rs/rust2fun/0.2.1/rust2fun/bifunctor/trait.Bifunctor.html)
- [Pure](https://docs.rs/rust2fun/0.2.1/rust2fun/pure/trait.Pure.html)
- [AndThen](https://docs.rs/rust2fun/0.2.1/rust2fun/and_then/trait.AndThen.html)
- [Apply](https://docs.rs/rust2fun/0.2.1/rust2fun/apply/trait.Apply.html)
- [Applicative](https://docs.rs/rust2fun/0.2.1/rust2fun/applicative/trait.Applicative.html)
- [FlatMap](https://docs.rs/rust2fun/0.2.1/rust2fun/flatmap/trait.FlatMap.html)
- [Monad](https://docs.rs/rust2fun/0.2.1/rust2fun/monad/trait.Monad.html) + ( [bind!](https://docs.rs/rust2fun/0.2.1/rust2fun/macro.bind.html) notation )
### Data types:
- [NEVec](https://docs.rs/rust2fun/0.2.1/rust2fun/data/ne_vec/struct.NEVec.html) (non-empty vector)
- [Validated](https://docs.rs/rust2fun/0.2.1/rust2fun/data/validated/enum.Validated.html)
- [ValidatedNev](https://docs.rs/rust2fun/0.2.1/rust2fun/data/validated/type.ValidatedNev.html)
## Examples
1. Function `print_user_credit_card` accepts user(s) wrapped in any effect (Option, Result, Vec, etc.) and prints
corresponding credit card(s).
```rust
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(user: F)
where
F: Functor,
F::Target: Functor<(), Param=CreditCard>,
{
user.map(get_credit_card).map(print_credit_card);
}
```
...usage:
```rust
fn user(id: u32) -> Option {
// Get user from database
}
fn all_users() -> Vec {
// Get all users from database
}
print_credit_card_of(user(1));
print_credit_card_of(all_users());
```
2. Validation accumulating all errors.
Assuming we have the following validation rules that need to be applied to create a new credit card:
```rust
fn validate_number(number: CreditCardNumber) -> Result {
// Validating credit card number
}
fn validate_expiration(date: Date) -> Result {
// Validating expiration date
}
fn validate_cvv(cvv: Code) -> Result {
// 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.):
```rust
fn validate_credit_card(
number: CreditCardNumber,
expiration: Date,
cvv: Code,
) -> ValidatedNev {
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:
```rust
fn validate_credit_card(
number: CreditCardNumber,
expiration: Date,
cvv: Code,
) -> ValidatedNev {
let number: ValidatedNev<_, _> = validate_number(number).into();
let expiration = validate_expiration(expiration).into();
let cvv = validate_cvv(cvv).into();
MapN::map3(number, expiration, cvv, CreditCard::new)
}
```
3. `bind!` notation for monads (like `do` notation in Haskell or `for` comprehension in Scala):
Assuming we have the following functions defined:
```rust
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 {
// Recover asset name for the given id
}
```
...we can use `bind!` notation to calculate daily profit for each asset:
```rust
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::>(),
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](https://www.behance.net/olasinitsyna))
- Moved macros imports to the prelude
- Added `noopX` and `tupleX` sets of functions
- Added type classes: Semigroup, Monoid, Bifunctor + Higher2 (thanks [lrind](https://github.com/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](https://github.com/GoldsteinE) for the report)
- Added type classes: Pure, AndThen
- Refactored `mapX` and `apX` functions