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