rust_overture/
flip.rs

1use std::rc::Rc;
2
3/// Flips a curried function from (A) -> (B) -> C into (B) -> (A) -> C
4pub fn flip<A, B, C>(f: Rc<dyn Fn(A) -> Rc<dyn Fn(B) -> C>>) -> Rc<dyn Fn(B) -> Rc<dyn Fn(A) -> C>>
5where
6    A: Clone + 'static,
7    B: Clone + 'static,
8    C: 'static,
9{
10    Rc::new(move |b: B| {
11        let f = f.clone();
12        Rc::new(move |a: A| f(a.clone())(b.clone()))
13    })
14}
15
16/// Flip zero-argument curried function
17pub fn flip0<A, B>(f: Rc<dyn Fn(A) -> Rc<dyn Fn() -> B>>) -> Rc<dyn Fn() -> Rc<dyn Fn(A) -> B>>
18where
19    A: Clone + 'static,
20    B: 'static,
21{
22    Rc::new(move || {
23        let f = f.clone();
24        Rc::new(move |a: A| f(a.clone())())
25    })
26}
27
28/// Flip two-argument curried function (A -> (B, C) -> D into (B, C) -> A -> D)
29pub fn flip2<A, B, C, D>(
30    f: Rc<dyn Fn(A) -> Rc<dyn Fn((B, C)) -> D>>,
31) -> Rc<dyn Fn((B, C)) -> Rc<dyn Fn(A) -> D>>
32where
33    A: Clone + 'static,
34    B: Clone + 'static,
35    C: Clone + 'static,
36    D: 'static,
37{
38    Rc::new(move |bc: (B, C)| {
39        let f = f.clone();
40        Rc::new(move |a: A| f(a.clone())(bc.clone()))
41    })
42}
43
44/// Flip three-argument curried function (A -> (B, C, D) -> E into (B, C, D) -> A -> E)
45pub fn flip3<A, B, C, D, E>(
46    f: Rc<dyn Fn(A) -> Rc<dyn Fn((B, C, D)) -> E>>,
47) -> Rc<dyn Fn((B, C, D)) -> Rc<dyn Fn(A) -> E>>
48where
49    A: Clone + 'static,
50    B: Clone + 'static,
51    C: Clone + 'static,
52    D: Clone + 'static,
53    E: 'static,
54{
55    Rc::new(move |bcd: (B, C, D)| {
56        let f = f.clone();
57        Rc::new(move |a: A| f(a.clone())(bcd.clone()))
58    })
59}
60
61/// Flip four-argument curried function (A -> (B, C, D, E) -> F into (B, C, D, E) -> A -> F)
62pub fn flip4<A, B, C, D, E, F>(
63    f: Rc<dyn Fn(A) -> Rc<dyn Fn((B, C, D, E)) -> F>>,
64) -> Rc<dyn Fn((B, C, D, E)) -> Rc<dyn Fn(A) -> F>>
65where
66    A: Clone + 'static,
67    B: Clone + 'static,
68    C: Clone + 'static,
69    D: Clone + 'static,
70    E: Clone + 'static,
71    F: 'static,
72{
73    Rc::new(move |bcde: (B, C, D, E)| {
74        let f = f.clone();
75        Rc::new(move |a: A| f(a.clone())(bcde.clone()))
76    })
77}
78
79/// Flip five-argument curried function (A -> (B, C, D, E, F) -> G into (B, C, D, E, F) -> A -> G)
80pub fn flip5<A, B, C, D, E, F, G>(
81    f: Rc<dyn Fn(A) -> Rc<dyn Fn((B, C, D, E, F)) -> G>>,
82) -> Rc<dyn Fn((B, C, D, E, F)) -> Rc<dyn Fn(A) -> G>>
83where
84    A: Clone + 'static,
85    B: Clone + 'static,
86    C: Clone + 'static,
87    D: Clone + 'static,
88    E: Clone + 'static,
89    F: Clone + 'static,
90    G: 'static,
91{
92    Rc::new(move |bcdef: (B, C, D, E, F)| {
93        let f = f.clone();
94        Rc::new(move |a: A| f(a.clone())(bcdef.clone()))
95    })
96}
97
98#[test]
99fn test_tall_flip() {
100    fn base(a: i32) -> Rc<dyn Fn(i32) -> i32> {
101        Rc::new(move |b| a + b)
102    }
103
104    let f = Rc::new(base);
105    let mut flipped = flip(f);
106
107    for i in 0..1000 {
108        flipped = flip(flipped);
109    }
110
111    let inner = flipped(1);
112    assert_eq!(inner(2), 3);
113}
114
115pub fn flip_throw<A, B, C, E>(
116    f: Rc<dyn Fn(A) -> Rc<dyn Fn(B) -> Result<C, E>>>,
117) -> Rc<dyn Fn(B) -> Rc<dyn Fn(A) -> Result<C, E>>>
118where
119    A: Clone + 'static,
120    B: Clone + 'static,
121    C: 'static,
122    E: 'static,
123{
124    Rc::new(move |b: B| {
125        let f = f.clone();
126        Rc::new(move |a: A| f(a.clone())(b.clone()))
127    })
128}
129
130#[macro_export]
131macro_rules! flip_macro {
132    ($f:expr) => {
133        $crate::flip(Rc::new($f))
134    };
135}