use core::marker::PhantomData;
use karpal_core::hkt::HKT;
pub trait Ran<G: HKT, H: HKT> {
type Input;
fn run_ran<R>(&self, k: impl Fn(Self::Input) -> G::Of<R>) -> H::Of<R>;
}
pub fn ran_fmap<G, H, A, B, T, F>(ran: T, f: F) -> RanMapped<G, H, A, B, T, F>
where
G: HKT,
H: HKT,
T: Ran<G, H, Input = A>,
F: Fn(A) -> B,
{
RanMapped {
ran,
f,
_marker: PhantomData,
}
}
pub struct RanMapped<G: HKT, H: HKT, A, B, T, F> {
ran: T,
f: F,
_marker: PhantomData<(G, H, A, B)>,
}
impl<G, H, A, B, T, F> Ran<G, H> for RanMapped<G, H, A, B, T, F>
where
G: HKT,
H: HKT,
T: Ran<G, H, Input = A>,
F: Fn(A) -> B,
{
type Input = B;
fn run_ran<R>(&self, k: impl Fn(B) -> G::Of<R>) -> H::Of<R> {
let f_ref = &self.f;
self.ran.run_ran(move |a| k(f_ref(a)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use karpal_core::hkt::OptionF;
struct SimpleRan(i32);
impl Ran<OptionF, OptionF> for SimpleRan {
type Input = i32;
fn run_ran<R>(&self, k: impl Fn(i32) -> Option<R>) -> Option<R> {
k(self.0)
}
}
#[test]
fn basic_ran() {
let ran = SimpleRan(42);
let result = ran.run_ran(|x| Some(x * 2));
assert_eq!(result, Some(84));
}
#[test]
fn ran_with_none() {
let ran = SimpleRan(0);
let result: Option<i32> = ran.run_ran(|_| None);
assert_eq!(result, None);
}
#[test]
fn ran_fmap_basic() {
let ran = SimpleRan(10);
let mapped = ran_fmap(ran, |x| x + 5);
let result = mapped.run_ran(|x| Some(x * 2));
assert_eq!(result, Some(30));
}
#[test]
fn ran_fmap_identity() {
let ran = SimpleRan(7);
let mapped = ran_fmap(ran, |x: i32| x);
let result = mapped.run_ran(Some);
assert_eq!(result, Some(7));
}
#[test]
fn ran_fmap_composition() {
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let left = ran_fmap(SimpleRan(5), move |a| g(f(a)));
let right = ran_fmap(ran_fmap(SimpleRan(5), f), g);
let left_result = left.run_ran(Some);
let right_result = right.run_ran(Some);
assert_eq!(left_result, right_result);
}
#[test]
fn ran_as_codensity() {
let ran = SimpleRan(42);
let result = ran.run_ran(Some);
assert_eq!(result, Some(42));
}
struct VecToOption(Vec<i32>);
impl Ran<OptionF, OptionF> for VecToOption {
type Input = i32;
fn run_ran<R>(&self, k: impl Fn(i32) -> Option<R>) -> Option<R> {
self.0.first().and_then(|&x| k(x))
}
}
#[test]
fn ran_different_impl() {
let ran = VecToOption(vec![10, 20, 30]);
let result = ran.run_ran(|x| Some(x.to_string()));
assert_eq!(result, Some("10".to_string()));
}
#[test]
fn ran_different_impl_empty() {
let ran = VecToOption(vec![]);
let result: Option<String> = ran.run_ran(|x| Some(x.to_string()));
assert_eq!(result, None);
}
}
#[cfg(test)]
mod law_tests {
use super::*;
use karpal_core::hkt::OptionF;
use proptest::prelude::*;
struct PureRan(i32);
impl Ran<OptionF, OptionF> for PureRan {
type Input = i32;
fn run_ran<R>(&self, k: impl Fn(i32) -> Option<R>) -> Option<R> {
k(self.0)
}
}
proptest! {
#[test]
fn functor_identity(x in any::<i32>()) {
let original = PureRan(x).run_ran(Some);
let mapped = ran_fmap(PureRan(x), |a: i32| a).run_ran(Some);
prop_assert_eq!(original, mapped);
}
#[test]
fn functor_composition(x in any::<i32>()) {
let f = |a: i32| a.wrapping_add(1);
let g = |a: i32| a.wrapping_mul(2);
let left = ran_fmap(PureRan(x), move |a| g(f(a))).run_ran(Some);
let right = ran_fmap(ran_fmap(PureRan(x), f), g).run_ran(Some);
prop_assert_eq!(left, right);
}
}
}