Skip to content

Commit

Permalink
add map3 into Apply
Browse files Browse the repository at this point in the history
add ap3 into Apply
  • Loading branch information
kalaninja committed Sep 1, 2023
1 parent e24de24 commit 81cdd12
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 41 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,26 @@ fn validate_credit_card(
expiration: Date,
cvv: Code,
) -> ValidatedNev<CreditCard, Error> {
ValidatedNev::pure(curry3!(CreditCard::new))
.ap(validate_number(number).into())
.ap(validate_expiration(expiration).into())
.ap(validate_cvv(cvv).into())
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<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)
}
```

Expand Down
106 changes: 76 additions & 30 deletions src/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
/// assert_eq!(Some("2".to_string()), f.ap(Some(2)));
/// ```
fn ap<A>(self, fa: Self::Target<A>) -> Self::Target<B>
where
Self::Param: FnOnce(A) -> B;
where
Self::Param: FnOnce(A) -> B;

/// Is a binary version of [ap].
///
Expand All @@ -33,23 +33,45 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
/// let f = Some(|x, y| x + y);
/// assert_eq!(Some(3), f.ap2(Some(1), Some(2)));
/// ```
#[inline]
fn ap2<A, Z>(self, fa: Self::Target<A>, fb: Self::Target<B>) -> Self::Target<Z>
where
Self::Target<(Self::Param, B)>: Semigroupal<A, Target<A> = Self::Target<A>>
+ Higher<
Target<(<Self::Target<(Self::Param, B)> as Higher>::Param, A)> = Self::Target<(
<Self::Target<(Self::Param, B)> as Higher>::Param,
A,
)>,
>,
Self::Target<(<Self::Target<(Self::Param, B)> as Higher>::Param, A)>:
Functor<Z, Target<Z> = Self::Target<Z>>,
Self: Sized,
Self::Param: FnOnce(A, B) -> Z,
where
Self::Param: FnOnce(A, B) -> Z,
Self::Target<(Self::Param, B)>: Semigroupal<A>
+ Higher<Target<A>=Self::Target<A>>
+ Higher<Target<((Self::Param, B), A)>=Self::Target<((Self::Param, B), A)>>,
Self::Target<((Self::Param, B), A)>: Functor<Z, Target<Z>=Self::Target<Z>>,
Self: Sized,
{
self.product(fb).product(fa).map(|((f, b), a)| f(a, b))
}

/// Is a ternary version of [ap].
///
/// # Examples
///
/// ```
/// use rust2fun::prelude::*;
///
/// let f = Some(|x, y, z| x + y + z);
/// assert_eq!(Some(6), f.ap3(Some(1), Some(2), Some(3)));
/// ```
#[inline]
fn ap3<A, C, Z>(self, fa: Self::Target<A>, fb: Self::Target<B>, fc: Self::Target<C>) -> Self::Target<Z>
where
Self::Param: FnOnce(A, B, C) -> Z,
Self::Target<(Self::Param, B)>: Semigroupal<A>
+ Higher<Target<A>=Self::Target<A>>
+ Higher<Target<((Self::Param, B), A)>=Self::Target<((Self::Param, B), A)>>,
Self::Target<((Self::Param, B), A)>: Semigroupal<C>
+ Higher<Target<C>=Self::Target<C>>
+ Higher<Target<(((Self::Param, B), A), C)>=Self::Target<(((Self::Param, B), A), C)>>,
Self::Target<(((Self::Param, B), A), C)>: Functor<Z, Target<Z>=Self::Target<Z>>,
Self: Sized,
{
self.product(fb).product(fa).product(fc).map(|(((f, b), a), c)| f(a, b, c))
}

/// Combine two effectful values into a single effectful value using a binary function.
///
/// # Examples
Expand All @@ -62,15 +84,39 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
/// let actual = x.map2(y, |x, y| x + y);
/// assert_eq!(Some(3), actual);
/// ```
#[inline]
fn map2<Z, F>(self, fb: Self::Target<B>, mut f: F) -> Self::Target<Z>
where
F: FnMut(Self::Param, B) -> Z,
Self::Target<(Self::Param, B)>: Functor<Z, Target<Z> = Self::Target<Z>>,
Self: Sized,
where
F: FnMut(Self::Param, B) -> Z,
Self::Target<(Self::Param, B)>: Functor<Z, Target<Z>=Self::Target<Z>>,
Self: Sized,
{
self.product(fb).map(|(a, b)| f(a, b))
}

