1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! The higher-kinded trait of a `Bifunctor`.

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),
        );
    }
}