1use crate::extend::Extend;
2use crate::hkt::{EnvF, IdentityF, OptionF};
3#[cfg(any(feature = "std", feature = "alloc"))]
4use crate::hkt::{NonEmptyVec, NonEmptyVecF};
5
6pub trait Comonad: Extend {
16 fn extract<A: Clone>(wa: &Self::Of<A>) -> A;
17}
18
19impl Comonad for IdentityF {
20 fn extract<A: Clone>(wa: &A) -> A {
21 wa.clone()
22 }
23}
24
25impl Comonad for OptionF {
26 fn extract<A: Clone>(wa: &Option<A>) -> A {
27 wa.as_ref()
28 .expect("Comonad::extract called on None")
29 .clone()
30 }
31}
32
33#[cfg(any(feature = "std", feature = "alloc"))]
34impl Comonad for NonEmptyVecF {
35 fn extract<A: Clone>(wa: &NonEmptyVec<A>) -> A {
36 wa.head.clone()
37 }
38}
39
40impl<E> Comonad for EnvF<E> {
41 fn extract<A: Clone>(wa: &(E, A)) -> A {
42 wa.1.clone()
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn identity_extract() {
52 assert_eq!(IdentityF::extract(&42), 42);
53 }
54
55 #[test]
56 fn option_extract() {
57 assert_eq!(OptionF::extract(&Some(42)), 42);
58 }
59
60 #[test]
61 #[should_panic(expected = "Comonad::extract called on None")]
62 fn option_extract_none_panics() {
63 OptionF::extract(&None::<i32>);
64 }
65
66 #[test]
67 fn nonemptyvec_extract() {
68 let nev = NonEmptyVec::new(1, vec![2, 3]);
69 assert_eq!(NonEmptyVecF::extract(&nev), 1);
70 }
71
72 #[test]
73 fn env_extract() {
74 assert_eq!(EnvF::<&str>::extract(&("hello", 42)), 42);
75 }
76}
77
78#[cfg(test)]
79mod law_tests {
80 use super::*;
81 use proptest::prelude::*;
82
83 fn nonemptyvec_strategy<T: core::fmt::Debug + Clone + 'static>(
84 elem: impl Strategy<Value = T> + Clone + 'static,
85 ) -> impl Strategy<Value = NonEmptyVec<T>> {
86 (elem.clone(), prop::collection::vec(elem, 0..5))
87 .prop_map(|(head, tail)| NonEmptyVec::new(head, tail))
88 }
89
90 proptest! {
91 #[test]
93 fn identity_left_identity(x in any::<i32>()) {
94 let f = |w: &i32| w.wrapping_add(1);
95 let left = IdentityF::extract(&IdentityF::extend(x, f));
96 let right = f(&x);
97 prop_assert_eq!(left, right);
98 }
99
100 #[test]
102 fn identity_right_identity(x in any::<i32>()) {
103 let result = IdentityF::extend(x, |w| IdentityF::extract(w));
104 prop_assert_eq!(result, x);
105 }
106
107 #[test]
108 fn nonemptyvec_left_identity(w in nonemptyvec_strategy(any::<i16>())) {
109 let f = |nev: &NonEmptyVec<i16>| nev.head.wrapping_add(1);
110 let left = NonEmptyVecF::extract(&NonEmptyVecF::extend(w.clone(), f));
111 let right = f(&w);
112 prop_assert_eq!(left, right);
113 }
114
115 #[test]
116 fn nonemptyvec_right_identity(w in nonemptyvec_strategy(any::<i16>())) {
117 let result = NonEmptyVecF::extend(w.clone(), |w| NonEmptyVecF::extract(w));
118 prop_assert_eq!(result, w);
119 }
120
121 #[test]
122 fn env_left_identity(e in any::<i8>(), a in any::<i16>()) {
123 let w = (e, a);
124 let f = |wa: &(i8, i16)| wa.1.wrapping_add(1);
125 let left = EnvF::<i8>::extract(&EnvF::<i8>::extend(w, f));
126 let right = f(&(e, a));
127 prop_assert_eq!(left, right);
128 }
129
130 #[test]
131 fn env_right_identity(e in any::<i8>(), a in any::<i16>()) {
132 let w = (e, a);
133 let result = EnvF::<i8>::extend(w, |w| EnvF::<i8>::extract(w));
134 prop_assert_eq!(result, (e, a));
135 }
136 }
137}