Skip to content

Commit

Permalink
Implement OptionT as HK2 (#52)
Browse files Browse the repository at this point in the history
* Implement OptionT as HK2

use tipealias for HKX

* add missing methods
  • Loading branch information
ffgiraldez committed Apr 3, 2017
1 parent a6120aa commit 1ef7a40
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 49 deletions.
31 changes: 11 additions & 20 deletions katz/src/main/kotlin/katz/data/OptionT.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,27 @@
package katz


data class OptionT<F, A>(val value: HK<F, Option<A>>) : HK<OptionT.F, A> {
data class OptionT<F, A>(val value: HK<F, Option<A>>) : HK2<OptionT.F, F, A> {

class F private constructor()

fun <B> fold(F: Functor<F>, default: () -> B, f: (A) -> B): HK<F, B> =
F.map(value, { it.fold(default, f) })
inline fun <B> flatMap(F: Monad<F>, crossinline f: (A) -> OptionT<F, B>): OptionT<F, B> = flatMapF(F, { it -> f(it).value })

fun <B> flatMapF(F: Monad<F>, f: (A) -> HK<F, Option<B>>): OptionT<F, B> =
OptionT(F.flatMap(value, { it.fold({ F.pure<Option<B>>(Option.None) }, { f(it) }) }))
inline fun <B> flatMapF(F: Monad<F>, crossinline f: (A) -> HK<F, Option<B>>): OptionT<F, B> =
OptionT(F.flatMap(value, { option -> option.fold({ F.pure(Option.None) }, { f(it) }) }))

fun <B> flatMap(F: Monad<F>, f: (A) -> OptionT<F, B>): OptionT<F, B> =
flatMapF(F, { f(it).value })
inline fun <B> map(F: Functor<F>, crossinline f: (Option<A>) -> Option<B>): OptionT<F, B> =
OptionT(F.map(value, { f(it) }))

inline fun <B> map(F: Functor<F>, crossinline f: (A) -> B): OptionT<F, B> =
OptionT(F.map(value, { it.map(f) }))
fun getOrElse(F: Functor<F>, default: () -> A): HK<F, A> = F.map(value, { it.getOrElse(default) })

fun getOrElse(F: Functor<F>, default: () -> A): HK<F, A> =
F.map(value, { it.getOrElse(default) })
inline fun filter(F: Functor<F>, crossinline p: (A) -> Boolean): OptionT<F, A> = OptionT(F.map(value, { it.filter(p) }))

fun filter(F: Functor<F>, p: (A) -> Boolean): OptionT<F, A> =
OptionT(F.map(value, { it.filter(p) }))
inline fun forall(F: Functor<F>, crossinline p: (A) -> Boolean): HK<F, Boolean> = F.map(value, { it.forall(p) })

fun forall(F: Functor<F>, p: (A) -> Boolean): HK<F, Boolean> =
F.map(value, { it.forall(p) })
fun isDefined(F: Functor<F>): HK<F, Boolean> = F.map(value, { it.isDefined })

fun isDefined(F: Functor<F>): HK<F, Boolean> =
F.map(value, { it.isDefined })

fun isEmpty(F: Functor<F>): HK<F, Boolean> =
F.map(value, { it.isEmpty })
fun isEmpty(F: Functor<F>): HK<F, Boolean> = F.map(value, { it.isEmpty })

fun orElse(F: Monad<F>, default: () -> OptionT<F, A>): OptionT<F, A> =
orElseF(F, { default().value })
Expand Down
13 changes: 7 additions & 6 deletions katz/src/main/kotlin/katz/instances/EitherMonad.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

package katz

object EitherMonad : Monad<HK<Either.F, *>> {
override fun <A> pure(a: A): HK2<Either.F, *, A> =
Either.Right(a)
class EitherMonad<L> : Monad<HK<Either.F, L>> {
override fun <A> pure(a: A): Either<L, A> = Either.Right(a)

override fun <A, B> flatMap(fa: HK2<Either.F, L, A>, f: (A) -> HK2<Either.F, L, B>): Either<L, B> {
return fa.ev().flatMap { f(it).ev() }
}

override fun <A, B> flatMap(fa: HK2<Either.F, *, A>, f: (A) -> HK2<Either.F, *, B>): HK2<Either.F, *, B> =
fa.ev().flatMap { f(it).ev() }
}

fun <A> HK2<Either.F, *, A>.ev(): Either<*, A> = this as Either<*, A>
fun <A, B> HK2<Either.F, A, B>.ev(): Either<A, B> = this as Either<A, B>
6 changes: 3 additions & 3 deletions katz/src/main/kotlin/katz/instances/IdMonad.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ package katz

object IdMonad : Monad<Id.F> {

override fun <A, B> map(fa: HK<Id.F, A>, f: (A) -> B): HK<Id.F, B> =
override fun <A, B> map(fa: HK<Id.F, A>, f: (A) -> B): Id<B> =
fa.ev().map(f)

override fun <A> pure(a: A): HK<Id.F, A> = Id(a)
override fun <A> pure(a: A): Id<A> = Id(a)

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

Expand Down
11 changes: 6 additions & 5 deletions katz/src/main/kotlin/katz/instances/IorMonad.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

package katz

class IorMonad<A>(val semigroup: Semigroup<A>) : Monad<HK<Ior.F, A>> {
override fun <B> pure(b: B): HK2<Ior.F, A, B> =
Ior.Right(b)
class IorMonad<L>(val AA: Semigroup<L>) : Monad<HK<Ior.F, L>> {
override fun <A, B> flatMap(fa: HK<HK<Ior.F, L>, A>, f: (A) -> HK<HK<Ior.F, L>, B>): HK<HK<Ior.F, L>, B> =
fa.ev().flatMap(AA) { f(it).ev() }

override fun <A> pure(a: A): HK2<Ior.F, L, A> = Ior.Right(a)


override fun <B, C> flatMap(fa: HK<HK<Ior.F, A>, B>, f: (B) -> HK<HK<Ior.F, A>, C>): HK<HK<Ior.F, A>, C> =
fa.ev().flatMap(semigroup) { f(it).ev() }
}

fun <A, B> HK2<Ior.F, A, B>.ev(): Ior<A, B> = this as Ior<A, B>
12 changes: 4 additions & 8 deletions katz/src/main/kotlin/katz/instances/OptionTMonad.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,13 @@
package katz


class OptionTMonad<F>(val M : Monad<F>) : Monad<OptionT.F> {

class OptionTMonad<F>(val M: Monad<F>) : Monad<HK<OptionT.F, F>> {
override fun <A> pure(a: A): OptionT<F, A> = OptionT(M.pure(Option.Some(a)))

override fun <A, B> flatMap(fa: HK<OptionT.F, A>, f: (A) -> HK<OptionT.F, B>): OptionT<F, B> =
fa.ev<F, A>().flatMap(M, { f(it).ev<F, B>() })

override fun <A, B> map(fa: HK<OptionT.F, A>, f: (A) -> B): OptionT<F, B> =
fa.ev<F, A>().map(M, f)
override fun <A, B> flatMap(fa: HK<HK<OptionT.F, F>, A>, f: (A) -> HK<HK<OptionT.F, F>, B>): OptionT<F, B> =
fa.ev().flatMap(M, { f(it).ev() })

}

//unchecked cast though we don't use F
fun <F, A> HK<OptionT.F, A>.ev(): OptionT<F, A> = this as OptionT<F, A>
fun <F, A> HK2<OptionT.F, F, A>.ev(): OptionT<F, A> = this as OptionT<F, A>
8 changes: 4 additions & 4 deletions katz/src/main/kotlin/katz/typeclasses/HigherKinds.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ package katz

interface HK<out F, out A>

interface HK2<out F, out A, out B> : HK<HK<F, A>, B>
typealias HK2<F, A, B> = HK<HK<F, A>, B>

interface HK3<out F, out A, out B, out C> : HK<HK2<F, A, B>, C>
typealias HK3<F, A, B, C> = HK<HK2<F, A, B>, C>

interface HK4<out F, out A, out B, out C, out D> : HK<HK3<F, A, B, C>, D>
typealias HK4<F, A, B, C, D> = HK<HK3<F, A, B, C>, D>

interface HK5<out F, out A, out B, out C, out D, out E> : HK<HK4<F, A, B, C, D>, E>
typealias HK5<F, A, B, C, D, E> = HK<HK4<F, A, B, C, D>, E>
5 changes: 3 additions & 2 deletions katz/src/test/kotlin/katz/data/EitherTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,15 @@ class EitherTest : UnitSpec() {

"Either.monad.flatMap should be consistent with Either#flatMap" {
forAll { a: Int ->
val M = EitherMonad<Int>()
val x = { b: Int -> Either.Right(b * a) }
val option = Either.Right(a)
option.flatMap(x) == EitherMonad.flatMap(option, x)
option.flatMap(x) == M.flatMap(option, x)
}
}

"Either.monad.binding should for comprehend over right either" {
val result = EitherMonad.binding {
val result = EitherMonad<Int>().binding {
val x = !Either.Right(1)
val y = Either.Right(1).bind()
val z = bind { Either.Right(1) }
Expand Down
2 changes: 1 addition & 1 deletion katz/src/test/kotlin/katz/data/IorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class IorTest : UnitSpec() {
forAll { a: Int ->
val x = { b: Int -> Ior.Right(b * a) }
val ior = Ior.Right(a)
ior.flatMap(intIorMonad.semigroup, x) == intIorMonad.flatMap(ior, x)
ior.flatMap(intIorMonad.AA, x) == intIorMonad.flatMap(ior, x)
}
}

Expand Down

0 comments on commit 1ef7a40

Please sign in to comment.