Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Foldable and Traversable instanced for most common classes #101

Merged
merged 29 commits into from
May 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
03e8c67
Add Composed types
May 28, 2017
2eb5336
Add Traverse instance for Ior
May 28, 2017
3462421
Add global lookup for Foldable and Traverse
May 28, 2017
b222231
Add Traverse instance for Try
May 28, 2017
c0d68c2
Add Traverse instance for OptionT
May 28, 2017
7b36c0a
Add Traverse instance for Validated
May 28, 2017
37c48c2
Add missing Traverse instance for EitherT
May 28, 2017
5e0e42b
Add Traverse instance for Coproduct
May 29, 2017
644097b
Add Traverse instance for EitherT
May 29, 2017
392c45d
Add Traverse instance for Id
May 29, 2017
eba936f
Fix an implicit lookup miss
May 29, 2017
1c5e10f
Add EitherT tests for traverse and fold
May 29, 2017
513b9ef
Cleanup unnecessary parameters
May 29, 2017
cfd82fc
Remove redundant parameters
May 29, 2017
25c6806
Merge branch 'master' into paco-folderganger
pakoito May 29, 2017
552be60
Fix detekt
May 29, 2017
85a63b5
Merge remote-tracking branch 'origin/paco-folderganger' into paco-fol…
May 29, 2017
a4471f8
Remove unnecessary parameter
May 29, 2017
9f7c7bb
Remove behaviour leak from EitherT and OptionT
May 29, 2017
271606b
Add reified constructors to composed types
May 29, 2017
15ebe54
Rename ComposeType API
May 29, 2017
3e1d9f7
Refactor Compose API to become an interface tag + two extension funct…
May 29, 2017
465cf49
Refactor .ev() to be atop of files
May 29, 2017
89267c7
Fix remaining ev()
May 29, 2017
ae0ef30
Fix extension functions
May 29, 2017
c4f790b
Add suppression for safe unchecked cast
May 29, 2017
5c7b546
Add format consistency pass for breaking to next line on methods
May 29, 2017
b5ea191
Add missing format
May 29, 2017
4be57d3
Add missing inference
May 29, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions katz/src/main/kotlin/katz/data/Cokleisli.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ typealias CokleisiFun<F, A, B> = (HK<F, A>) -> B

typealias CoreaderT<F, A, B> = Cokleisli<F, A, B>

fun <F, A, B> CokleisiTKind<F, A, B>.ev(): Cokleisli<F, A, B> =
this as Cokleisli<F, A, B>

data class Cokleisli<F, A, B>(val MM: Comonad<F>, val run: CokleisiFun<F, A, B>) : CokleisiTKind<F, A, B> {
class F private constructor()

Expand Down Expand Up @@ -46,5 +49,3 @@ data class Cokleisli<F, A, B>(val MM: Comonad<F>, val run: CokleisiFun<F, A, B>)
Cokleisli(MF, { MF.extract(it) })
}
}

fun <F, A, B> CokleisiTKind<F, A, B>.ev(): Cokleisli<F, A, B> = this as Cokleisli<F, A, B>
23 changes: 18 additions & 5 deletions katz/src/main/kotlin/katz/data/Coproduct.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ typealias CoproductF<F> = HK<Coproduct.F, F>
typealias CoproductFG<F, G> = HK2<Coproduct.F, F, G>
typealias CoproductKind<F, G, A> = HK3<Coproduct.F, F, G, A>

fun <F, G, A> CoproductKind<F, G, A>.ev(): Coproduct<F, G, A> = this as Coproduct<F, G, A>
fun <F, G, A> CoproductKind<F, G, A>.ev(): Coproduct<F, G, A> =
this as Coproduct<F, G, A>

