1use crate::functor::Functor;
2use crate::hkt::{EnvF, IdentityF, OptionF};
3#[cfg(any(feature = "std", feature = "alloc"))]
4use crate::hkt::{NonEmptyVec, NonEmptyVecF};
5
6pub trait Extend: Functor {
15 fn extend<A, B>(wa: Self::Of<A>, f: impl Fn(&Self::Of<A>) -> B) -> Self::Of<B>
16 where
17 A: Clone;
18
19 fn duplicate<A>(wa: Self::Of<A>) -> Self::Of<Self::Of<A>>
20 where
21 A: Clone,
22 Self::Of<A>: Clone,
23 {
24 Self::extend(wa, |w| w.clone())
25 }
26}
27
28impl Extend for IdentityF {
29 fn extend<A, B>(wa: A, f: impl Fn(&A) -> B) -> B
30 where
31 A: Clone,
32 {
33 f(&wa)
34 }
35}
36
37impl Extend for OptionF {
38 fn extend<A, B>(wa: Option<A>, f: impl Fn(&Option<A>) -> B) -> Option<B>
39 where
40 A: Clone,
41 {
42 if wa.is_some() { Some(f(&wa)) } else { None }
43 }
44}
45
46#[cfg(any(feature = "std", feature = "alloc"))]
47impl Extend for NonEmptyVecF {
48 fn extend<A, B>(wa: NonEmptyVec<A>, f: impl Fn(&NonEmptyVec<A>) -> B) -> NonEmptyVec<B>
49 where
50 A: Clone,
51 {
52 let suffixes = wa.tails();
54 let head = f(&suffixes.head);
55 let tail = suffixes.tail.iter().map(&f).collect();
56 NonEmptyVec::new(head, tail)
57 }
58}
59
60impl<E> Extend for EnvF<E> {
61 fn extend<A, B>(wa: (E, A), f: impl Fn(&(E, A)) -> B) -> (E, B)
62 where
63 A: Clone,
64 {
65 let b = f(&wa);
66 (wa.0, b)
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn identity_extend() {
76 let result = IdentityF::extend(42, |w| w + 1);
77 assert_eq!(result, 43);
78 }
79
80 #[test]
81 fn option_extend_some() {
82 let result = OptionF::extend(Some(3), |opt| match opt {
83 Some(x) => x * 2,
84 None => 0,
85 });
86 assert_eq!(result, Some(6));
87 }
88
89 #[test]
90 fn option_extend_none() {
91 let result = OptionF::extend(None::<i32>, |opt| match opt {
92 Some(x) => x * 2,
93 None => 0,
94 });
95 assert_eq!(result, None);
96 }
97
98 #[test]
99 fn nonemptyvec_extend() {
100 let nev = NonEmptyVec::new(1, vec![2, 3]);
101 let result = NonEmptyVecF::extend(nev, |w| w.iter().sum::<i32>());
103 assert_eq!(result, NonEmptyVec::new(6, vec![5, 3]));
106 }
107
108 #[test]
109 fn env_extend() {
110 let result = EnvF::<&str>::extend(("hello", 42), |&(env, val)| format!("{}: {}", env, val));
111 assert_eq!(result, ("hello", "hello: 42".to_string()));
112 }
113
114 #[test]
115 fn identity_duplicate() {
116 let result = IdentityF::duplicate(42);
117 assert_eq!(result, 42);
118 }
119
120 #[test]
121 fn option_duplicate() {
122 let result = OptionF::duplicate(Some(42));
123 assert_eq!(result, Some(Some(42)));
124 }
125
126 #[test]
127 fn nonemptyvec_duplicate() {
128 let nev = NonEmptyVec::new(1, vec![2, 3]);
129 let result = NonEmptyVecF::duplicate(nev);
130 assert_eq!(result.head, NonEmptyVec::new(1, vec![2, 3]));
131 assert_eq!(result.tail.len(), 2);
132 assert_eq!(result.tail[0], NonEmptyVec::new(2, vec![3]));
133 assert_eq!(result.tail[1], NonEmptyVec::new(3, vec![]));
134 }
135}
136
137#[cfg(test)]
138mod law_tests {
139 use super::*;
140 use proptest::prelude::*;
141
142 fn nonemptyvec_strategy<T: core::fmt::Debug + Clone + 'static>(
143 elem: impl Strategy<Value = T> + Clone + 'static,
144 ) -> impl Strategy<Value = NonEmptyVec<T>> {
145 (elem.clone(), prop::collection::vec(elem, 0..5))
146 .prop_map(|(head, tail)| NonEmptyVec::new(head, tail))
147 }
148
149 proptest! {
150 #[test]
152 fn option_associativity(x in any::<Option<i16>>()) {
153 let f = |opt: &Option<i16>| opt.map_or(0i16, |v| v.wrapping_add(1));
154 let g = |opt: &Option<i16>| opt.map_or(0i16, |v| v.wrapping_mul(2));
155
156 let left = OptionF::extend(OptionF::extend(x.clone(), g), f);
157 let right = OptionF::extend(x, |w| f(&OptionF::extend(w.clone(), g)));
158 prop_assert_eq!(left, right);
159 }
160
161 #[test]
162 fn nonemptyvec_associativity(w in nonemptyvec_strategy(any::<i8>())) {
163 let f = |nev: &NonEmptyVec<i8>| nev.head.wrapping_add(1);
164 let g = |nev: &NonEmptyVec<i8>| nev.head.wrapping_mul(2);
165
166 let left = NonEmptyVecF::extend(NonEmptyVecF::extend(w.clone(), g), f);
167 let right = NonEmptyVecF::extend(w, |w| f(&NonEmptyVecF::extend(w.clone(), g)));
168 prop_assert_eq!(left, right);
169 }
170 }
171}