extern crate alloc;
use alloc::boxed::Box;
use core::fmt;
pub struct FunctioLinearis<A, B> {
f: Box<dyn FnOnce(A) -> B + Send>,
}
impl<A, B> FunctioLinearis<A, B> {
#[inline]
pub fn new<F>(f: F) -> Self
where
F: FnOnce(A) -> B + Send + 'static,
{
FunctioLinearis { f: Box::new(f) }
}
#[inline]
pub fn apply(self, a: A) -> B {
(self.f)(a)
}
#[inline]
pub fn compose<C>(self, g: FunctioLinearis<B, C>) -> FunctioLinearis<A, C>
where
A: 'static,
B: 'static,
C: 'static,
{
FunctioLinearis::new(move |a: A| g.apply(self.apply(a)))
}
#[inline]
pub fn and_then<C>(self, g: FunctioLinearis<B, C>) -> FunctioLinearis<A, C>
where
A: 'static,
B: 'static,
C: 'static,
{
self.compose(g)
}
#[inline]
pub fn map_output<C, F>(self, f: F) -> FunctioLinearis<A, C>
where
F: FnOnce(B) -> C + Send + 'static,
A: 'static,
B: 'static,
{
FunctioLinearis::new(move |a: A| f(self.apply(a)))
}
#[inline]
pub fn contramap_input<Z, F>(self, f: F) -> FunctioLinearis<Z, B>
where
F: FnOnce(Z) -> A + Send + 'static,
A: 'static,
B: 'static,
{
FunctioLinearis::new(move |z: Z| self.apply(f(z)))
}
}
impl<A: Send + 'static> FunctioLinearis<A, A> {
pub fn identity() -> Self {
FunctioLinearis::new(|a: A| a)
}
}
impl<A: 'static, B: 'static> fmt::Debug for FunctioLinearis<A, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"FunctioLinearis<{} ⊸ {}>",
core::any::type_name::<A>(),
core::any::type_name::<B>()
)
}
}
pub type FunctioSemel<A, B> = FunctioLinearis<A, B>;
#[inline]
pub fn linear_apply<A, B>(f: FunctioLinearis<A, B>, a: A) -> B {
f.apply(a)
}
#[inline]
pub fn linear_compose<A: 'static, B: 'static, C: 'static>(
f: FunctioLinearis<A, B>,
g: FunctioLinearis<B, C>,
) -> FunctioLinearis<A, C> {
f.compose(g)
}
#[inline]
pub fn linear_flip<A: 'static + Send, B: 'static + Send, C: 'static + Send>(
f: FunctioLinearis<A, FunctioLinearis<B, C>>,
) -> FunctioLinearis<B, FunctioLinearis<A, C>> {
FunctioLinearis::new(move |b: B| {
FunctioLinearis::new(move |a: A| {
let inner: FunctioLinearis<B, C> = f.apply(a);
inner.apply(b)
})
})
}
#[inline]
pub fn linear_const<A: 'static, B: Send + 'static>(b: B) -> FunctioLinearis<A, B> {
FunctioLinearis::new(move |_: A| b)
}
#[inline]
pub fn linear_curry<A: 'static + Send, B: 'static + Send, C: 'static + Send>(
f: FunctioLinearis<(A, B), C>,
) -> FunctioLinearis<A, FunctioLinearis<B, C>> {
FunctioLinearis::new(move |a: A| FunctioLinearis::new(move |b: B| f.apply((a, b))))
}
#[inline]
pub fn linear_uncurry<A: 'static + Send, B: 'static + Send, C: 'static + Send>(
f: FunctioLinearis<A, FunctioLinearis<B, C>>,
) -> FunctioLinearis<(A, B), C> {
FunctioLinearis::new(move |(a, b): (A, B)| {
let inner: FunctioLinearis<B, C> = f.apply(a);
inner.apply(b)
})
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
use alloc::string::ToString;
#[test]
fn test_functio_linearis_new_apply() {
let f = FunctioLinearis::new(|x: i32| x * 2);
let result = f.apply(21);
assert_eq!(result, 42);
}
#[test]
fn test_functio_linearis_compose() {
let f = FunctioLinearis::new(|x: i32| x + 1);
let g = FunctioLinearis::new(|x: i32| x * 2);
let composed = f.compose(g);
let result = composed.apply(5);
assert_eq!(result, 12); }
#[test]
fn test_functio_linearis_and_then() {
let f = FunctioLinearis::new(|x: i32| x + 1);
let g = FunctioLinearis::new(|x: i32| x * 2);
let chained = f.and_then(g);
let result = chained.apply(5);
assert_eq!(result, 12);
}
#[test]
fn test_functio_linearis_map_output() {
let f = FunctioLinearis::new(|x: i32| x + 1);
let mapped = f.map_output(|y| y.to_string());
assert_eq!(mapped.apply(41), "42");
}
#[test]
fn test_functio_linearis_contramap_input() {
let f = FunctioLinearis::new(|x: i32| x * 2);
let contramapped = f.contramap_input(|s: &str| {
s.parse::<i32>()
.expect("test input should be a valid integer string")
});
assert_eq!(contramapped.apply("21"), 42);
}
#[test]
fn test_functio_linearis_identity() {
let id: FunctioLinearis<i32, i32> = FunctioLinearis::identity();
assert_eq!(id.apply(42), 42);
}
#[test]
fn test_linear_apply_fn() {
let f = FunctioLinearis::new(|x: i32| x * 2);
let result = linear_apply(f, 21);
assert_eq!(result, 42);
}
#[test]
fn test_linear_compose_fn() {
let f = FunctioLinearis::new(|x: i32| x + 1);
let g = FunctioLinearis::new(|x: i32| x * 2);
let h = linear_compose(f, g);
assert_eq!(h.apply(5), 12);
}
#[test]
fn test_linear_const() {
let f: FunctioLinearis<i32, &str> = linear_const("hello");
assert_eq!(f.apply(42), "hello");
}
#[test]
fn test_linear_curry() {
let f = FunctioLinearis::new(|(a, b): (i32, i32)| a + b);
let curried = linear_curry(f);
let result = curried.apply(10).apply(32);
assert_eq!(result, 42);
}
#[test]
fn test_linear_uncurry() {
let f = FunctioLinearis::new(|a: i32| FunctioLinearis::new(move |b: i32| a + b));
let uncurried = linear_uncurry(f);
let result = uncurried.apply((10, 32));
assert_eq!(result, 42);
}
#[test]
fn test_linear_flip() {
let f =
FunctioLinearis::new(|a: i32| FunctioLinearis::new(move |b: &str| format!("{b}: {a}")));
let flipped = linear_flip(f);
let result = flipped.apply("answer").apply(42);
assert_eq!(result, "answer: 42");
}
#[test]
fn test_debug() {
let f: FunctioLinearis<i32, i32> = FunctioLinearis::new(|x| x);
let debug_str = format!("{f:?}");
assert!(debug_str.contains("FunctioLinearis"));
}
}