higher/
bifunctor.rs

1use core::convert::identity;
2
3/// A `Bifunctor` lets you change the types of a generic type with two type
4/// parameters.
5///
6/// A `Bifunctor` works just like a [`Functor`](crate::Functor), but for types
7/// with two type parameters. It will convert a `F<_, _>: Bifunctor` from `F<A,
8/// B>` to `F<C, D>` using two functions, one `Fn(A) -> C` and the other `Fn(B)
9/// -> D`.
10pub trait Bifunctor<'a, A, B> {
11    type Target<T, U>;
12
13    /// Map a `Bifunctor<A, B>` to a `Bifunctor<C, D>` using a function from `A`
14    /// to `C` and a function from `B` to `D`.
15    fn bimap<C, D, L, R>(self, left: L, right: R) -> Self::Target<C, D>
16    where
17        L: Fn(A) -> C + 'a,
18        R: Fn(B) -> D + 'a;
19
20    /// Map only the left hand side of the bifunctor from `A` to `C`.
21    fn lmap<C, L>(self, left: L) -> Self::Target<C, B>
22    where
23        Self: Sized,
24        B: 'a,
25        L: Fn(A) -> C + 'a,
26    {
27        self.bimap(left, identity)
28    }
29
30    /// Map only the right hand side of the bifunctor from `B` to `D`.
31    fn rmap<D, R>(self, right: R) -> Self::Target<A, D>
32    where
33        Self: Sized,
34        A: 'a,
35        R: Fn(B) -> D + 'a,
36    {
37        self.bimap(identity, right)
38    }
39}
40
41impl<A, B> Bifunctor<'_, A, B> for Result<A, B> {
42    type Target<T, U> = Result<T, U>;
43
44    fn bimap<C, D, L, R>(self, left: L, right: R) -> Self::Target<C, D>
45    where
46        L: Fn(A) -> C,
47        R: Fn(B) -> D,
48    {
49        match self {
50            Ok(a) => Ok(left(a)),
51            Err(b) => Err(right(b)),
52        }
53    }
54}
55
56impl<A, B> Bifunctor<'_, A, B> for Vec<(A, B)> {
57    type Target<T, U> = Vec<(T, U)>;
58
59    fn bimap<C, D, L, R>(self, left: L, right: R) -> Self::Target<C, D>
60    where
61        L: Fn(A) -> C,
62        R: Fn(B) -> D,
63    {
64        self.into_iter().map(|(a, b)| (left(a), right(b))).collect()
65    }
66}