pub type Unit = ();
#[inline]
pub const fn unit() -> Unit {}
#[inline]
pub fn discard<A>(_: A) -> Unit {}
#[inline]
pub fn extend<A>(value: A) -> impl FnOnce(Unit) -> A {
move |()| value
}
#[cfg(test)]
mod tests {
use super::*;
mod unit_value {
use super::*;
#[test]
fn unit_returns_unit_type() {
let u: Unit = unit();
assert_eq!(u, ());
}
#[test]
fn unit_is_zero_sized() {
assert_eq!(std::mem::size_of::<Unit>(), 0);
}
}
mod discard {
use super::*;
use rstest::rstest;
#[rstest]
#[case::integer(42_i32)]
#[case::string("hello")]
#[case::tuple((1, 2, 3))]
fn discard_returns_unit_for_any_value<T>(#[case] value: T) {
let result: Unit = discard(value);
assert_eq!(result, ());
}
#[test]
fn discard_consumes_value() {
let s = String::from("owned");
let _: Unit = discard(s);
}
}
mod extend {
use super::*;
#[test]
fn extend_produces_value_from_unit() {
let f = extend(42_i32);
assert_eq!(f(()), 42);
}
#[test]
fn extend_with_string() {
let f = extend(String::from("hello"));
assert_eq!(f(()), "hello");
}
}
mod laws {
use super::*;
#[test]
fn discard_then_extend_recovers_value() {
let v = 99_i32;
let f = extend(v);
let result = f(discard(42_i32));
assert_eq!(result, v);
}
#[test]
fn unit_is_identity_element_for_product_left() {
let pair: (Unit, i32) = ((), 42);
assert_eq!(pair.1, 42);
}
#[test]
fn unit_is_identity_element_for_product_right() {
let pair: (i32, Unit) = (42, ());
assert_eq!(pair.0, 42);
}
}
}