Skip to content

Commit 6b4dd1a

Browse files
committed
introduce Bireducible
1 parent f969051 commit 6b4dd1a

File tree

1 file changed

+175
-0
lines changed

1 file changed

+175
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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+
* Left-reduce `F` by applying `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+
* This method has to be implemented.
33+
*/
34+
def bireduceLeftTo[A, B, C](fab: F[A, B])(
35+
ma: A => C,
36+
mb: B => C
37+
)(
38+
mca: (C, A) => C,
39+
mcb: (C, B) => C
40+
): C
41+
42+
def bireduceRightTo[A, B, C](fab: F[A, B])(
43+
ma: A => Eval[C],
44+
mb: B => Eval[C]
45+
)(
46+
mac: (A, Eval[C]) => Eval[C],
47+
mbc: (B, Eval[C]) => Eval[C]
48+
): Eval[C]
49+
50+
def bireduceLeft[A, B](fab: F[A, B])(ma: (A, A) => A, mb: (B, B) => B): A Ior B =
51+
Bireducible.bireduceLeft(fab)(ma, mb)(self)
52+
53+
def bireduceRight[A, B](fab: F[A, B])(
54+
ma: (A, Eval[A]) => Eval[A],
55+
mb: (B, Eval[B]) => Eval[B]
56+
): Eval[A Ior B] =
57+
Bireducible.bireduceRight(fab)(ma, mb)(self)
58+
59+
/**
60+
* Collapse the structure by mapping each element to an element of a type that has a [[cats.Semigroup]]
61+
*/
62+
def bireduceMap[A, B, C](fab: F[A, B])(ma: A => C, mb: B => C)(implicit C: Semigroup[C]): C =
63+
Bireducible.bireduceMap(fab)(ma, mb)(self, C)
64+
65+
def bireduce[A, B](fab: F[A, B])(implicit A: Semigroup[A], B: Semigroup[B]): A Ior B =
66+
Bireducible.bireduce(fab)(self, A, B)
67+
68+
def compose[G[_, _]](implicit ev: Bireducible[G]): Bireducible[λ[(α, β) => F[G[α, β], G[α, β]]]] =
69+
new ComposedBireducible[F, G] {
70+
override val F = self
71+
override val G = ev
72+
}
73+
}
74+
75+
object Bireducible {
76+
77+
/**
78+
* Summon an instance of [[Bireducible]].
79+
*/
80+
@inline def apply[F[_, _]](implicit F: Bireducible[F]): Bireducible[F] = F
81+
82+
/** Default implementation for [[cats.Bireducible#bireduceLeft]] based on [[cats.Bireducible#bireduceLeftTo]]. */
83+
private[cats] def bireduceLeft[F[_, _], A, B](fab: F[A, B])(
84+
ma: (A, A) => A,
85+
mb: (B, B) => B
86+
)(implicit
87+
F: Bireducible[F]
88+
): A Ior B =
89+
F.bireduceLeftTo(fab)(Ior.left, Ior.right)(
90+
(c, a) => c.addLeft(a)(ma(_, _)),
91+
(c, b) => c.addRight(b)(mb(_, _))
92+
)
93+
94+
/** Default implementation for [[cats.Bireducible#bireduceRight]] based on [[cats.Bireducible#bireduceRightTo]]. */
95+
private[cats] def bireduceRight[F[_, _], A, B](fab: F[A, B])(
96+
ma: (A, Eval[A]) => Eval[A],
97+
mb: (B, Eval[B]) => Eval[B]
98+
)(implicit
99+
F: Bireducible[F]
100+
): Eval[A Ior B] =
101+
F.bireduceRightTo(fab)(
102+
{ a => Eval.now(Ior.left(a)) },
103+
{ b => Eval.now(Ior.right(b)) }
104+
)(
105+
{ (a, ec) =>
106+
ec.flatMap {
107+
case Ior.Left(aa) => ma(a, Eval.now(aa)).map(Ior.left)
108+
case Ior.Right(bb) => Eval.now(Ior.both(a, bb))
109+
case Ior.Both(aa, bb) => ma(a, Eval.now(aa)).map(Ior.both(_, bb))
110+
}
111+
},
112+
{ (b, ec) =>
113+
ec.flatMap {
114+
case Ior.Left(aa) => Eval.now(Ior.Both(aa, b))
115+
case Ior.Right(bb) => mb(b, Eval.now(bb)).map(Ior.right)
116+
case Ior.Both(aa, bb) => mb(b, Eval.now(bb)).map(Ior.both(aa, _))
117+
}
118+
}
119+
)
120+
121+
/** Default implementation for [[cats.Bireducible#bireduceMap]] based on [[cats.Bireducible#bireduceLeftTo]]. */
122+
private[cats] def bireduceMap[F[A, B], A, B, C](fab: F[A, B])(
123+
ma: A => C,
124+
mb: B => C
125+
)(implicit
126+
F: Bireducible[F],
127+
C: Semigroup[C]
128+
): C =
129+
F.bireduceLeftTo(fab)(ma, mb)(
130+
(c: C, a: A) => C.combine(c, ma(a)),
131+
(c: C, b: B) => C.combine(c, mb(b))
132+
)
133+
134+
/** Default implementation for [[cats.Bireducible#bireduce]] based on [[cats.Bireducible#bireduceLeft]]. */
135+
private[cats] def bireduce[F[_, _], A, B](fab: F[A, B])(implicit
136+
F: Bireducible[F],
137+
A: Semigroup[A],
138+
B: Semigroup[B]
139+
): A Ior B =
140+
F.bireduceLeft(fab)(A.combine, B.combine)
141+
}
142+
143+
private[cats] trait ComposedBireducible[F[_, _], G[_, _]]
144+
extends Bireducible[λ[(α, β) => F[G[α, β], G[α, β]]]]
145+
with ComposedBifoldable[F, G] {
146+
147+
implicit def F: Bireducible[F]
148+
implicit def G: Bireducible[G]
149+
150+
override def bireduceLeftTo[A, B, C](fgab: F[G[A, B], G[A, B]])(
151+
ma: A => C,
152+
mb: B => C
153+
)(
154+
mca: (C, A) => C,
155+
mcb: (C, B) => C
156+
): C = {
157+
def bireduceG(gab: G[A, B]): C = G.bireduceLeftTo(gab)(ma, mb)(mca, mcb)
158+
def bifoldG(c: C, gab: G[A, B]): C = G.bifoldLeft(gab, c)(mca, mcb)
159+
160+
F.bireduceLeftTo[G[A, B], G[A, B], C](fgab)(bireduceG, bireduceG)(bifoldG, bifoldG)
161+
}
162+
163+
override def bireduceRightTo[A, B, C](fgab: F[G[A, B], G[A, B]])(
164+
ma: A => Eval[C],
165+
mb: B => Eval[C]
166+
)(
167+
mac: (A, Eval[C]) => Eval[C],
168+
mbc: (B, Eval[C]) => Eval[C]
169+
): Eval[C] = {
170+
def bireduceG(gab: G[A, B]): Eval[C] = G.bireduceRightTo(gab)(ma, mb)(mac, mbc)
171+
def bifoldG(gab: G[A, B], c: Eval[C]): Eval[C] = G.bifoldRight(gab, c)(mac, mbc)
172+
173+
F.bireduceRightTo(fgab)(bireduceG, bireduceG)(bifoldG, bifoldG)
174+
}
175+
}

0 commit comments

Comments
 (0)