use super::*;
use super::types::*;
pub trait Bifunctor {
fn bimap<A, B, C, D, F: Fn(A) -> B, G: Fn(C) -> D>(
f: F,
g: G,
x: K2<Self, A, C>,
) -> K2<Self, B, D>;
fn first<A, B, C, F: Fn(A) -> B>(f: F, x: K2<Self, A, C>) -> K2<Self, B, C> {
Self::bimap(f, |i| i, x)
}
fn second<A, B, C, F: Fn(B) -> C>(f: F, x: K2<Self, A, B>) -> K2<Self, A, C> {
Self::bimap(|i| i, f, x)
}
}
impl Bifunctor for PairC {
fn bimap<A, B, C, D, F: Fn(A) -> B, G: Fn(C) -> D>(
f: F,
g: G,
x: K2<Self, A, C>,
) -> K2<Self, B, D> {
let x = x.into_inner();
PairC::new((f(x.0), g(x.1)))
}
}
impl Bifunctor for ResultC {
fn bimap<A, B, C, D, F: Fn(A) -> B, G: Fn(C) -> D>(
f: F,
g: G,
x: K2<Self, A, C>,
) -> K2<Self, B, D> {
Self::new(match x.into_inner() {
Ok(a) => Ok(f(a)),
Err(b) => Err(g(b)),
})
}
}
#[cfg(test)]
mod test {
use super::*;
fn check_law_id<T, U, B: Bifunctor + Kind2<T, U>>(input: K2<B, T, U>)
where
K2Type<B, T, U>: PartialEq + core::fmt::Debug + Clone,
{
let output = B::bimap(|x| x, |y| y, input.clone());
assert_eq!(input, output);
}
fn check_law_compose<T, U, B: Bifunctor, V, W, X, Y, F, G, H, I>(
f: F,
g: G,
h: H,
i: I,
input: K2<B, T, U>,
) where
B: Kind2<T, U> + Kind2<X, Y>,
F: Fn(V) -> X + Clone,
G: Fn(T) -> V + Clone,
H: Fn(W) -> Y + Clone,
I: Fn(U) -> W + Clone,
K2Type<B, T, U>: Clone,
K2Type<B, X, Y>: PartialEq + core::fmt::Debug + Clone,
{
let composed_second = B::bimap(
f.clone(),
h.clone(),
B::bimap(g.clone(), i.clone(), input.clone()),
);
let composed_first = B::bimap(|x| f(g(x)), |y| h(i(y)), input);
assert_eq!(composed_first, composed_second);
}
#[test]
fn pair_bifunctor_law_id() {
check_law_id(PairC::new((42, false)));
}
#[test]
fn pair_bifunctor_law_compose() {
check_law_compose(
|x| x + 1,
|x| x * 2,
|x| x - 1.0,
|x| x * 3.0,
PairC::new((42, 100.0)),
);
}
#[test]
fn result_bifunctor_law_id() {
let just_fine: Result<i32, bool> = Ok(42);
check_law_id(ResultC::new(just_fine));
let not_fine: Result<i32, bool> = Err(true);
check_law_id(ResultC::new(not_fine));
}
#[test]
fn result_bifunctor_law_compose() {
let just_fine: Result<i32, f32> = Ok(42);
check_law_compose(
|x| x + 1,
|x| x * 2,
|x| x - 1.0,
|x| x * 3.0,
ResultC::new(just_fine),
);
let not_fine: Result<i32, f32> = Err(100.0);
check_law_compose(
|x| x + 1,
|x| x * 2,
|x| x - 1.0,
|x| x * 3.0,
ResultC::new(not_fine),
);
}
}