rust2fun/
apply.rs

1//! Apply.
2
3use core::marker::PhantomData;
4
5use crate::functor::Functor;
6
7/// Weaker version of Applicative has apply but not pure.
8pub trait Apply<A, B>: Functor<B> {
9    /// Apply a function in a context to a value in a context.
10    ///
11    /// # Examples
12    ///
13    /// ```
14    /// use rust2fun::prelude::*;
15    ///
16    /// let ff = Some(|x: i32| x.to_string());
17    /// assert_eq!(Some("1".to_string()), ff.ap(Some(1)));
18    /// assert_eq!(Some("2".to_string()), ff.ap(Some(2)));
19    ///
20    /// let ff = vec![|x| x + 1, |x| x + 2];
21    /// let fa = vec![3, 4];
22    /// let actual = ff.ap(fa);
23    /// assert_eq!(actual, [4, 5, 5, 6]);
24    /// ```
25    fn ap(self, fa: Self::Target<A>) -> Self::Target<B>
26    where
27        Self::Param: FnMut(A) -> B;
28}
29
30/// Macro to implement [Apply] for types with [Iterator] support.
31#[macro_export]
32macro_rules! apply_iter {
33    ($name:ident) => {
34        impl<F, A: Clone, B> $crate::apply::Apply<A, B> for $name<F> {
35            #[inline]
36            fn ap(self, fa: Self::Target<A>) -> Self::Target<B>
37            where
38                Self::Param: FnMut(A) -> B,
39            {
40                self.into_iter()
41                    .flat_map(|mut f| fa.clone().into_iter().map(move |a| f(a)))
42                    .collect::<$name<B>>()
43            }
44        }
45    };
46    ($name:ident, $ct:tt $(+ $dt:tt )*) => {
47        impl<F: $ct $(+ $dt )*, A: Clone, B: $ct $(+ $dt )*> $crate::apply::Apply<A, B> for $name<F> {
48            #[inline]
49            fn ap(self, fa: Self::Target<A>) -> Self::Target<B>
50            where
51                Self::Param: FnMut(A) -> B,
52            {
53                self.into_iter()
54                    .flat_map(|mut f| fa.clone().into_iter().map(move |a| f(a)))
55                    .collect::<$name<B>>()
56            }
57        }
58    };
59}
60
61impl<F, A, B> Apply<A, B> for PhantomData<F> {
62    #[inline]
63    fn ap(self, _fa: PhantomData<A>) -> PhantomData<B>
64    where
65        F: FnOnce(A) -> B,
66    {
67        PhantomData
68    }
69}
70
71impl<F, A, B> Apply<A, B> for Option<F> {
72    #[inline]
73    fn ap(self, fa: Option<A>) -> Option<B>
74    where
75        F: FnOnce(A) -> B,
76    {
77        match (self, fa) {
78            (Some(f), Some(a)) => Some(f(a)),
79            _ => None,
80        }
81    }
82}
83
84impl<F, A, B, E> Apply<A, B> for Result<F, E> {
85    #[inline]
86    fn ap(self, fa: Result<A, E>) -> Result<B, E>
87    where
88        F: FnOnce(A) -> B,
89    {
90        match (self, fa) {
91            (Ok(f), Ok(a)) => Ok(f(a)),
92            (Err(e), _) => Err(e),
93            (_, Err(e)) => Err(e),
94        }
95    }
96}
97
98if_std! {
99    use std::boxed::Box;
100    use std::collections::*;
101    use std::hash::Hash;
102    use std::vec::Vec;
103
104    impl<F, A, B> Apply<A, B> for Box<F> {
105        #[inline]
106        fn ap(self, fa: Box<A>) -> Box<B>
107        where
108            F: FnOnce(A) -> B,
109        {
110            Box::new((*self)(*fa))
111        }
112    }
113
114    apply_iter!(Vec);
115    apply_iter!(LinkedList);
116    apply_iter!(VecDeque);
117    apply_iter!(BinaryHeap, Ord);
118    apply_iter!(BTreeSet, Ord);
119    apply_iter!(HashSet, Eq + Hash);
120
121    impl<F, A, B, K: Eq + Hash> Apply<A, B> for HashMap<K, F> {
122        #[inline]
123        fn ap(mut self, fa: HashMap<K, A>) -> HashMap<K, B>
124        where
125            F: FnOnce(A) -> B,
126        {
127            fa.into_iter()
128                .filter_map(|(k, a)| self.remove(&k).map(|f| (k, f(a))))
129                .collect()
130        }
131    }
132}