karpal_core/
contravariant.rs1use crate::hkt::HKT;
2
3pub trait Contravariant: HKT {
9 fn contramap<A: 'static, B>(fa: Self::Of<A>, f: impl Fn(B) -> A + 'static) -> Self::Of<B>;
10}
11
12#[cfg(any(feature = "std", feature = "alloc"))]
14pub struct PredicateF;
15
16#[cfg(any(feature = "std", feature = "alloc"))]
17impl HKT for PredicateF {
18 #[cfg(feature = "std")]
19 type Of<T> = Box<dyn Fn(T) -> bool>;
20
21 #[cfg(all(not(feature = "std"), feature = "alloc"))]
22 type Of<T> = alloc::boxed::Box<dyn Fn(T) -> bool>;
23}
24
25#[cfg(any(feature = "std", feature = "alloc"))]
26impl Contravariant for PredicateF {
27 fn contramap<A: 'static, B>(fa: Self::Of<A>, f: impl Fn(B) -> A + 'static) -> Self::Of<B> {
28 Box::new(move |b| fa(f(b)))
29 }
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 #[test]
37 fn predicate_contramap() {
38 let is_positive: Box<dyn Fn(i32) -> bool> = Box::new(|x| x > 0);
39 let str_len_positive = PredicateF::contramap(is_positive, |s: &str| s.len() as i32);
40 assert!(str_len_positive("hello"));
41 assert!(!str_len_positive(""));
42 }
43}
44
45#[cfg(test)]
46mod law_tests {
47 use super::*;
48 use proptest::prelude::*;
49
50 proptest! {
51 #[test]
53 fn predicate_identity(x in any::<i32>()) {
54 let pred: Box<dyn Fn(i32) -> bool> = Box::new(|a| a > 0);
55 let expected = pred(x);
56 let result = PredicateF::contramap(pred, |a: i32| a);
57 prop_assert_eq!(result(x), expected);
58 }
59
60 #[test]
62 fn predicate_composition(x in any::<i16>()) {
63 let pred: Box<dyn Fn(i32) -> bool> = Box::new(|a| a > 0);
64 let f = |a: i16| a as i32;
65 let g = |a: i16| a.wrapping_add(1);
66
67 let pred1: Box<dyn Fn(i32) -> bool> = Box::new(|a| a > 0);
69 let left = PredicateF::contramap(pred1, move |a: i16| f(g(a)));
70
71 let inner = PredicateF::contramap(pred, f);
73 let right = PredicateF::contramap(inner, g);
74
75 prop_assert_eq!(left(x), right(x));
76 }
77 }
78}