1use crate::contravariant::{Contravariant, PredicateF};
2#[cfg(all(not(feature = "std"), feature = "alloc"))]
3use alloc::boxed::Box;
4
5pub trait Decide: Contravariant {
14 fn choose<A: 'static, B: 'static, C: 'static>(
15 f: impl Fn(C) -> Result<A, B> + 'static,
16 fa: Self::Of<A>,
17 fb: Self::Of<B>,
18 ) -> Self::Of<C>;
19}
20
21impl Decide for PredicateF {
22 fn choose<A: 'static, B: 'static, C: 'static>(
23 f: impl Fn(C) -> Result<A, B> + 'static,
24 fa: Box<dyn Fn(A) -> bool>,
25 fb: Box<dyn Fn(B) -> bool>,
26 ) -> Box<dyn Fn(C) -> bool> {
27 Box::new(move |c| match f(c) {
28 Ok(a) => fa(a),
29 Err(b) => fb(b),
30 })
31 }
32}
33
34#[cfg(test)]
35mod tests {
36 use super::*;
37
38 #[test]
39 fn predicate_choose() {
40 let is_positive: Box<dyn Fn(i32) -> bool> = Box::new(|x| x > 0);
41 let is_short: Box<dyn Fn(String) -> bool> = Box::new(|s| s.len() < 5);
42
43 let classifier =
45 PredicateF::choose(|input: Result<i32, String>| input, is_positive, is_short);
46
47 assert!(classifier(Ok(5)));
48 assert!(!classifier(Ok(-1)));
49 assert!(classifier(Err("hi".to_string())));
50 assert!(!classifier(Err("hello world".to_string())));
51 }
52}
53
54#[cfg(test)]
55mod law_tests {
56 use super::*;
57 use proptest::prelude::*;
58
59 proptest! {
60 #[test]
62 fn predicate_associativity(x in any::<i8>(), y in any::<i8>(), z in any::<i8>()) {
63 let pa: Box<dyn Fn(i8) -> bool> = Box::new(|a| a > 0);
64 let pb: Box<dyn Fn(i8) -> bool> = Box::new(|b| b > 0);
65 let pc: Box<dyn Fn(i8) -> bool> = Box::new(|c| c > 0);
66
67 let pa2: Box<dyn Fn(i8) -> bool> = Box::new(|a| a > 0);
68 let pb2: Box<dyn Fn(i8) -> bool> = Box::new(|b| b > 0);
69 let pc2: Box<dyn Fn(i8) -> bool> = Box::new(|c| c > 0);
70
71 let ab = PredicateF::choose(|v: Result<i8, i8>| v, pa, pb);
74 let left = PredicateF::choose(
75 |tag: (u8, i8)| {
76 if tag.0 < 2 {
77 Ok(if tag.0 == 0 { Ok(tag.1) } else { Err(tag.1) })
78 } else {
79 Err(tag.1)
80 }
81 },
82 ab,
83 pc,
84 );
85
86 let bc = PredicateF::choose(|v: Result<i8, i8>| v, pb2, pc2);
87 let right = PredicateF::choose(
88 |tag: (u8, i8)| {
89 if tag.0 == 0 {
90 Ok(tag.1)
91 } else {
92 Err(if tag.0 == 1 { Ok(tag.1) } else { Err(tag.1) })
93 }
94 },
95 pa2,
96 bc,
97 );
98
99 prop_assert_eq!(left((0, x)), right((0, x)));
101 prop_assert_eq!(left((1, y)), right((1, y)));
102 prop_assert_eq!(left((2, z)), right((2, z)));
103 }
104 }
105}