higher_cat/
bifunctor.rs

1use std::collections::{BTreeMap, HashMap};
2use std::hash::{BuildHasher, Hash};
3
4use higher::Bilift;
5
6/// A `Bifunctor` lets you change the types of a generic type with two type
7/// parameters.
8///
9/// A `Bifunctor` works just like a `Functor`, but for types with two type
10/// parameters. It will convert a `F<_, _>: Bifunctor` from `F<A, B>` to
11/// `F<C, D>` using two functions, one `Fn(A) -> C` and the other `Fn(B) -> D`.
12pub trait Bifunctor<A, B, C, D>: Bilift<A, B, C, D> {
13    fn bimap<L, R>(self, left: L, right: R) -> <Self as Bilift<A, B, C, D>>::Target
14    where
15        L: Fn(A) -> C,
16        R: Fn(B) -> D;
17}
18
19pub trait BifunctorLeft<A, B, C>: Bifunctor<A, B, C, B> {
20    fn lmap<F>(self, f: F) -> <Self as Bilift<A, B, C, B>>::Target
21    where
22        F: Fn(A) -> C;
23}
24
25impl<A, B, C> BifunctorLeft<A, B, C> for A
26where
27    A: Bifunctor<A, B, C, B>,
28{
29    fn lmap<F>(self, f: F) -> <Self as Bilift<A, B, C, B>>::Target
30    where
31        F: Fn(A) -> C,
32    {
33        self.bimap(f, |a| a)
34    }
35}
36
37pub trait BifunctorRight<A, B, C>: Bifunctor<A, B, A, C> {
38    fn rmap<F>(self, f: F) -> <Self as Bilift<A, B, A, C>>::Target
39    where
40        F: Fn(B) -> C;
41}
42
43impl<A, B, C> BifunctorRight<A, B, C> for A
44where
45    A: Bifunctor<A, B, A, C>,
46{
47    fn rmap<F>(self, f: F) -> <Self as Bilift<A, B, A, C>>::Target
48    where
49        F: Fn(B) -> C,
50    {
51        self.bimap(|a| a, f)
52    }
53}
54
55impl<A, B, C, D> Bifunctor<A, B, C, D> for Result<A, B> {
56    fn bimap<L, R>(self, left: L, right: R) -> <Self as Bilift<A, B, C, D>>::Target
57    where
58        L: Fn(A) -> C,
59        R: Fn(B) -> D,
60    {
61        match self {
62            Ok(a) => Ok(left(a)),
63            Err(b) => Err(right(b)),
64        }
65    }
66}
67
68impl<A, B, C, D, S> Bifunctor<A, B, C, D> for HashMap<A, B, S>
69where
70    A: Eq + Hash,
71    C: Eq + Hash,
72    S: BuildHasher + Default,
73{
74    fn bimap<L, R>(self, left: L, right: R) -> <Self as Bilift<A, B, C, D>>::Target
75    where
76        L: Fn(A) -> C,
77        R: Fn(B) -> D,
78    {
79        self.into_iter().map(|(k, v)| (left(k), right(v))).collect()
80    }
81}
82
83impl<A, B, C, D> Bifunctor<A, B, C, D> for BTreeMap<A, B>
84where
85    A: Ord,
86    C: Ord,
87{
88    fn bimap<L, R>(self, left: L, right: R) -> <Self as Bilift<A, B, C, D>>::Target
89    where
90        L: Fn(A) -> C,
91        R: Fn(B) -> D,
92    {
93        self.into_iter().map(|(k, v)| (left(k), right(v))).collect()
94    }
95}