Skip to content

Commit d31f841

Browse files
authored
Add foldMapA to Foldable (perhaps by adding lift to Monoid in Applicative)? (#234)
1 parent 1f1a1c8 commit d31f841

File tree

3 files changed

+23
-1
lines changed

3 files changed

+23
-1
lines changed

arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Applicative.kt

+5
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ interface Applicative<F> : Apply<F> {
2727
if (n <= 0) just(MA.empty())
2828
else mapN(this@replicate, replicate(n - 1, MA)) { (a, xs) -> MA.run { a + xs } }
2929
}
30+
31+
fun <F, A> Monoid<A>.lift(ap: Applicative<F>): Monoid<Kind<F, A>> = object : Monoid<Kind<F, A>> {
32+
override fun empty(): Kind<F, A> = ap.just(this@lift.empty())
33+
override fun Kind<F, A>.combine(b: Kind<F, A>): Kind<F, A> = with(ap) { map2(b) { it.a.combine(it.b) } }
34+
}

arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Foldable.kt

+6
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ interface Foldable<F> {
180180
fun <A> Kind<F, A>.size(MN: Monoid<Long>): Long =
181181
foldMap(MN) { 1 }
182182

183+
/**
184+
* Applicative folding on F by mapping A values to G<B>, combining the B values using the given Monoid<B> instance.
185+
*/
186+
fun <G, A, B, AP, MO> Kind<F, A>.foldMapA(ap: AP, mo: MO, f: (A) -> Kind<G, B>): Kind<G, B>
187+
where AP : Applicative<G>, MO : Monoid<B> = foldMap(mo.lift(ap), f)
188+
183189
/**
184190
* Monadic folding on F by mapping A values to G<B>, combining the B values using the given Monoid<B> instance.
185191
*

arrow-libs/core/arrow-core-test/src/main/kotlin/arrow/core/test/laws/FoldableLaws.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ object FoldableLaws {
8888
Law("Foldable Laws: isEmpty returns if there are elements or not") { FF.`isEmpty returns if there are elements or not`(GEN, EQBool) },
8989
Law("Foldable Laws: isNotEmpty consistent with isEmpty") { FF.`isNotEmpty consistent with isEmpty`(GEN, EQBool) },
9090
Law("Foldable Laws: foldMapM folds on F mapping values to G(B) using given Monoid") { FF.`foldMapM folds on F mapping values to G(B) using given Monoid`(GEN, EQForIdInt) },
91-
Law("Foldable Laws: get gets the item at the given index of the Foldable") { FF.`get gets the item at the given index of the Foldable`(GEN, EQOptionInt) }
91+
Law("Foldable Laws: get gets the item at the given index of the Foldable") { FF.`get gets the item at the given index of the Foldable`(GEN, EQOptionInt) },
92+
Law("Foldable Laws: foldMapA folds on F mapping values to G(B) using given Monoid") { FF.`foldMapA folds on F mapping values to G(B) using given Monoid`(GEN, EQForIdInt) }
9293
)
9394
}
9495

@@ -327,4 +328,14 @@ object FoldableLaws {
327328
}.swap().toOption()
328329
fa.get(idx).equalUnderTheLaw(expected, EQ)
329330
}
331+
332+
fun <F> Foldable<F>.`foldMapA folds on F mapping values to G(B) using given Monoid`(G: Gen<Kind<F, Int>>, EQ: Eq<Kind<ForId, Int>>) =
333+
forAll(Gen.functionAToB<Int, Kind<ForId, Int>>(Gen.intSmall().map(::Id)), G) { f: (Int) -> Kind<ForId, Int>, fa: Kind<F, Int> ->
334+
Id.monad().run {
335+
with(Int.monoid()) {
336+
val expected = fa.foldM(this@run, this.empty()) { b, a -> f(a).map { this.run { b.combine(it) } } }
337+
fa.foldMapA(this@run, this, f).equalUnderTheLaw(expected, EQ)
338+
}
339+
}
340+
}
330341
}

0 commit comments

Comments
 (0)