/// Combine three effectful values into a single effectful value using a ternary function.
///
/// # Examples
///
/// ```
/// use rust2fun::prelude::*;
///
/// let x = Some(1);
/// let y = Some(2);
/// let z = Some(3);
/// let actual = x.map3(y, z, |x, y, z| x + y + z);
/// assert_eq!(Some(6), actual);
/// ```
#[inline]
fn map3<C, Z, F>(self, fb: Self::Target<B>, fc: Self::Target<C>, mut f: F) -> Self::Target<Z>
where F: FnMut(Self::Param, B, C) -> Z,
Self::Target<(Self::Param, B)>: Apply<C, Target<C>=Self::Target<C>>,
Self::Target<(Self::Param, B)>: Higher<Target<Z>=Self::Target<Z>>,
<Self::Target<(Self::Param, B)> as Higher>::Target<((Self::Param, B), C)>: Functor<Z, Target<Z>=Self::Target<Z>>,
Self: Sized, {
self.product(fb).map2(fc, |(a, b), c| { f(a, b, c) })
}

/// Compose two effectful values discarding the result of the first.
///
/// # Examples
Expand All @@ -85,9 +131,9 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
/// ```
#[inline]
fn product_r(self, fb: Self::Target<B>) -> Self::Target<B>
where
Self::Target<(Self::Param, B)>: Functor<B, Target<B> = Self::Target<B>>,
Self: Sized,
where
Self::Target<(Self::Param, B)>: Functor<B, Target<B>=Self::Target<B>>,
Self: Sized,
{
self.map2(fb, |_, b| b)
}
Expand All @@ -106,9 +152,9 @@ pub trait Apply<B>: Functor<B> + Semigroupal<B> {
/// ```
#[inline]
fn product_l(self, fb: Self::Target<B>) -> Self
where
Self::Target<(Self::Param, B)>: Functor<Self::Param, Target<Self::Param> = Self>,
Self: Higher<Target<<Self as Higher>::Param> = Self> + Sized,
where
Self::Target<(Self::Param, B)>: Functor<Self::Param, Target<Self::Param>=Self>,
Self: Higher<Target<<Self as Higher>::Param>=Self> + Sized,
{
self.map2(fb, |a, _| a)
}
Expand Down Expand Up @@ -150,8 +196,8 @@ macro_rules! apply_iter {
impl<F, B> Apply<B> for PhantomData<F> {
#[inline]
fn ap<A>(self, _fa: PhantomData<A>) -> PhantomData<B>
where
Self::Param: FnOnce(A) -> B,
where
Self::Param: FnOnce(A) -> B,
{
PhantomData
}
Expand All @@ -160,8 +206,8 @@ impl<F, B> Apply<B> for PhantomData<F> {
impl<F, B> Apply<B> for Option<F> {
#[inline]
fn ap<A>(self, fa: Option<A>) -> Option<B>
where
Self::Param: FnOnce(A) -> B,
where
Self::Param: FnOnce(A) -> B,
{
self.and_then(|f| fa.map(f))
}
Expand All @@ -170,8 +216,8 @@ impl<F, B> Apply<B> for Option<F> {
impl<F, B, E> Apply<B> for Result<F, E> {
#[inline]
fn ap<A>(self, fa: Result<A, E>) -> Result<B, E>
where
Self::Param: FnOnce(A) -> B,
where
Self::Param: FnOnce(A) -> B,
{
self.and_then(|f| fa.map(f))
}
Expand Down
26 changes: 19 additions & 7 deletions src/data/validated/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@
//! # }
//! # }
//! #
//! fn validate_number(number: CreditCardNumber) -> Validated<CreditCardNumber, Error> {
//! fn validate_number(number: CreditCardNumber) -> ValidatedNev<CreditCardNumber, Error> {
//! unimplemented!("Validate credit card number")
//! }
//!
//! fn validate_expiration(date: Date) -> Validated<Date, Error> {
//! fn validate_expiration(date: Date) -> ValidatedNev<Date, Error> {
//! unimplemented!("Validate credit card expiration date")
//! }
//!
//! fn validate_cvv(cvv: Code) -> Validated<Code, Error> {
//! fn validate_cvv(cvv: Code) -> ValidatedNev<Code, Error> {
//! unimplemented!("Validate credit card cvv")
//! }
//!
Expand All @@ -57,10 +57,22 @@
//! expiration: Date,
//! cvv: Code,
//! ) -> ValidatedNev<CreditCard, Error> {
//! ValidatedNev::pure(curry3!(CreditCard::new))
//! .ap(validate_number(number).into())
//! .ap(validate_expiration(expiration).into())
//! .ap(validate_cvv(cvv).into())
//! ValidatedNev::pure(CreditCard::new)
//! .ap3(validate_number(number),
//! validate_expiration(expiration),
//! validate_cvv(cvv))
//! }
//!
//! // Alternative implementation using `map3`:
//! fn validate_credit_card_alt(
//! number: CreditCardNumber,
//! expiration: Date,
//! cvv: Code,
//! ) -> ValidatedNev<CreditCard, Error> {
//! Apply::map3(validate_number(number),
//! validate_expiration(expiration),
//! validate_cvv(cvv),
//! CreditCard::new)
//! }
//! ```
pub use Validated::{Invalid, Valid};
Expand Down

0 comments on commit 81cdd12

Please sign in to comment.