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