data class Coproduct<F, G, A>(val CF: Comonad<F>, val CG: Comonad<G>, val run: Either<HK<F, A>, HK<G, A>>) : CoproductKind<F, G, A> {

class F private constructor()

fun <B> map(f: (A) -> B): Coproduct<F, G, B> {
return Coproduct(CF, CG, run.bimap(CF.lift(f), CG.lift(f)))
}
fun <B> map(f: (A) -> B): Coproduct<F, G, B> =
Coproduct(CF, CG, run.bimap(CF.lift(f), CG.lift(f)))

fun <B> coflatMap(f: (Coproduct<F, G, A>) -> B): Coproduct<F, G, B> =
Coproduct(CF, CG, run.bimap(
Expand All @@ -26,8 +26,21 @@ data class Coproduct<F, G, A>(val CF: Comonad<F>, val CG: Comonad<G>, val run: E
fun <H> fold(f: FunctionK<F, H>, g: FunctionK<G, H>): HK<H, A> =
run.fold({ f(it) }, { g(it) })

fun <B> foldL(b: B, f: (B, A) -> B, FF: Foldable<F>, FG: Foldable<G>): B =
run.fold({ FF.foldL(it, b, f) }, { FG.foldL(it, b, f) })

fun <B> foldR(lb: Eval<B>, f: (A, Eval<B>) -> Eval<B>, FF: Foldable<F>, FG: Foldable<G>): Eval<B> =
run.fold({ FF.foldR(it, lb, f) }, { FG.foldR(it, lb, f) })

fun <H, B> traverse(f: (A) -> HK<H, B>, GA: Applicative<H>, FT: Traverse<F>, GT: Traverse<G>): HK<H, Coproduct<F, G, B>> =
run.fold({
GA.map(FT.traverse(it, f, GA), { Coproduct(CF, CG, Either.Left(it)) })
}, {
GA.map(GT.traverse(it, f, GA), { Coproduct(CF, CG, Either.Right(it)) })
})

companion object {
inline operator fun <reified F, reified G, A> invoke(run: Either<HK<F, A>, HK<G, A>>, CF: Comonad<F> = comonad<F>(), CG: Comonad<G> = comonad<G>()) =
inline operator fun <reified F, reified G, A> invoke(run: Either<HK<F, A>, HK<G, A>>, CF: Comonad<F> = comonad<F>(), CG: Comonad<G> = comonad<G>()): Coproduct<F, G, A> =
Coproduct(CF, CG, run)
}

Expand Down
15 changes: 10 additions & 5 deletions katz/src/main/kotlin/katz/data/Coreader.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package katz

fun <A, B> ((A) -> B).coreader(): CoreaderT<Id.F, A, B> = Coreader(this)
fun <A, B> ((A) -> B).coreader(): CoreaderT<Id.F, A, B> =
Coreader(this)

fun <A, B> CoreaderT<Id.F, A, B>.runId(d: A): B = this.run(Id(d))
fun <A, B> CoreaderT<Id.F, A, B>.runId(d: A): B =
this.run(Id(d))

object Coreader {
operator fun <A, B> invoke(run: (A) -> B): CoreaderT<Id.F, A, B> = Cokleisli({ a: IdKind<A> -> run(a.ev().value) })
operator fun <A, B> invoke(run: (A) -> B): CoreaderT<Id.F, A, B> =
Cokleisli({ a: IdKind<A> -> run(a.ev().value) })

fun <A, B> pure(x: B): CoreaderT<Id.F, A, B> = Cokleisli.pure<Id.F, A, B>(x)
fun <A, B> pure(x: B): CoreaderT<Id.F, A, B> =
Cokleisli.pure<Id.F, A, B>(x)

fun <B> ask(): CoreaderT<Id.F, B, B> = Cokleisli.ask<Id.F, B>()
fun <B> ask(): CoreaderT<Id.F, B, B> =
Cokleisli.ask<Id.F, B>()
}
3 changes: 3 additions & 0 deletions katz/src/main/kotlin/katz/data/Either.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package katz
typealias EitherKind<A, B> = HK2<Either.F, A, B>
typealias EitherF<L> = HK<Either.F, L>

fun <A, B> EitherKind<A, B>.ev(): Either<A, B> =
this as Either<A, B>

/**
* Port of https://github.com/scala/scala/blob/v2.12.1/src/library/scala/util/Either.scala
*
Expand Down
28 changes: 23 additions & 5 deletions katz/src/main/kotlin/katz/data/EitherT.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package katz
typealias EitherTKind<F, A, B> = HK3<EitherT.F, F, A, B>
typealias EitherTF<F, L> = HK2<EitherT.F, F, L>

fun <F, A, B> EitherTKind<F, A, B>.ev(): EitherT<F, A, B> =
this as EitherT<F, A, B>

/**
* [EitherT]`<F, A, B>` is a light wrapper on an `F<`[Either]`<A, B>>` with some
* convenient methods for working with this nested structure.
Expand All @@ -15,15 +18,20 @@ data class EitherT<F, A, B>(val MF: Monad<F>, val value: HK<F, Either<A, B>>) :

companion object {

inline operator fun <reified F, A, B> invoke(value: HK<F, Either<A, B>>, MF: Monad<F> = monad<F>()): EitherT<F, A, B> = EitherT(MF, value)
inline operator fun <reified F, A, B> invoke(value: HK<F, Either<A, B>>, MF: Monad<F> = monad<F>()): EitherT<F, A, B> =
EitherT(MF, value)

@JvmStatic inline fun <reified F, A, B> pure(b: B, MF: Monad<F> = monad<F>()): EitherT<F, A, B> = right(b, MF)
@JvmStatic inline fun <reified F, A, B> pure(b: B, MF: Monad<F> = monad<F>()): EitherT<F, A, B> =
right(b, MF)

@JvmStatic inline fun <reified F, A, B> right(b: B, MF: Monad<F> = monad<F>()): EitherT<F, A, B> = EitherT(MF, MF.pure(Either.Right(b)))
@JvmStatic inline fun <reified F, A, B> right(b: B, MF: Monad<F> = monad<F>()): EitherT<F, A, B> =
EitherT(MF, MF.pure(Either.Right(b)))

@JvmStatic inline fun <reified F, A, B> left(a: A, MF: Monad<F> = monad<F>()): EitherT<F, A, B> = EitherT(MF, MF.pure(Either.Left(a)))
@JvmStatic inline fun <reified F, A, B> left(a: A, MF: Monad<F> = monad<F>()): EitherT<F, A, B> =
EitherT(MF, MF.pure(Either.Left(a)))

@JvmStatic inline fun <reified F, A, B> fromEither(value: Either<A, B>, MF: Monad<F> = monad<F>()): EitherT<F, A, B> = EitherT(MF, MF.pure(value))
@JvmStatic inline fun <reified F, A, B> fromEither(value: Either<A, B>, MF: Monad<F> = monad<F>()): EitherT<F, A, B> =
EitherT(MF, MF.pure(value))
}

inline fun <C> fold(crossinline l: (A) -> C, crossinline r: (B) -> C): HK<F, C> =
Expand Down Expand Up @@ -59,4 +67,14 @@ data class EitherT<F, A, B>(val MF: Monad<F>, val value: HK<F, Either<A, B>>) :
fun toOptionT(): OptionT<F, B> =
OptionT(MF, MF.map(value, { it.toOption() }))

fun <C> foldL(b: C, f: (C, B) -> C, FF: Foldable<F>): C =
FF.compose(EitherTraverse<A>()).foldLC(value, b, f)

fun <C> foldR(lb: Eval<C>, f: (B, Eval<C>) -> Eval<C>, FF: Foldable<F>): Eval<C> =
FF.compose(EitherTraverse<A>()).foldRC(value, lb, f)

fun <G, C> traverse(f: (B) -> HK<G, C>, GA: Applicative<G>, FF: Traverse<F>, MF: Monad<F>): HK<G, HK<EitherTF<F, A>, C>> {
val fa = ComposedTraverse(FF, EitherTraverse<A>(), EitherMonad<A>()).traverseC(value, f, GA)
return GA.map(fa, { EitherT(MF, MF.map(it.lower(), { it.ev() })) })
}
}
5 changes: 3 additions & 2 deletions katz/src/main/kotlin/katz/data/Eval.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package katz

fun <A> HK<Eval.F, A>.ev(): Eval<A> =
this as Eval<A>

/**
* Eval is a monad which controls evaluation of a value or a computation that produces a value.
*
Expand Down Expand Up @@ -205,5 +208,3 @@ sealed class Eval<out A> : HK<Eval.F, A> {
@JvmStatic val One: Eval<Int> = Now(1)
}
}

fun <A> HK<Eval.F, A>.ev(): Eval<A> = this as Eval<A>
15 changes: 9 additions & 6 deletions katz/src/main/kotlin/katz/data/Id.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ package katz

typealias IdKind<A> = HK<Id.F, A>

fun <A> IdKind<A>.ev(): Id<A> = this as Id<A>

fun <A> IdKind<A>.value(): A =
this.ev().value

data class Id<out A>(val value: A) : IdKind<A> {

class F private constructor()

inline fun <B> map(f: (A) -> B): Id<B> = Id(f(value))
inline fun <B> map(f: (A) -> B): Id<B> =
Id(f(value))

inline fun <B> flatMap(f: (A) -> Id<B>): Id<B> = f(value)
inline fun <B> flatMap(f: (A) -> Id<B>): Id<B> =
f(value)

companion object : IdBimonad, GlobalInstance<Bimonad<Id.F>>()

}

fun <A> IdKind<A>.ev(): Id<A> = this as Id<A>

fun <A> IdKind<A>.value(): A = this.ev().value
6 changes: 4 additions & 2 deletions katz/src/main/kotlin/katz/data/Ior.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package katz

import katz.Either.Left
import katz.Either.Right
import katz.Option

typealias IorKind<A, B> = HK2<Ior.F, A, B>

fun <A, B> IorKind<A, B>.ev(): Ior<A, B> =
this as Ior<A, B>

/**
* Port of https://github.com/typelevel/cats/blob/v0.9.0/core/src/main/scala/cats/data/Ior.scala
*
Expand Down Expand Up @@ -268,4 +270,4 @@ inline fun <A, B, D> Ior<A, B>.flatMap(SA: Semigroup<A>, crossinline f: (B) -> I
}
}

inline fun <A, B> Ior<A, B>.getOrElse(crossinline default: () -> B): B = fold({ default() }, { it }, { _, b -> b })
inline fun <A, B> Ior<A, B>.getOrElse(crossinline default: () -> B): B = fold({ default() }, { it }, { _, b -> b })
20 changes: 13 additions & 7 deletions katz/src/main/kotlin/katz/data/Kleisli.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ typealias KleisiFun<F, D, A> = (D) -> HK<F, A>

typealias ReaderT<F, D, A> = Kleisli<F, D, A>

fun <F, D, A> KleisiTKind<F, D, A>.ev(): Kleisli<F, D, A> =
this as Kleisli<F, D, A>

class Kleisli<F, D, A>(val MF: Monad<F>, val run: KleisiFun<F, D, A>) : KleisiTKind<F, D, A> {
class F private constructor()

fun <B> map(f: (A) -> B): Kleisli<F, D, B> = Kleisli(MF, { a -> MF.map(run(a), f) })
fun <B> map(f: (A) -> B): Kleisli<F, D, B> =
Kleisli(MF, { a -> MF.map(run(a), f) })

fun <B> flatMap(f: (A) -> Kleisli<F, D, B>): Kleisli<F, D, B> =
Kleisli(MF, { d ->
Expand All @@ -22,7 +26,8 @@ class Kleisli<F, D, A>(val MF: Monad<F>, val run: KleisiFun<F, D, A>) : KleisiTK
o.map({ b -> Tuple2(a, b) })
})

fun <DD> local(f: (DD) -> D): Kleisli<F, DD, A> = Kleisli(MF, { dd -> run(f(dd)) })
fun <DD> local(f: (DD) -> D): Kleisli<F, DD, A> =
Kleisli(MF, { dd -> run(f(dd)) })

fun <B> andThen(f: (A) -> HK<F, B>): Kleisli<F, D, B> =
Kleisli(MF, { MF.flatMap(run(it), f) })
Expand All @@ -32,16 +37,17 @@ class Kleisli<F, D, A>(val MF: Monad<F>, val run: KleisiFun<F, D, A>) : KleisiTK

companion object {

inline operator fun <reified F, D, A> invoke(noinline run: KleisiFun<F, D, A>, MF: Monad<F> = monad<F>()): Kleisli<F, D, A> = Kleisli(MF, run)
inline operator fun <reified F, D, A> invoke(noinline run: KleisiFun<F, D, A>, MF: Monad<F> = monad<F>()): Kleisli<F, D, A> =
Kleisli(MF, run)

@JvmStatic inline fun <reified F, D, A> pure(x: A, MF: Monad<F> = monad<F>()): Kleisli<F, D, A> = Kleisli(MF, { _ -> MF.pure(x) })
@JvmStatic inline fun <reified F, D, A> pure(x: A, MF: Monad<F> = monad<F>()): Kleisli<F, D, A> =
Kleisli(MF, { _ -> MF.pure(x) })

@JvmStatic inline fun <reified F, D> ask(MF: Monad<F> = monad<F>()): Kleisli<F, D, D> = Kleisli(MF, { MF.pure(it) })
@JvmStatic inline fun <reified F, D> ask(MF: Monad<F> = monad<F>()): Kleisli<F, D, D> =
Kleisli(MF, { MF.pure(it) })
}

}

inline fun <reified F, D, A> Kleisli<F, D, Kleisli<F, D, A>>.flatten(): Kleisli<F, D, A> =
flatMap({ it })

fun <F, D, A> KleisiTKind<F, D, A>.ev(): Kleisli<F, D, A> = this as Kleisli<F, D, A>
17 changes: 11 additions & 6 deletions katz/src/main/kotlin/katz/data/NonEmptyList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package katz

typealias NonEmptyListKind<A> = HK<NonEmptyList.F, A>

fun <A> NonEmptyListKind<A>.ev(): NonEmptyList<A> =
this as NonEmptyList<A>

/**
* A List that can not be empty
*/
Expand All @@ -17,9 +20,8 @@ class NonEmptyList<out A> private constructor(

val size: Int = all.size

fun contains(element: @UnsafeVariance A): Boolean {
return (head == element).or(tail.contains(element))
}
fun contains(element: @UnsafeVariance A): Boolean =
(head == element).or(tail.contains(element))

fun containsAll(elements: Collection<@UnsafeVariance A>): Boolean =
elements.all { contains(it) }
Expand All @@ -32,11 +34,14 @@ class NonEmptyList<out A> private constructor(
fun <B> flatMap(f: (A) -> NonEmptyList<B>): NonEmptyList<B> =
f(head) + tail.flatMap { f(it).all }

operator fun plus(l: NonEmptyList<@UnsafeVariance A>): NonEmptyList<A> = NonEmptyList(all + l.all)
operator fun plus(l: NonEmptyList<@UnsafeVariance A>): NonEmptyList<A> =
NonEmptyList(all + l.all)

operator fun plus(l: List<@UnsafeVariance A>): NonEmptyList<A> = NonEmptyList(all + l)
operator fun plus(l: List<@UnsafeVariance A>): NonEmptyList<A> =
NonEmptyList(all + l)

operator fun plus(a: @UnsafeVariance A): NonEmptyList<A> = NonEmptyList(all + a)
operator fun plus(a: @UnsafeVariance A): NonEmptyList<A> =
NonEmptyList(all + a)

fun iterator(): Iterator<A> = all.iterator()

Expand Down
10 changes: 8 additions & 2 deletions katz/src/main/kotlin/katz/data/Option.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package katz

typealias OptionKind<A> = HK<Option.F, A>

fun <A> OptionKind<A>.ev(): Option<A> =
this as Option<A>

/**
* Port of https://github.com/scala/scala/blob/v2.12.1/src/library/scala/Option.scala
*
Expand All @@ -13,8 +16,11 @@ sealed class Option<out A> : OptionKind<A> {
class F private constructor()

companion object : OptionMonad, GlobalInstance<Monad<Option.F>>() {
@JvmStatic fun <A : Any> fromNullable(a: A?): Option<A> = if (a != null) Option.Some(a) else Option.None
operator fun <A> invoke(a: A): Option<A> = Option.Some(a)
@JvmStatic fun <A : Any> fromNullable(a: A?): Option<A> =
if (a != null) Option.Some(a) else Option.None

operator fun <A> invoke(a: A): Option<A> =
Option.Some(a)
}

/**
Expand Down
Loading