scsys_core/cont/traits/
hkt.rs

1/*
2    Appellation: hkt <module>
3    Contrib: @FL03
4*/
5
6#[allow(clippy::upper_case_acronyms)]
7/// The [`HKT`] trait defines an interface for higher-kinded types (HKT).
8pub trait HKT<T> {
9    type Cont<_T>;
10}
11
12/// The [`Functor`] trait extends the [`HKT`] trait to provide a way to map over its content(s)
13/// using a function `f` that transforms values of type `T` into values of type `U`.
14pub trait Functor<T>: HKT<T> {
15    fn fmap<F, U>(&self, f: F) -> Self::Cont<U>
16    where
17        F: Fn(&T) -> U;
18}
19
20/*
21 *************  Implementations  *************
22*/
23macro_rules! hkt {
24    (@impl $($t:ident)::*<$T:ident>) => {
25        impl<$T> HKT<$T> for $($t)::*<$T> {
26            type Cont<_T> = $($t)::*<_T>;
27        }
28    };
29    (
30        $(
31            $($t:ident)::*<$T:ident>
32        ),* $(,)?
33    ) => {
34        $(
35            hkt!(@impl $($t)::*<$T>);
36        )*
37    };
38}
39
40hkt! {
41    core::option::Option<T>
42}
43
44#[cfg(feature = "alloc")]
45hkt! {
46    alloc::vec::Vec<T>,
47    alloc::boxed::Box<T>,
48    alloc::rc::Rc<T>,
49    alloc::rc::Weak<T>,
50    alloc::sync::Arc<T>,
51    alloc::collections::BinaryHeap<T>,
52    alloc::collections::BTreeSet<T>,
53    alloc::collections::LinkedList<T>,
54    alloc::collections::VecDeque<T>,
55}
56
57#[cfg(feature = "std")]
58hkt! {
59    std::cell::Cell<T>,
60    std::cell::OnceCell<T>,
61    std::cell::RefCell<T>,
62    std::sync::Mutex<T>,
63    std::sync::RwLock<T>,
64    std::sync::LazyLock<T>,
65    std::collections::HashSet<V>,
66}
67
68#[cfg(feature = "alloc")]
69impl<K, V> HKT<V> for alloc::collections::BTreeMap<K, V> {
70    type Cont<U> = alloc::collections::BTreeMap<K, U>;
71}
72
73#[cfg(feature = "std")]
74impl<K, V> HKT<V> for std::collections::HashMap<K, V> {
75    type Cont<U> = std::collections::HashMap<K, U>;
76}
77
78impl<T> Functor<T> for Option<T> {
79    fn fmap<F, U>(&self, f: F) -> Self::Cont<U>
80    where
81        F: Fn(&T) -> U,
82    {
83        self.as_ref().map(f)
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_option() {
93        let opt = Some(42u8);
94        let opt2 = opt.fmap(|&x| x as usize + 1);
95        assert_eq!(opt2, Some(43_usize));
96    }
97}