rust_fp_categories/
functor.rs1#[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
57impl<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}