partial_functional/
functor.rs1use 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 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 #[quickcheck]
56 fn test_functor_composition_law_with_option(value: Option<u32>) -> TestResult {
57 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 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}