Skip to content

Commit 501d6af

Browse files
authored
Merge pull request #129 from kategory/paco-foldabletweaks
Add instance lookup for Foldable and Traverse methods
2 parents d7e0013 + 0ab215c commit 501d6af

File tree

3 files changed

+29
-29
lines changed

3 files changed

+29
-29
lines changed

kategory/src/main/kotlin/kategory/instances/Traverse.kt

+9-9
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ interface Traverse<F> : Functor<F>, Foldable<F>, Typeclass {
1111
*/
1212
fun <G, A, B> traverse(fa: HK<F, A>, f: (A) -> HK<G, B>, GA: Applicative<G>): HK<G, HK<F, B>>
1313

14-
/**
15-
* Thread all the G effects through the F structure to invert the structure from F<G<A>> to G<F<A>>.
16-
*/
17-
fun <G, A> sequence(fga: HK<F, HK<G, A>>, GA: Applicative<G>): HK<G, HK<F, A>> =
18-
traverse(fga, { it }, GA)
19-
20-
fun <G, A, B> flatTraverse(fa: HK<F, A>, f: (A) -> HK<G, HK<F, B>>, GA: Applicative<G>, FM: Monad<F>): HK<G, HK<F, B>> =
21-
GA.map(traverse(fa, f, GA), { FM.flatten(it) })
22-
2314
override fun <A, B> map(fa: HK<F, A>, f: (A) -> B): HK<F, B> =
2415
traverse(fa, { Id(f(it)) }, Id).value()
2516
}
2617

18+
inline fun <reified F, reified G, A, B> Traverse<F>.flatTraverse(fa: HK<F, A>, noinline f: (A) -> HK<G, HK<F, B>>, GA: Applicative<G> = applicative(), FM: Monad<F> = monad()): HK<G, HK<F, B>> =
19+
GA.map(traverse(fa, f, GA), { FM.flatten(it) })
20+
21+
/**
22+
* Thread all the G effects through the F structure to invert the structure from F<G<A>> to G<F<A>>.
23+
*/
24+
inline fun <F, reified G, A> Traverse<F>.sequence(fga: HK<F, HK<G, A>>, GA: Applicative<G> = applicative()): HK<G, HK<F, A>> =
25+
traverse(fga, { it }, GA)
26+
2727
inline fun <reified F> traverse(): Traverse<F> =
2828
instance(InstanceParametrizedType(Traverse::class.java, listOf(F::class.java)))

kategory/src/main/kotlin/kategory/typeclasses/Foldable.kt

+19-19
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,6 @@ interface Foldable<in F> : Typeclass {
5757
fun <A, B> foldMap(mb: Monoid<B>, fa: HK<F, A>, f: (A) -> B): B =
5858
foldL(fa, mb.empty(), { b, a -> mb.combine(b, f(a)) })
5959

60-
/**
61-
* Left associative monadic folding on F.
62-
*
63-
* The default implementation of this is based on foldL, and thus will always fold across the entire structure.
64-
* Certain structures are able to implement this in such a way that folds can be short-circuited (not traverse the
65-
* entirety of the structure), depending on the G result produced at a given step.
66-
*/
67-
fun <G, A, B> foldM(MG: Monad<G>, fa: HK<F, A>, z: B, f: (B, A) -> HK<G, B>): HK<G, B> =
68-
foldL(fa, MG.pure(z), { gb, a -> MG.flatMap(gb) { f(it, a) } })
69-
70-
/**
71-
* Monadic folding on F by mapping A values to G<B>, combining the B values using the given Monoid<B> instance.
72-
*
73-
* Similar to foldM, but using a Monoid<B>.
74-
*/
75-
fun <G, A, B> foldMapM(MG: Monad<G>, bb: Monoid<B>, fa: HK<F, A>, f: (A) -> HK<G, B>) : HK<G, B> =
76-
foldM(MG, fa, bb.empty(), { b, a -> MG.map(f(a)) { bb.combine(b, it) } })
77-
7860
/**
7961
* Traverse F<A> using Applicative<G>.
8062
*
@@ -84,7 +66,7 @@ interface Foldable<in F> : Typeclass {
8466
* not otherwise needed.
8567
*/
8668
fun <G, A, B> traverse_(ag: Applicative<G>, fa: HK<F, A>, f: (A) -> HK<G, B>): HK<G, Unit> =
87-
foldR(fa, always { ag.pure(Unit) }, { a, acc -> ag.map2Eval(f(a), acc) { Unit } }).value()
69+
foldR(fa, always { ag.pure(Unit) }, { a, acc -> ag.map2Eval(f(a), acc) { Unit } }).value()
8870

8971
/**
9072
* Sequence F<G<A>> using Applicative<G>.
@@ -137,5 +119,23 @@ interface Foldable<in F> : Typeclass {
137119
}
138120
}
139121

122+
/**
123+
* Monadic folding on F by mapping A values to G<B>, combining the B values using the given Monoid<B> instance.
124+
*
125+
* Similar to foldM, but using a Monoid<B>.
126+
*/
127+
inline fun <F, reified G, A, reified B> Foldable<F>.foldMapM(fa: HK<F, A>, noinline f: (A) -> HK<G, B>, MG: Monad<G> = monad(), bb: Monoid<B> = monoid()): HK<G, B> =
128+
foldM(fa, bb.empty(), { b, a -> MG.map(f(a)) { bb.combine(b, it) } }, MG)
129+
130+
/**
131+
* Left associative monadic folding on F.
132+
*
133+
* The default implementation of this is based on foldL, and thus will always fold across the entire structure.
134+
* Certain structures are able to implement this in such a way that folds can be short-circuited (not traverse the
135+
* entirety of the structure), depending on the G result produced at a given step.
136+
*/
137+
inline fun <F, reified G, A, B> Foldable<F>.foldM(fa: HK<F, A>, z: B, crossinline f: (B, A) -> HK<G, B>, MG: Monad<G> = monad()): HK<G, B> =
138+
foldL(fa, MG.pure(z), { gb, a -> MG.flatMap(gb) { f(it, a) } })
139+
140140
inline fun <reified F> foldable(): Foldable<F> =
141141
instance(InstanceParametrizedType(Foldable::class.java, listOf(F::class.java)))

kategory/src/test/kotlin/kategory/laws/FoldableLaws.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ object FoldableLaws {
7474
inline fun <reified F> foldMIdIsFoldL(FF: Foldable<F>, crossinline cf: (Int) -> HK<F, Int>, EQ: Eq<Any?>) =
7575
forAll(genFunctionAToB<Int, Int>(genIntSmall()), genConstructor(genIntSmall(), cf), { f: (Int) -> Int, fa: HK<F, Int> ->
7676
val foldL: Int = FF.foldL(fa, IntMonoid.empty(), { acc, a -> IntMonoid.combine(acc, f(a)) })
77-
val foldM: Int = FF.foldM(Id, fa, IntMonoid.empty(), { acc, a -> Id(IntMonoid.combine(acc, f(a))) } ).value()
77+
val foldM: Int = FF.foldM(fa, IntMonoid.empty(), { acc, a -> Id(IntMonoid.combine(acc, f(a))) }, Id).value()
7878
foldM.equalUnderTheLaw(foldL, EQ)
7979
})
8080
}

0 commit comments

Comments
 (0)