pub trait Functor {
type Inner;
type Output<B>;
fn map<B>(self, f: impl FnOnce(Self::Inner) -> B) -> Self::Output<B>;
}
#[inline]
pub fn map<F: Functor, B>(fa: F, f: impl FnOnce(F::Inner) -> B) -> F::Output<B> {
fa.map(f)
}
#[inline]
pub fn as_<F: Functor, B: Clone>(fa: F, value: B) -> F::Output<B> {
fa.map(|_| value)
}
#[inline]
pub fn map_to<A, B>(f: impl FnOnce(A) -> B) -> impl FnOnce(Option<A>) -> Option<B> {
move |fa| fa.map(f)
}
pub mod option {
#[inline]
pub fn map<A, B>(fa: Option<A>, f: impl FnOnce(A) -> B) -> Option<B> {
fa.map(f)
}
#[inline]
pub fn as_<A, B>(fa: Option<A>, value: B) -> Option<B> {
fa.map(|_| value)
}
#[inline]
pub fn void<A>(fa: Option<A>) -> Option<()> {
fa.map(|_| ())
}
#[inline]
pub fn tap<A: Clone, B>(fa: Option<A>, f: impl FnOnce(&A) -> B) -> Option<(A, B)> {
fa.map(|a| {
let b = f(&a);
(a, b)
})
}
}
pub mod result {
#[inline]
pub fn map<A, B, E>(fa: Result<A, E>, f: impl FnOnce(A) -> B) -> Result<B, E> {
fa.map(f)
}
#[inline]
pub fn as_<A, B, E>(fa: Result<A, E>, value: B) -> Result<B, E> {
fa.map(|_| value)
}
#[inline]
pub fn void<A, E>(fa: Result<A, E>) -> Result<(), E> {
fa.map(|_| ())
}
}
pub mod vec {
#[inline]
pub fn map<A, B>(fa: Vec<A>, f: impl FnMut(A) -> B) -> Vec<B> {
fa.into_iter().map(f).collect()
}
#[inline]
pub fn as_<A, B: Clone>(fa: Vec<A>, value: B) -> Vec<B> {
fa.into_iter().map(|_| value.clone()).collect()
}
#[inline]
pub fn void<A>(fa: Vec<A>) -> Vec<()> {
fa.into_iter().map(|_| ()).collect()
}
}
impl<A> Functor for Option<A> {
type Inner = A;
type Output<B> = Option<B>;
#[inline]
fn map<B>(self, f: impl FnOnce(A) -> B) -> Option<B> {
self.map(f)
}
}
impl<A, E> Functor for Result<A, E> {
type Inner = A;
type Output<B> = Result<B, E>;
#[inline]
fn map<B>(self, f: impl FnOnce(A) -> B) -> Result<B, E> {
self.map(f)
}
}
impl<A> Functor for Vec<A> {
type Inner = A;
type Output<B> = Vec<B>;
#[inline]
fn map<B>(self, f: impl FnOnce(A) -> B) -> Vec<B> {
let mut f = Some(f);
self
.into_iter()
.map(|a| {
if let Some(func) = f.take() {
func(a)
} else {
panic!("FnOnce called multiple times in Vec::map")
}
})
.collect()
}
}
impl<A, const N: usize> Functor for [A; N] {
type Inner = A;
type Output<B> = [B; N];
#[inline]
fn map<B>(self, f: impl FnOnce(A) -> B) -> [B; N] {
let mut f = Some(f);
self.map(|a| {
if let Some(func) = f.take() {
func(a)
} else {
panic!("FnOnce called multiple times in array::map")
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
mod option_functor {
use super::*;
#[test]
fn map_some_applies_function() {
assert_eq!(Some(3).map(|x| x * 2), Some(6));
}
#[test]
fn map_none_returns_none() {
assert_eq!(None::<i32>.map(|x| x * 2), None);
}
#[test]
fn identity_law() {
let fa = Some(42);
assert_eq!(fa.map(|x| x), fa);
}
#[test]
fn composition_law() {
let fa = Some(5);
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let left = fa.map(|x| f(g(x)));
let right = fa.map(g).map(f);
assert_eq!(left, right);
}
#[rstest]
#[case::positive(Some(5_i32), 10)]
#[case::zero(Some(0_i32), 0)]
#[case::negative(Some(-3_i32), -6)]
fn map_doubles(#[case] input: Option<i32>, #[case] expected: i32) {
assert_eq!(input.map(|x| x * 2), Some(expected));
}
}
mod result_functor {
#[test]
fn map_ok_applies_function() {
assert_eq!(Ok::<i32, &str>(3).map(|x| x * 2), Ok(6));
}
#[test]
fn map_err_returns_err() {
assert_eq!(Err::<i32, &str>("fail").map(|x| x * 2), Err("fail"));
}
#[test]
fn identity_law() {
let fa: Result<i32, &str> = Ok(42);
assert_eq!(fa.clone().map(|x| x), fa);
}
#[test]
fn composition_law() {
let fa: Result<i32, &str> = Ok(5);
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let left = fa.clone().map(|x| f(g(x)));
let right = fa.map(g).map(f);
assert_eq!(left, right);
}
}
mod option_module {
use super::*;
#[test]
fn as_replaces_value() {
assert_eq!(option::as_(Some(5), "replaced"), Some("replaced"));
}
#[test]
fn as_none_returns_none() {
assert_eq!(option::as_(None::<i32>, "replaced"), None);
}
#[test]
fn void_discards_value() {
assert_eq!(option::void(Some(42)), Some(()));
}
#[test]
fn tap_tuples_with_derived() {
let result = option::tap(Some(5), |x| x * 2);
assert_eq!(result, Some((5, 10)));
}
}
mod result_module {
use super::*;
#[test]
fn as_replaces_ok_value() {
assert_eq!(result::as_(Ok::<i32, &str>(5), "replaced"), Ok("replaced"));
}
#[test]
fn void_discards_ok_value() {
assert_eq!(result::void(Ok::<i32, &str>(42)), Ok(()));
}
#[test]
fn void_preserves_err() {
assert_eq!(result::void(Err::<i32, &str>("e")), Err("e"));
}
}
mod vec_module {
use super::*;
#[test]
fn map_transforms_elements() {
assert_eq!(vec::map(vec![1, 2, 3], |x| x * 2), vec![2, 4, 6]);
}
#[test]
fn map_empty_vec() {
assert_eq!(
vec::map(Vec::<i32>::new(), |x: i32| x * 2),
Vec::<i32>::new()
);
}
#[test]
fn as_replaces_all() {
assert_eq!(vec::as_(vec![1, 2, 3], "x"), vec!["x", "x", "x"]);
}
#[test]
fn void_discards_all() {
assert_eq!(vec::void(vec![1, 2, 3]), vec![(), (), ()]);
}
}
mod free_functions {
use super::*;
#[test]
fn map_works_on_option() {
assert_eq!(map(Some(3), |x| x + 1), Some(4));
}
#[test]
fn as_works_on_option() {
assert_eq!(as_(Some(3), "const"), Some("const"));
}
}
mod laws {
#[test]
fn option_identity_law_exhaustive() {
for val in [None, Some(0), Some(42), Some(-7)] {
assert_eq!(val.map(|x| x), val, "identity failed for {val:?}");
}
}
#[test]
fn result_identity_law_exhaustive() {
let cases: Vec<Result<i32, &str>> = vec![Ok(0), Ok(42), Err("e")];
for val in cases {
assert_eq!(val.clone().map(|x| x), val, "identity failed for {val:?}");
}
}
#[test]
fn option_composition_law_exhaustive() {
let f = |x: i32| x + 10;
let g = |x: i32| x * 3;
for val in [None, Some(0), Some(5), Some(-2)] {
let left = val.map(|x| f(g(x)));
let right = val.map(g).map(f);
assert_eq!(left, right, "composition failed for {val:?}");
}
}
}
mod property_laws {
use proptest::prelude::*;
proptest! {
#[test]
fn option_functor_identity(x: Option<i8>) {
prop_assert_eq!(x.map(|v| v), x);
}
#[test]
fn option_functor_composition(x: Option<i8>, a in 0i8..10, b in 0i8..10) {
let f = move |v: i8| v.saturating_add(a);
let g = move |v: i8| v.saturating_mul(b);
let composed = x.map(move |v| f(g(v)));
let sequential = x.map(g).map(f);
prop_assert_eq!(composed, sequential);
}
#[test]
fn result_functor_identity(ok: bool, n: i32) {
let x: Result<i32, &str> = if ok { Ok(n) } else { Err("e") };
prop_assert_eq!(x.map(|v| v), x);
}
#[test]
fn result_functor_composition(n: i8, a in 0i8..10, b in 0i8..10) {
let x: Result<i8, &str> = Ok(n);
let f = move |v: i8| v.saturating_add(a);
let g = move |v: i8| v.saturating_mul(b);
prop_assert_eq!(x.map(move |v| f(g(v))), x.map(g).map(f));
}
}
}
mod map_to_fn {
use super::*;
#[test]
fn map_to_applies_to_some() {
let f = map_to(|x: i32| x * 3);
assert_eq!(f(Some(4)), Some(12));
}
#[test]
fn map_to_applies_to_none() {
let f = map_to(|x: i32| x + 1);
assert_eq!(f(None), None);
}
}
mod array_functor {
#[test]
fn array_map_single_element() {
let arr: [i32; 1] = [5];
let result = arr.map(|x| x * 2);
assert_eq!(result, [10]);
}
#[test]
fn array_zero_size_map() {
let arr: [i32; 0] = [];
let result: [String; 0] = arr.map(|x| x.to_string());
assert_eq!(result, [] as [String; 0]);
}
}
mod vec_functor_trait {
use super::*;
#[test]
fn vec_map_single_element_via_trait() {
let v: Vec<i32> = vec![7];
let result = Functor::map(v, |x| x + 10);
assert_eq!(result, vec![17]);
}
#[test]
fn vec_empty_map_via_trait() {
let v: Vec<i32> = vec![];
let result = Functor::map(v, |x: i32| x * 2);
assert_eq!(result, Vec::<i32>::new());
}
}
mod free_functions_result {
use super::*;
#[test]
fn free_map_ok_result() {
let r: Result<i32, &str> = Ok(10);
assert_eq!(map(r, |x| x + 1), Ok(11));
}
#[test]
fn free_map_err_result() {
let r: Result<i32, &str> = Err("fail");
let mapped = map(r, |x: i32| x + 1);
assert_eq!(mapped, Err("fail"));
}
#[test]
fn free_as_replaces_ok_value() {
let r: Result<i32, &str> = Ok(42);
let result = as_(r, "replaced");
assert_eq!(result, Ok("replaced"));
}
}
mod result_module_fns {
use super::*;
#[test]
fn result_map_ok() {
assert_eq!(result::map(Ok::<i32, &str>(5), |x| x * 2), Ok(10));
}
#[test]
fn result_map_err_passes_through() {
assert_eq!(result::map(Err::<i32, &str>("e"), |x| x * 2), Err("e"));
}
#[test]
fn result_as_ok() {
assert_eq!(result::as_(Ok::<i32, &str>(5), "c"), Ok("c"));
}
#[test]
fn result_void_ok() {
assert_eq!(result::void(Ok::<i32, &str>(5)), Ok(()));
}
#[test]
fn result_void_err() {
assert_eq!(result::void(Err::<i32, &str>("e")), Err("e"));
}
}
mod vec_module_fns {
use super::*;
#[test]
fn vec_map_transforms_each() {
assert_eq!(vec::map(vec![1_i32, 2, 3], |x| x * 2), vec![2, 4, 6]);
}
#[test]
fn vec_as_replaces_all() {
assert_eq!(vec::as_(vec![1_i32, 2, 3], 0_i32), vec![0, 0, 0]);
}
#[test]
fn vec_void_discards_values() {
assert_eq!(vec::void(vec![1_i32, 2, 3]), vec![(), (), ()]);
}
}
mod option_module_fns {
use super::*;
#[test]
fn option_map_some() {
assert_eq!(option::map(Some(3_i32), |x| x * 2), Some(6));
}
#[test]
fn option_map_none() {
assert_eq!(option::map(None::<i32>, |x| x * 2), None);
}
#[test]
fn option_as_some() {
assert_eq!(option::as_(Some(3_i32), "replaced"), Some("replaced"));
}
#[test]
fn option_as_none() {
assert_eq!(option::as_(None::<i32>, "replaced"), None);
}
#[test]
fn option_void_some() {
assert_eq!(option::void(Some(42_i32)), Some(()));
}
#[test]
fn option_void_none() {
assert_eq!(option::void(None::<i32>), None);
}
#[test]
fn option_tap_some() {
let result = option::tap(Some(5_i32), |x| x * 3);
assert_eq!(result, Some((5, 15)));
}
#[test]
fn option_tap_none() {
let result: Option<(i32, i32)> = option::tap(None::<i32>, |x| x * 3);
assert_eq!(result, None);
}
}
}