partial_functional/
functor.rs

1use crate::HKT;
2
3pub trait Functor<A, B>: HKT<A, B> {
4    fn fmap<F: FnOnce(A) -> B>(self, f: F) -> <Self as HKT<A, B>>::Target;
5}
6
7impl<A, B> Functor<A, B> for Option<A> {
8    fn fmap<F: FnOnce(A) -> B>(self, f: F) -> <Self as HKT<A, B>>::Target {
9        self.map(f)
10    }
11}
12
13impl<A, B, E> Functor<A, B> for Result<A, E> {
14    fn fmap<F: FnOnce(A) -> B>(self, f: F) -> <Self as HKT<A, B>>::Target {
15        self.map(f)
16    }
17}
18
19#[cfg(test)]
20mod tests {
21    use std::convert::identity;
22
23    use paste::paste;
24    use quickcheck::TestResult;
25    use quickcheck_macros::quickcheck;
26
27    use super::*;
28
29    // fmap id = id
30    macro_rules! functor_identity {
31        ( $(($a:ident, $b:ty)),* $(,)? ) => {
32            $(
33                paste!{
34                    #[quickcheck]
35                    fn [<functor_identity_law_with_ $a>](value: $b) -> bool {
36                        value.fmap(identity) == identity(value)
37                    }
38                }
39            )*
40        };
41    }
42
43    functor_identity!((option, Option<u32>), (result, Result<u32, u8>),);
44
45    macro_rules! assert_composition {
46        ( $name:ident, $f:expr, $g:expr ) => {
47            let f = $f;
48            let g = $g;
49
50            TestResult::from_bool($name.fmap(|x| g(f(x))) == $name.fmap(f).fmap(g))
51        }
52    }
53
54    // fmap ( f . g ) == fmap f . fmap g
55    #[quickcheck]
56    fn test_functor_composition_law_with_option(value: Option<u32>) -> TestResult {
57        // This prevents any overflowing numbers to be tested
58        if let Some(val) = value {
59            if val >= u32::MAX / 2 - 4 {
60                return TestResult::discard();
61            }
62        }
63
64        assert_composition!{
65            value,
66            |x| x + 2,
67            |x| x * 2
68        }
69    }
70
71    #[quickcheck]
72    fn test_functor_composition_law_with_result(value: Result<u32, u8>) -> TestResult {
73        // This prevents any overflowing numbers to be tested
74        if let Ok(val) = value {
75            if val >= u32::MAX / 2 - 4 {
76                return TestResult::discard();
77            }
78        }
79
80        assert_composition!{
81            value,
82            |x| x + 2,
83            |x| x * 2
84        }
85    }
86}