Skip to content

Commit

Permalink
Merge pull request #129 from kategory/paco-foldabletweaks
Browse files Browse the repository at this point in the history
Add instance lookup for Foldable and Traverse methods
  • Loading branch information
pakoito committed Jul 17, 2017
2 parents d7e0013 + 0ab215c commit 501d6af
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 29 deletions.
18 changes: 9 additions & 9 deletions kategory/src/main/kotlin/kategory/instances/Traverse.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ interface Traverse<F> : Functor<F>, Foldable<F>, Typeclass {
*/
fun <G, A, B> traverse(fa: HK<F, A>, f: (A) -> HK<G, B>, GA: Applicative<G>): HK<G, HK<F, B>>

/**
* Thread all the G effects through the F structure to invert the structure from F<G<A>> to G<F<A>>.
*/
fun <G, A> sequence(fga: HK<F, HK<G, A>>, GA: Applicative<G>): HK<G, HK<F, A>> =
traverse(fga, { it }, GA)

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>> =
GA.map(traverse(fa, f, GA), { FM.flatten(it) })

override fun <A, B> map(fa: HK<F, A>, f: (A) -> B): HK<F, B> =
traverse(fa, { Id(f(it)) }, Id).value()
}

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>> =
GA.map(traverse(fa, f, GA), { FM.flatten(it) })

/**
* Thread all the G effects through the F structure to invert the structure from F<G<A>> to G<F<A>>.
*/
inline fun <F, reified G, A> Traverse<F>.sequence(fga: HK<F, HK<G, A>>, GA: Applicative<G> = applicative()): HK<G, HK<F, A>> =
traverse(fga, { it }, GA)

inline fun <reified F> traverse(): Traverse<F> =
instance(InstanceParametrizedType(Traverse::class.java, listOf(F::class.java)))
38 changes: 19 additions & 19 deletions kategory/src/main/kotlin/kategory/typeclasses/Foldable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,6 @@ interface Foldable<in F> : Typeclass {
fun <A, B> foldMap(mb: Monoid<B>, fa: HK<F, A>, f: (A) -> B): B =
foldL(fa, mb.empty(), { b, a -> mb.combine(b, f(a)) })

/**
* Left associative monadic folding on F.
*
* The default implementation of this is based on foldL, and thus will always fold across the entire structure.
* Certain structures are able to implement this in such a way that folds can be short-circuited (not traverse the
* entirety of the structure), depending on the G result produced at a given step.
*/
fun <G, A, B> foldM(MG: Monad<G>, fa: HK<F, A>, z: B, f: (B, A) -> HK<G, B>): HK<G, B> =
foldL(fa, MG.pure(z), { gb, a -> MG.flatMap(gb) { f(it, a) } })

/**
* Monadic folding on F by mapping A values to G<B>, combining the B values using the given Monoid<B> instance.
*
* Similar to foldM, but using a Monoid<B>.
*/
fun <G, A, B> foldMapM(MG: Monad<G>, bb: Monoid<B>, fa: HK<F, A>, f: (A) -> HK<G, B>) : HK<G, B> =
foldM(MG, fa, bb.empty(), { b, a -> MG.map(f(a)) { bb.combine(b, it) } })

/**
* Traverse F<A> using Applicative<G>.
*
Expand All @@ -84,7 +66,7 @@ interface Foldable<in F> : Typeclass {
* not otherwise needed.
*/
fun <G, A, B> traverse_(ag: Applicative<G>, fa: HK<F, A>, f: (A) -> HK<G, B>): HK<G, Unit> =
foldR(fa, always { ag.pure(Unit) }, { a, acc -> ag.map2Eval(f(a), acc) { Unit } }).value()
foldR(fa, always { ag.pure(Unit) }, { a, acc -> ag.map2Eval(f(a), acc) { Unit } }).value()

/**
* Sequence F<G<A>> using Applicative<G>.
Expand Down Expand Up @@ -137,5 +119,23 @@ interface Foldable<in F> : Typeclass {
}
}

/**
* Monadic folding on F by mapping A values to G<B>, combining the B values using the given Monoid<B> instance.
*
* Similar to foldM, but using a Monoid<B>.
*/
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> =
foldM(fa, bb.empty(), { b, a -> MG.map(f(a)) { bb.combine(b, it) } }, MG)

/**
* Left associative monadic folding on F.
*
* The default implementation of this is based on foldL, and thus will always fold across the entire structure.
* Certain structures are able to implement this in such a way that folds can be short-circuited (not traverse the
* entirety of the structure), depending on the G result produced at a given step.
*/
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> =
foldL(fa, MG.pure(z), { gb, a -> MG.flatMap(gb) { f(it, a) } })

inline fun <reified F> foldable(): Foldable<F> =
instance(InstanceParametrizedType(Foldable::class.java, listOf(F::class.java)))
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/laws/FoldableLaws.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ object FoldableLaws {
inline fun <reified F> foldMIdIsFoldL(FF: Foldable<F>, crossinline cf: (Int) -> HK<F, Int>, EQ: Eq<Any?>) =
forAll(genFunctionAToB<Int, Int>(genIntSmall()), genConstructor(genIntSmall(), cf), { f: (Int) -> Int, fa: HK<F, Int> ->
val foldL: Int = FF.foldL(fa, IntMonoid.empty(), { acc, a -> IntMonoid.combine(acc, f(a)) })
val foldM: Int = FF.foldM(Id, fa, IntMonoid.empty(), { acc, a -> Id(IntMonoid.combine(acc, f(a))) } ).value()
val foldM: Int = FF.foldM(fa, IntMonoid.empty(), { acc, a -> Id(IntMonoid.combine(acc, f(a))) }, Id).value()
foldM.equalUnderTheLaw(foldL, EQ)
})
}

0 comments on commit 501d6af

Please sign in to comment.