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