1use std::collections::{BTreeMap, HashMap};
2use std::hash::{BuildHasher, Hash};
3
4use higher::Bilift;
5
6pub 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}