Skip to main content

rok_utils/fp/
compose.rs

1use std::collections::HashMap;
2use std::hash::Hash;
3use std::ops::Deref;
4
5pub fn pipe<T>(value: T, fns: Vec<fn(T) -> T>) -> T {
6    fns.into_iter().fold(value, |v, f| f(v))
7}
8
9pub fn compose<A, B, C>(f: impl Fn(B) -> C, g: impl Fn(A) -> B) -> impl Fn(A) -> C {
10    move |x| f(g(x))
11}
12
13pub fn tap<T>(value: T, f: impl FnOnce(&T)) -> T {
14    f(&value);
15    value
16}
17
18pub fn apply<T, U>(value: T, f: impl FnOnce(T) -> U) -> U {
19    f(value)
20}
21
22pub fn or_default<T: Default>(opt: Option<T>) -> T {
23    opt.unwrap_or_default()
24}
25
26pub fn retry<T, E>(times: usize, f: impl FnMut() -> Result<T, E>) -> Result<T, E> {
27    let mut f = f;
28    let mut last_err = None;
29    for _ in 0..times {
30        match f() {
31            ok @ Ok(_) => return ok,
32            Err(e) => last_err = Some(e),
33        }
34    }
35    Err(last_err.unwrap())
36}
37
38#[cfg(feature = "random")]
39pub fn shuffle<T: Clone>(arr: &[T]) -> Vec<T> {
40    use rand::seq::SliceRandom;
41    let mut rng = rand::thread_rng();
42    let mut out: Vec<T> = arr.to_vec();
43    out.shuffle(&mut rng);
44    out
45}
46
47#[derive(Debug)]
48pub struct Lazy<T, F = fn() -> T> {
49    cell: once_cell::sync::Lazy<T, F>,
50}
51
52impl<T, F: FnOnce() -> T> Lazy<T, F> {
53    pub fn new(init: F) -> Self {
54        Self {
55            cell: once_cell::sync::Lazy::new(init),
56        }
57    }
58
59    pub fn get(&self) -> &T {
60        Deref::deref(&self.cell)
61    }
62}
63
64unsafe impl<T, F: FnOnce() -> T + Send + Sync> Send for Lazy<T, F> {}
65unsafe impl<T, F: FnOnce() -> T + Send + Sync> Sync for Lazy<T, F> {}
66
67pub fn memoize<K, R>(f: impl Fn(K) -> R) -> impl Fn(K) -> R
68where
69    K: Hash + Eq + Clone,
70    R: Clone,
71{
72    let cache = std::cell::RefCell::new(HashMap::<K, R>::new());
73    move |arg: K| {
74        let arg = arg.clone();
75        if let Some(result) = cache.borrow().get(&arg) {
76            return result.clone();
77        }
78        let result = f(arg.clone());
79        cache.borrow_mut().insert(arg, result.clone());
80        result
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_pipe() {
90        let result = pipe(1, vec![|x| x + 1, |x| x * 2]);
91        assert_eq!(result, 4);
92    }
93
94    #[test]
95    fn test_compose() {
96        let f = compose(|x: i32| x + 1, |x: i32| x * 2);
97        assert_eq!(f(3), 7);
98    }
99
100    #[test]
101    fn test_tap() {
102        let mut seen = false;
103        let _ = tap(42, |v| seen = *v == 42);
104        assert!(seen);
105    }
106
107    #[test]
108    fn test_apply() {
109        let result = apply(5, |x| x * 2);
110        assert_eq!(result, 10);
111    }
112
113    #[test]
114    fn test_or_default() {
115        assert_eq!(or_default(Some(42)), 42);
116        assert_eq!(or_default(None::<i32>), 0);
117    }
118
119    #[test]
120    fn test_retry_success() {
121        let result = retry(3, || Ok::<i32, i32>(42));
122        assert_eq!(result.unwrap(), 42);
123    }
124
125    #[test]
126    fn test_retry_eventual() {
127        let mut attempts = 0;
128        let result = retry(3, &mut || {
129            attempts += 1;
130            if attempts < 3 {
131                Err(1)
132            } else {
133                Ok(42)
134            }
135        });
136        assert_eq!(result.unwrap(), 42);
137        assert_eq!(attempts, 3);
138    }
139
140    #[test]
141    fn test_retry_failure() {
142        let result = retry(2, || Err::<i32, i32>(42));
143        assert!(result.is_err());
144    }
145
146    #[test]
147    fn test_lazy() {
148        let lazy: Lazy<i32, fn() -> i32> = Lazy::new(|| 42);
149        assert_eq!(*lazy.get(), 42);
150        assert_eq!(*lazy.get(), 42);
151    }
152
153    #[test]
154    fn test_memoize() {
155        let count_ptr = std::cell::Cell::new(0);
156        let memoized = memoize(move |x: i32| {
157            count_ptr.set(count_ptr.get() + 1);
158            x * 2
159        });
160        assert_eq!(memoized(5), 10);
161        assert_eq!(memoized(5), 10);
162        let _ = count_ptr;
163    }
164}