rust_fp_categories/
functor.rs

1#[allow(dead_code)]
2use std::rc::Rc;
3
4pub trait Functor {
5    type Elm;
6    type M<B>;
7
8    fn fmap<B, F>(self, f: F) -> Self::M<B>
9    where
10        F: Fn(&Self::Elm) -> B;
11}
12
13macro_rules! functor_numeric_impl {
14    ($($t:ty)*) => ($(
15        impl Functor for $t {
16          type Elm = $t;
17          type M<U> = U;
18
19          fn fmap<B, F>(self, f: F) -> Self::M<B>
20          where
21            F: Fn(&Self::Elm) -> B,
22          {
23            f(&self)
24          }
25        }
26    )*)
27}
28
29functor_numeric_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
30
31impl<A> Functor for Rc<A> {
32    type Elm = A;
33    type M<U> = Rc<U>;
34
35    fn fmap<B, F>(self, f: F) -> Self::M<B>
36    where
37        F: FnOnce(&Self::Elm) -> B,
38    {
39        let v = f(&self);
40        Rc::new(v)
41    }
42}
43
44impl<A> Functor for Box<A> {
45    type Elm = A;
46    type M<U> = Box<U>;
47
48    fn fmap<B, F>(self, f: F) -> Self::M<B>
49    where
50        F: FnOnce(&Self::Elm) -> B,
51    {
52        let v = f(&self);
53        Box::new(v)
54    }
55}
56
57// ---
58
59impl<A> Functor for Option<A> {
60    type Elm = A;
61    type M<B> = Option<B>;
62
63    fn fmap<B, F>(self, f: F) -> Self::M<B>
64    where
65        F: FnOnce(&Self::Elm) -> B,
66    {
67        match self {
68            Some(ref v) => Some(f(v)),
69            None => None,
70        }
71    }
72}
73
74impl<A, E> Functor for Result<A, E> {
75    type Elm = A;
76    type M<B> = Result<B, E>;
77
78    fn fmap<B, F>(self, f: F) -> Self::M<B>
79    where
80        F: FnOnce(&Self::Elm) -> B,
81    {
82        match self {
83            Ok(v) => Ok(f(&v)),
84            Err(e) => Err(e),
85        }
86    }
87}
88
89impl<A> Functor for Vec<A> {
90    type Elm = A;
91    type M<B> = Vec<B>;
92
93    fn fmap<B, F>(self, f: F) -> Self::M<B>
94    where
95        F: Fn(&Self::Elm) -> B,
96    {
97        self.iter().map(f).collect::<Vec<B>>()
98    }
99}
100
101#[cfg(test)]
102mod laws {
103    mod option {
104        use crate::Functor;
105        use std::convert::identity;
106
107        #[quickcheck]
108        fn covariant_identity(n: Option<i32>) -> bool {
109            n.fmap(|x| identity(*x)) == n
110        }
111
112        #[quickcheck]
113        fn covariant_composition(n: Option<i32>) -> bool {
114            let f1: fn(&i32) -> i32 = |x| *x * 2;
115            let f2: fn(&i32) -> i32 = |x| *x + 4;
116            n.fmap(f1).fmap(f2) == n.fmap(|x| f2(&f1(x)))
117        }
118    }
119
120    mod result {
121        use crate::Functor;
122        use std::convert::identity;
123
124        #[quickcheck]
125        fn covariant_identity(n: Result<i32, String>) -> bool {
126            let expected = n.clone();
127            n.fmap(|x| identity(*x)) == expected
128        }
129
130        #[quickcheck]
131        fn covariant_composition(n: Result<i32, String>) -> bool {
132            let expected = n.clone();
133            let f1: fn(&i32) -> i32 = |x| *x * 2;
134            let f2: fn(&i32) -> i32 = |x| *x + 4;
135            n.fmap(f1).fmap(f2) == expected.fmap(|x| f2(&f1(x)))
136        }
137    }
138
139    mod vec {
140        use crate::Functor;
141        use std::convert::identity;
142
143        #[quickcheck]
144        fn covariant_identity(n: Vec<i32>) -> bool {
145            let expected = n.clone();
146            n.fmap(|x| identity(*x)) == expected
147        }
148
149        #[quickcheck]
150        fn covariant_composition(n: Vec<i32>) -> bool {
151            let expected = n.clone();
152            let f1: fn(&i32) -> i32 = |x| *x * 2;
153            let f2: fn(&i32) -> i32 = |x| *x + 4;
154            n.fmap(f1).fmap(f2) == expected.fmap(|x| f2(&f1(x)))
155        }
156    }
157}