Skip to content

Commit 022f168

Browse files
committed
introduce Bireducible
1 parent dfc1681 commit 022f168

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright (c) 2015 Typelevel
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of
5+
* this software and associated documentation files (the "Software"), to deal in
6+
* the Software without restriction, including without limitation the rights to
7+
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8+
* the Software, and to permit persons to whom the Software is furnished to do so,
9+
* subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16+
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18+
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
package cats
23+
24+
import cats.data.Ior
25+
26+
trait Bireducible[F[_, _]] extends Bifoldable[F] { self =>
27+
28+
/**
29+
* Apply `ma` and `mb` to the "initial elements" of `fab` and combine them with
30+
* every other value using the given functions `mca` and `mcb`.
31+
*/
32+
def bireduceLeftTo[A, B, C](fab: F[A, B])(
33+
ma: A => C,
34+
mb: B => C
35+
)(
36+
mca: (C, A) => C,
37+
mcb: (C, B) => C
38+
): C
39+
40+
def bireduceRightTo[A, B, C](fab: F[A, B])(
41+
ma: A => Eval[C],
42+
mb: B => Eval[C]
43+
)(
44+
mac: (A, Eval[C]) => Eval[C],
45+
mbc: (B, Eval[C]) => Eval[C]
46+
): Eval[C]
47+
48+
def bireduceLeft[A, B](fab: F[A, B])(ma: (A, A) => A, mb: (B, B) => B): A Ior B =
49+
bireduceLeftTo(fab)(Ior.left, Ior.right)((c, a) => c.leftMap(ma(a, _)), (c, b) => c.map(mb(b, _)))
50+
51+
def bireduceRight[A, B](fab: F[A, B])(
52+
ma: (A, Eval[A]) => Eval[A],
53+
mb: (B, Eval[B]) => Eval[B]
54+
): Eval[A Ior B] =
55+
bireduceRightTo(fab)(
56+
{ a => Eval.now(Ior.left(a)) },
57+
{ b => Eval.now(Ior.right(b)) }
58+
)(
59+
{ (a, c) =>
60+
c.flatMap {
61+
_.leftMap { aa =>
62+
ma(a, Eval.now(aa))
63+
}.fold(
64+
aa => aa.map(Ior.left),
65+
bb => Eval.now(Ior.right(bb)),
66+
(aa, bb) => aa.map(Ior.both(_, bb))
67+
)
68+
}
69+
},
70+
{ (b, c) =>
71+
c.flatMap {
72+
_.map { bb =>
73+
mb(b, Eval.now(bb))
74+
}.fold(
75+
aa => Eval.now(Ior.left(aa)),
76+
bb => bb.map(Ior.right),
77+
(aa, bb) => bb.map(Ior.both(aa, _))
78+
)
79+
}
80+
}
81+
)
82+
83+
/**
84+
* Collapse the structure by mapping each element to an element of a type that has a [[cats.Monoid]]
85+
*/
86+
def bireduceMap[A, B, C](fab: F[A, B])(ma: A => C, mb: B => C)(implicit C: Semigroup[C]): C =
87+
bireduceLeftTo(fab)(ma, mb)(
88+
(c: C, a: A) => C.combine(c, ma(a)),
89+
(c: C, b: B) => C.combine(c, mb(b))
90+
)
91+
92+
def bireduce[A, B, C](fab: F[A, B])(implicit A: Semigroup[A], B: Semigroup[B]): A Ior B =
93+
bireduceLeft(fab)(A.combine, B.combine)
94+
95+
def compose[G[_, _]](implicit ev: Bireducible[G]): Bireducible[λ[(α, β) => F[G[α, β], G[α, β]]]] =
96+
new ComposedBireducible[F, G] {
97+
override val F = self
98+
override val G = ev
99+
}
100+
}
101+
102+
object Bireducible {
103+
104+
/**
105+
* Summon an instance of [[Bireducible]].
106+
*/
107+
@inline def apply[F[_, _]](implicit F: Bireducible[F]): Bireducible[F] = F
108+
}
109+
110+
private[cats] trait ComposedBireducible[F[_, _], G[_, _]]
111+
extends Bireducible[λ[(α, β) => F[G[α, β], G[α, β]]]]
112+
with ComposedBifoldable[F, G] {
113+
114+
implicit def F: Bireducible[F]
115+
implicit def G: Bireducible[G]
116+
117+
override def bireduceLeftTo[A, B, C](fgab: F[G[A, B], G[A, B]])(
118+
ma: A => C,
119+
mb: B => C
120+
)(
121+
mca: (C, A) => C,
122+
mcb: (C, B) => C
123+
): C = {
124+
def bireduceG(gab: G[A, B]): C = G.bireduceLeftTo(gab)(ma, mb)(mca, mcb)
125+
def bifoldG(c: C, gab: G[A, B]): C = G.bifoldLeft(gab, c)(mca, mcb)
126+
127+
F.bireduceLeftTo[G[A, B], G[A, B], C](fgab)(bireduceG, bireduceG)(bifoldG, bifoldG)
128+
}
129+
130+
override def bireduceRightTo[A, B, C](fgab: F[G[A, B], G[A, B]])(
131+
ma: A => Eval[C],
132+
mb: B => Eval[C]
133+
)(
134+
mac: (A, Eval[C]) => Eval[C],
135+
mbc: (B, Eval[C]) => Eval[C]
136+
): Eval[C] = {
137+
def bireduceG(gab: G[A, B]): Eval[C] = G.bireduceRightTo(gab)(ma, mb)(mac, mbc)
138+
def bifoldG(gab: G[A, B], c: Eval[C]): Eval[C] = G.bifoldRight(gab, c)(mac, mbc)
139+
140+
F.bireduceRightTo(fgab)(bireduceG, bireduceG)(bifoldG, bifoldG)
141+
}
142+
}

0 commit comments

Comments
 (0)