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