use super::Linearis;
#[inline]
pub fn linear_pair<A, B>(a: Linearis<A>, b: Linearis<B>) -> Linearis<(A, B)> {
Linearis::new((a.consume(), b.consume()))
}
#[inline]
pub fn linear_first<A, B>(pair: Linearis<(A, B)>) -> Linearis<A> {
let (a, _) = pair.consume();
Linearis::new(a)
}
#[inline]
pub fn linear_second<A, B>(pair: Linearis<(A, B)>) -> Linearis<B> {
let (_, b) = pair.consume();
Linearis::new(b)
}
#[inline]
pub fn linear_swap<A, B>(pair: Linearis<(A, B)>) -> Linearis<(B, A)> {
let (a, b) = pair.consume();
Linearis::new((b, a))
}
pub struct CurriedLinear<A, B, C, F>
where
F: FnOnce(Linearis<(A, B)>) -> C,
{
f: F,
a: Linearis<A>,
_phantom: core::marker::PhantomData<(B, C)>,
}
impl<A, B, C, F> CurriedLinear<A, B, C, F>
where
F: FnOnce(Linearis<(A, B)>) -> C,
{
#[inline]
pub fn apply(self, b: Linearis<B>) -> C {
let pair = linear_pair(self.a, b);
(self.f)(pair)
}
}
pub fn linear_curry<A, B, C, F>(f: F) -> impl FnOnce(Linearis<A>) -> CurriedLinear<A, B, C, F>
where
F: FnOnce(Linearis<(A, B)>) -> C,
{
move |a: Linearis<A>| CurriedLinear {
f,
a,
_phantom: core::marker::PhantomData,
}
}
pub fn linear_uncurry<A, B, C, F, G>(f: F) -> impl FnOnce(Linearis<(A, B)>) -> C
where
F: FnOnce(Linearis<A>) -> G,
G: FnOnce(Linearis<B>) -> C,
{
move |pair: Linearis<(A, B)>| {
let (a, b) = pair.consume();
f(Linearis::new(a))(Linearis::new(b))
}
}
#[inline]
pub fn consume_both<A, B, C, F>(pair: Linearis<(A, B)>, f: F) -> C
where
F: FnOnce(A, B) -> C,
{
let (a, b) = pair.consume();
f(a, b)
}
#[inline]
pub fn consume_either<A, B, F>(use_left: bool, left: Linearis<A>, right: Linearis<A>, f: F) -> B
where
F: FnOnce(A) -> B,
{
if use_left {
let _ = right.consume();
f(left.consume())
} else {
let _ = left.consume();
f(right.consume())
}
}
#[inline]
pub fn linear_apply<A, B, F>(f: Linearis<F>, a: Linearis<A>) -> Linearis<B>
where
F: FnOnce(A) -> B,
{
Linearis::new(f.consume()(a.consume()))
}
#[inline]
pub fn linear_compose<A, B, C, F, G>(f: F, g: G) -> impl FnOnce(A) -> C
where
F: FnOnce(A) -> B,
G: FnOnce(B) -> C,
{
move |a| g(f(a))
}
#[inline]
pub fn linear_flip<A, B, C, F>(f: F) -> impl FnOnce(B, A) -> C
where
F: FnOnce(A, B) -> C,
{
move |b, a| f(a, b)
}
#[inline]
pub fn linear_const<A, B>(a: Linearis<A>) -> impl FnOnce(Linearis<B>) -> Linearis<A> {
move |b| {
let _ = b.consume();
a
}
}
#[inline]
pub fn linear_seq_first<A, B>(a: Linearis<A>, b: Linearis<B>) -> Linearis<A> {
let _ = b.consume();
a
}
#[inline]
pub fn linear_seq_second<A, B>(a: Linearis<A>, b: Linearis<B>) -> Linearis<B> {
let _ = a.consume();
b
}
#[inline]
pub fn linear_dup<A: Clone>(x: Linearis<A>) -> (Linearis<A>, Linearis<A>) {
let value = x.consume();
(Linearis::new(value.clone()), Linearis::new(value))
}
#[inline]
pub fn linear_discard<A>(x: Linearis<A>) {
let _ = x.consume();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_linear_pair() {
let x = Linearis::new(1);
let y = Linearis::new("hello");
let pair = linear_pair(x, y);
assert_eq!(pair.consume(), (1, "hello"));
}
#[test]
fn test_linear_first() {
let pair = Linearis::new((1, "hello"));
let first = linear_first(pair);
assert_eq!(first.consume(), 1);
}
#[test]
fn test_linear_second() {
let pair = Linearis::new((1, "hello"));
let second = linear_second(pair);
assert_eq!(second.consume(), "hello");
}
#[test]
fn test_linear_swap() {
let pair = Linearis::new((1, "hello"));
let swapped = linear_swap(pair);
assert_eq!(swapped.consume(), ("hello", 1));
}
#[test]
fn test_linear_curry() {
let add_pair = |pair: Linearis<(i32, i32)>| {
let (a, b) = pair.consume();
a + b
};
let curried = linear_curry(add_pair);
let partial = curried(Linearis::new(1));
let result = partial.apply(Linearis::new(2));
assert_eq!(result, 3);
}
#[test]
fn test_linear_uncurry() {
let add = |a: Linearis<i32>| move |b: Linearis<i32>| a.consume() + b.consume();
let uncurried = linear_uncurry(add);
let result = uncurried(Linearis::new((1, 2)));
assert_eq!(result, 3);
}
#[test]
fn test_consume_both() {
let pair = Linearis::new((1, 2));
let sum = consume_both(pair, |a, b| a + b);
assert_eq!(sum, 3);
}
#[test]
fn test_consume_either() {
let left = Linearis::new(1);
let right = Linearis::new(2);
let result = consume_either(true, left, right, |n| n * 10);
assert_eq!(result, 10);
let left2 = Linearis::new(1);
let right2 = Linearis::new(2);
let result2 = consume_either(false, left2, right2, |n| n * 10);
assert_eq!(result2, 20);
}
#[test]
fn test_linear_apply() {
let f = Linearis::new(|x: i32| x * 2);
let x = Linearis::new(5);
let result = linear_apply(f, x);
assert_eq!(result.consume(), 10);
}
#[test]
fn test_linear_compose() {
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let composed = linear_compose(f, g);
assert_eq!(composed(5), 12);
}
#[test]
fn test_linear_flip() {
let sub = |a: i32, b: i32| a - b;
let flipped = linear_flip(sub);
assert_eq!(flipped(1, 5), 4);
}
#[test]
fn test_linear_const() {
let always_42 = linear_const(Linearis::new(42));
let result = always_42(Linearis::new("ignored"));
assert_eq!(result.consume(), 42);
}
#[test]
fn test_linear_seq_first() {
let a = Linearis::new(1);
let b = Linearis::new(2);
let result = linear_seq_first(a, b);
assert_eq!(result.consume(), 1);
}
#[test]
fn test_linear_seq_second() {
let a = Linearis::new(1);
let b = Linearis::new(2);
let result = linear_seq_second(a, b);
assert_eq!(result.consume(), 2);
}
#[test]
fn test_linear_dup() {
let x = Linearis::new(42);
let (a, b) = linear_dup(x);
assert_eq!(a.consume(), 42);
assert_eq!(b.consume(), 42);
}
#[test]
fn test_linear_discard() {
let x = Linearis::new(42);
linear_discard(x);
}
}