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
use crate::prelude::*;
/// [`Functor`], but specialized to know at compile-time
/// that the mapping function will only be called one time.
pub trait FunctorOnce<F, A>
where F: HKT1<T<A> = Self>
{
/// See [`FunctorOnce`]
fn fmap1<AB, B>(self, f: AB) -> F::T<B>
where AB: F1Once<A, Ret = B>;
}
/// Functor adds a mapping operation to generic types.
///
/// In essence, `map` allows one to lift a function of type
/// `fn(A) -> B` to some new Functor context, e.g. `fn(Option<A>) -> Option<B>`.
///
/// # Laws
/// - Invoking map with an identity function (e.g. `|a| a`) should do absolutely nothing.
///
/// ```
/// use naan::prelude::*;
///
/// #[derive(Debug, PartialEq, Eq)]
/// struct Container<T>(T);
/// struct ContainerHKT;
/// impl HKT1 for ContainerHKT {
/// type T<A> = Container<A>;
/// }
///
/// impl<A> Functor<ContainerHKT, A> for Container<A> {
/// fn fmap<AB, B>(self, f: AB) -> Container<B>
/// where AB: F1<A, Ret = B>
/// {
/// Container(f.call(self.0))
/// }
/// }
///
/// assert_eq!(Container(0u8).fmap(|n| n + 1).fmap(|n: u8| n.to_string()),
/// Container("1".to_string()))
/// ```
pub trait Functor<F, A>
where F: HKT1<T<A> = Self>
{
/// Use a function from `A -> B` to transform an
/// `F<A>` to an `F<B>`.
fn fmap<AB, B>(self, f: AB) -> F::T<B>
where AB: F1<A, Ret = B>;
}
/// [`Functor`] but with looser type constraints,
/// allowing for blanket [`Functor`] implementations
/// on types [`Equiv`]alent to `F<A>`
pub trait FunctorSurrogate<F, A>
where F: HKT1,
Self: Equiv<To = F::T<A>>
{
/// Type yielded by `fmap` that is akin to `F::T<B>`.
///
/// The output type may use both type parameters, or only one.
///
/// The reason we allow the output to be parameterized by `AB` (the function from `A -> B`)
/// is so that the returning type can store **the function** and defer transformation.
///
/// This allows implementing lazy functors with no heap dependency (ex. [`IO`])
type Output<AB, B>;
/// Use a function from `A -> B` to transform something
/// akin to `F<A>` to something akin to `F<B>`.
fn map_<AB, B>(self, f: AB) -> Self::Output<AB, B>
where AB: F1<A, Ret = B>,
Self::Output<AB, B>: Equiv<To = F::T<B>>;
}