use std::fmt::Debug;
#[derive(Debug, Clone, PartialEq)]
pub struct Ap<A> {
pub value: A,
}
impl<A: Clone + Debug> Ap<A> {
pub fn pure(a: A) -> Self {
Self { value: a }
}
pub fn map<B: Clone + Debug>(self, f: impl FnOnce(A) -> B) -> Ap<B> {
Ap {
value: f(self.value),
}
}
pub fn map2<B: Clone + Debug, C: Clone + Debug>(
self,
other: Ap<B>,
f: impl FnOnce(A, B) -> C,
) -> Ap<C> {
Ap {
value: f(self.value, other.value),
}
}
pub fn ap<B: Clone + Debug>(self, f: impl FnOnce(A) -> B) -> Ap<B> {
Ap {
value: f(self.value),
}
}
}
pub fn sequence<A: Clone + Debug>(items: Vec<Ap<A>>) -> Ap<Vec<A>> {
Ap {
value: items.into_iter().map(|a| a.value).collect(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pure_wraps_value() {
let a = Ap::pure(42);
assert_eq!(a.value, 42);
}
#[test]
fn map_applies_function() {
let result = Ap::pure(21).map(|x| x * 2);
assert_eq!(result.value, 42);
}
#[test]
fn map2_combines_independent() {
let x = Ap::pure(3);
let y = Ap::pure(4);
let result = x.map2(y, |a, b| a + b);
assert_eq!(result.value, 7);
}
#[test]
fn sequence_collects_all() {
let items = vec![Ap::pure(1), Ap::pure(2), Ap::pure(3)];
let result = sequence(items);
assert_eq!(result.value, vec![1, 2, 3]);
}
#[test]
fn identity_law() {
let result = Ap::pure(42).ap(|x| x);
assert_eq!(result.value, 42);
}
#[test]
fn homomorphism_law() {
let f = |x: i32| x * 2;
let x = 21;
let left = Ap::pure(x).ap(f);
let right = Ap::pure(f(x));
assert_eq!(left.value, right.value);
}
#[test]
fn ontology_query_combination() {
let taxonomy_result = Ap::pure(vec!["Dog", "Mammal", "Animal"]);
let mereology_result = Ap::pure(vec!["Tail", "Fur"]);
let combined = taxonomy_result.map2(mereology_result, |ancestors, parts| {
(ancestors.len(), parts.len())
});
assert_eq!(combined.value, (3, 2));
}
mod prop {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_map_pure(x in any::<i32>()) {
let result = Ap::pure(x).map(|a| a + 1);
prop_assert_eq!(result.value, x + 1);
}
#[test]
fn prop_map2_commutative(a in any::<i32>(), b in any::<i32>()) {
let r1 = Ap::pure(a).map2(Ap::pure(b), |x, y| x.wrapping_add(y));
let r2 = Ap::pure(b).map2(Ap::pure(a), |x, y| x.wrapping_add(y));
prop_assert_eq!(r1.value, r2.value);
}
#[test]
fn prop_sequence_length(n in 0..20usize) {
let items: Vec<Ap<usize>> = (0..n).map(Ap::pure).collect();
let result = sequence(items);
prop_assert_eq!(result.value.len(), n);
}
}
}
}