Skip to main content

karpal_free/
ran.rs

1use core::marker::PhantomData;
2
3use karpal_core::hkt::HKT;
4
5/// Right Kan Extension — `Ran G H A ≅ ∀R. (A → G R) → H R`.
6///
7/// Unlike most constructions in this crate, `Ran` is a **trait** rather than
8/// a concrete type. This is because the universal quantifier `∀R` requires a
9/// generic method, which cannot be made object-safe in Rust.
10///
11/// # Usage
12///
13/// Implement `Ran` for your own types to describe computations in CPS form:
14///
15/// ```rust,ignore
16/// struct MyRan(i32);
17///
18/// impl Ran<OptionF, OptionF> for MyRan {
19///     type Input = i32;
20///     fn run_ran<R>(&self, k: impl Fn(i32) -> Option<R>) -> Option<R> {
21///         k(self.0)
22///     }
23/// }
24/// ```
25///
26/// # Relationship to Codensity
27///
28/// `Ran<F, F, A>` specialised to `G = H = F` is exactly `Codensity<F, A>`.
29/// The `Codensity` type in this crate provides a concrete, ergonomic
30/// implementation of that specialisation.
31pub trait Ran<G: HKT, H: HKT> {
32    /// The input type (corresponds to `A` in `∀R. (A → G R) → H R`).
33    type Input;
34
35    /// Run the Ran: given a continuation `k: A → G R`, produce `H R`.
36    fn run_ran<R>(&self, k: impl Fn(Self::Input) -> G::Of<R>) -> H::Of<R>;
37}
38
39/// Map a function over a `Ran` implementation, producing a new `Ran`.
40///
41/// `ran_fmap(f: A → B)` transforms `Ran<G, H, Input=A>` into `Ran<G, H, Input=B>`:
42///
43/// ```text
44/// new.run_ran(k: B → G R) = old.run_ran(|a| k(f(a)))
45/// ```
46pub fn ran_fmap<G, H, A, B, T, F>(ran: T, f: F) -> RanMapped<G, H, A, B, T, F>
47where
48    G: HKT,
49    H: HKT,
50    T: Ran<G, H, Input = A>,
51    F: Fn(A) -> B,
52{
53    RanMapped {
54        ran,
55        f,
56        _marker: PhantomData,
57    }
58}
59
60/// A `Ran` with a mapped input, produced by [`ran_fmap`].
61pub struct RanMapped<G: HKT, H: HKT, A, B, T, F> {
62    ran: T,
63    f: F,
64    _marker: PhantomData<(G, H, A, B)>,
65}
66
67impl<G, H, A, B, T, F> Ran<G, H> for RanMapped<G, H, A, B, T, F>
68where
69    G: HKT,
70    H: HKT,
71    T: Ran<G, H, Input = A>,
72    F: Fn(A) -> B,
73{
74    type Input = B;
75
76    fn run_ran<R>(&self, k: impl Fn(B) -> G::Of<R>) -> H::Of<R> {
77        let f_ref = &self.f;
78        self.ran.run_ran(move |a| k(f_ref(a)))
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use karpal_core::hkt::OptionF;
86
87    struct SimpleRan(i32);
88
89    impl Ran<OptionF, OptionF> for SimpleRan {
90        type Input = i32;
91        fn run_ran<R>(&self, k: impl Fn(i32) -> Option<R>) -> Option<R> {
92            k(self.0)
93        }
94    }
95
96    #[test]
97    fn basic_ran() {
98        let ran = SimpleRan(42);
99        let result = ran.run_ran(|x| Some(x * 2));
100        assert_eq!(result, Some(84));
101    }
102
103    #[test]
104    fn ran_with_none() {
105        let ran = SimpleRan(0);
106        let result: Option<i32> = ran.run_ran(|_| None);
107        assert_eq!(result, None);
108    }
109
110    #[test]
111    fn ran_fmap_basic() {
112        let ran = SimpleRan(10);
113        let mapped = ran_fmap(ran, |x| x + 5);
114        let result = mapped.run_ran(|x| Some(x * 2));
115        // mapped.run_ran(k) = ran.run_ran(|a| k(f(a)))
116        // = ran.run_ran(|a| k(a + 5))
117        // = k(10 + 5) = k(15) = Some(15 * 2) = Some(30)
118        assert_eq!(result, Some(30));
119    }
120
121    #[test]
122    fn ran_fmap_identity() {
123        let ran = SimpleRan(7);
124        let mapped = ran_fmap(ran, |x: i32| x);
125        let result = mapped.run_ran(Some);
126        assert_eq!(result, Some(7));
127    }
128
129    #[test]
130    fn ran_fmap_composition() {
131        let f = |x: i32| x + 1;
132        let g = |x: i32| x * 2;
133
134        let left = ran_fmap(SimpleRan(5), move |a| g(f(a)));
135        let right = ran_fmap(ran_fmap(SimpleRan(5), f), g);
136
137        let left_result = left.run_ran(Some);
138        let right_result = right.run_ran(Some);
139        assert_eq!(left_result, right_result);
140    }
141
142    // Demonstrate relationship to Codensity: Ran<F, F, A> ≅ Codensity<F, A>
143    #[test]
144    fn ran_as_codensity() {
145        // Ran<OptionF, OptionF> with Input = i32
146        // is equivalent to Codensity<OptionF, i32>:
147        //   ∀R. (i32 → Option R) → Option R
148        let ran = SimpleRan(42);
149        // "lower" via pure: k = Some
150        let result = ran.run_ran(Some);
151        assert_eq!(result, Some(42));
152    }
153
154    // Test with a different G and H
155    struct VecToOption(Vec<i32>);
156
157    impl Ran<OptionF, OptionF> for VecToOption {
158        type Input = i32;
159        fn run_ran<R>(&self, k: impl Fn(i32) -> Option<R>) -> Option<R> {
160            // Apply k to the first element
161            self.0.first().and_then(|&x| k(x))
162        }
163    }
164
165    #[test]
166    fn ran_different_impl() {
167        let ran = VecToOption(vec![10, 20, 30]);
168        let result = ran.run_ran(|x| Some(x.to_string()));
169        assert_eq!(result, Some("10".to_string()));
170    }
171
172    #[test]
173    fn ran_different_impl_empty() {
174        let ran = VecToOption(vec![]);
175        let result: Option<String> = ran.run_ran(|x| Some(x.to_string()));
176        assert_eq!(result, None);
177    }
178}
179
180#[cfg(test)]
181mod law_tests {
182    use super::*;
183    use karpal_core::hkt::OptionF;
184    use proptest::prelude::*;
185
186    struct PureRan(i32);
187
188    impl Ran<OptionF, OptionF> for PureRan {
189        type Input = i32;
190        fn run_ran<R>(&self, k: impl Fn(i32) -> Option<R>) -> Option<R> {
191            k(self.0)
192        }
193    }
194
195    proptest! {
196        // Functor identity: ran_fmap(id, r).run(k) == r.run(k)
197        #[test]
198        fn functor_identity(x in any::<i32>()) {
199            let original = PureRan(x).run_ran(Some);
200            let mapped = ran_fmap(PureRan(x), |a: i32| a).run_ran(Some);
201            prop_assert_eq!(original, mapped);
202        }
203
204        // Functor composition: ran_fmap(g . f) == ran_fmap(g) . ran_fmap(f)
205        #[test]
206        fn functor_composition(x in any::<i32>()) {
207            let f = |a: i32| a.wrapping_add(1);
208            let g = |a: i32| a.wrapping_mul(2);
209
210            let left = ran_fmap(PureRan(x), move |a| g(f(a))).run_ran(Some);
211            let right = ran_fmap(ran_fmap(PureRan(x), f), g).run_ran(Some);
212            prop_assert_eq!(left, right);
213        }
214    }
215}