1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
use crate::prelude::*;
/// [`Apply`], but specialized to know at compile-time
/// that the function contained in `F` will only be called one time.
pub trait ApplyOnce<F, AB>
where Self: Functor<F, AB>,
F: HKT1<T<AB> = Self>
{
/// See [`ApplyOnce`]
fn apply1<A, B>(self, a: F::T<A>) -> F::T<B>
where AB: F1Once<A, Ret = B>;
}
/// `Apply` generalizes [`Functor`] to any arity.
///
/// In other words, you can lift a function
///
/// `A -> B -> C -> D`
///
/// to
///
/// `F<A> -> F<B> -> F<C> -> F<D>`.
///
/// # Example
/// ```
/// use naan::prelude::*;
///
/// fn maybe_n() -> Option<usize> {
/// Some(1)
/// }
///
/// fn maybe_string() -> Option<String> {
/// None
/// }
///
/// fn maybe_bytes() -> Option<Vec<u8>> {
/// None
/// }
///
/// fn combine_many_things(n: usize, string: String, bytes: Vec<u8>) {
/// // ...
/// }
///
/// # fn a() {
/// Some(combine_many_things.curry()).apply(maybe_n())
/// .apply(maybe_string())
/// .apply(maybe_bytes());
///
/// // with `if let`:
/// if let Some(n) = maybe_n() {
/// if let Some(s) = maybe_string() {
/// if let Some(bs) = maybe_bytes() {
/// combine_many_things(n, s, bs);
/// }
/// }
/// }
///
/// // with `let else`:
/// let Some(n) = maybe_n() else {return};
/// let Some(s) = maybe_string() else {return};
/// let Some(bs) = maybe_bytes() else {return};
/// combine_many_things(n, s, bs);
///
/// // with `match`:
/// match (maybe_n(), maybe_string(), maybe_bytes()) {
/// | (Some(n), Some(s), Some(bs)) => combine_many_things(n, s, bs),
/// | _ => (),
/// }
/// # }
/// ```
pub trait Apply<F, AB>
where Self: Functor<F, AB>,
F: HKT1<T<AB> = Self>
{
/// See [`Apply.apply_with`]
fn apply<A, B>(self, a: F::T<A>) -> F::T<B>
where Self: Sized,
AB: F1<A, Ret = B>,
A: Clone
{
self.apply_with(a, Clone::clone)
}
/// Apply the function `A -> B` contained in `Self` (`F<A -> B>`) to
/// an instance of `F<A>` to get `F<B>`.
fn apply_with<A, B, Cloner>(self, a: F::T<A>, cloner: Cloner) -> F::T<B>
where AB: F1<A, Ret = B>,
Cloner: for<'a> F1<&'a A, Ret = A>;
}
/// Adds onto [`Apply`] the ability to lift a _value_
/// to the `F` context.
pub trait Applicative<F, A>
where Self: Apply<F, A>,
F: HKT1<T<A> = Self>
{
/// Lift `A` to `F<A>`
fn pure(a: A) -> F::T<A>;
/// Append a single `A` to `F<A>`
fn append_one(self, a: A) -> Self
where Self: Sized + Semigroup
{
self.append(Self::pure(a))
}
}
/// [`Apply`] but with looser type constraints,
/// allowing for blanket [`Apply`] implementations
/// on types [`Equiv`]alent to `F<AB>`
pub trait ApplySurrogate<F, AB, TofA>
where Self: FunctorSurrogate<F, AB> + Equiv<To = F::T<AB>>,
F: HKT1
{
/// Type returned by `apply_` that is
/// conceptually the same as `F::T<B>`.
///
/// Related: [`FunctorSurrogate::Output`]
type ApplyOutput<A, B>: Equiv<To = F::T<B>>;
/// Apply the function `A -> B` contained in `Self` (`F<A -> B>`) to
/// an instance of `F<A>` to get `F<B>`.
fn apply_<A, B>(self, a: TofA) -> Self::ApplyOutput<A, B>
where AB: F1Once<A, Ret = B>;
}
/// [`Applicative`] but with looser type constraints,
/// allowing for blanket [`Applicative`] implementations
/// on types [`Equiv`]alent to `F<A>`
pub trait ApplicativeSurrogate<F, A>
where Self: Sized + ApplySurrogate<F, A, Self> + Equiv<To = F::T<A>>,
F: HKT1
{
/// Lift `A` to `F<A>`
fn pure(a: A) -> F::T<A>;
}