Skip to content

Commit

Permalink
Add Tuple2 instances
Browse files Browse the repository at this point in the history
  • Loading branch information
aedans committed Feb 4, 2018
1 parent bd06705 commit 834e561
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 8 deletions.
26 changes: 24 additions & 2 deletions arrow-core/src/main/kotlin/arrow/core/TupleN.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
package arrow.core

data class Tuple2<out A, out B>(val a: A, val b: B) {
import arrow.*

@higherkind
data class Tuple2<out A, out B>(val a: A, val b: B) : Tuple2Kind<A, B> {
fun <C> map(f: (B) -> C) = a toT f(b)
fun <C> ap(f: Tuple2Kind<*, (B) -> C>) = map(f.ev().b)
fun <C> flatMap(f: (B) -> Tuple2Kind<@UnsafeVariance A, C>) = f(b).ev()
fun <C> coflatMap(f: (Tuple2Kind<A, B>) -> C): Tuple2<A, C> = a toT f(this)
fun extract() = b
fun <C> foldL(b: C, f: (C, B) -> C) = f(b, this.b)
fun <C> foldR(lb: Eval<C>, f: (B, Eval<C>) -> Eval<C>) = f(b, lb)

fun reverse(): Tuple2<B, A> = Tuple2(b, a)
companion object

companion object {
fun <A> pure(a: A) = null toT a

tailrec fun <A, B> tailRecM(a: A, f: (A) -> Tuple2Kind<*, Either<A, B>>): Tuple2<*, B> {
val b = f(a).ev().b
return when (b) {
is Either.Left -> tailRecM(b.a, f)
is Either.Right -> Tuple2.pure(b.b)
}
}
}
}

data class Tuple3<out A, out B, out C>(val a: A, val b: B, val c: C) {
Expand Down
17 changes: 14 additions & 3 deletions arrow-data/src/test/kotlin/arrow/data/TupleTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ package arrow.data
import arrow.core.*
import io.kotlintest.KTestJUnitRunner
import io.kotlintest.matchers.shouldNotBe
import arrow.test.laws.EqLaws
import org.junit.runner.RunWith
import arrow.test.UnitSpec
import arrow.typeclasses.eq
import arrow.typeclasses.monoid
import arrow.test.laws.*
import arrow.typeclasses.*

@RunWith(KTestJUnitRunner::class)
class TupleTest : UnitSpec() {
init {

"instances can be resolved implicitly" {
functor<Tuple2<Int, Int>>() shouldNotBe null
applicative<Tuple2<Int, Int>>() shouldNotBe null
monad<Tuple2<Int, Int>>() shouldNotBe null
comonad<Tuple2<Int, Int>>() shouldNotBe null
foldable<Tuple2<Int, Int>>() shouldNotBe null
traverse<Tuple2<Int, Int>>() shouldNotBe null
monoid<Tuple2<Int, Int>>() shouldNotBe null

eq<Tuple2<Int, Int>>() shouldNotBe null
Expand All @@ -27,6 +32,12 @@ class TupleTest : UnitSpec() {
eq<Tuple10<Int, Int, Int, Int, Int, Int, Int, Int, Int, Int>>() shouldNotBe null
}

testLaws(FunctorLaws.laws(Tuple2.functor(), { Tuple2.pure(it) }, Eq.any()))
testLaws(ApplicativeLaws.laws(Tuple2.applicative(), Eq.any()))
testLaws(MonadLaws.laws(Tuple2.monad(), Eq.any()))
testLaws(ComonadLaws.laws(Tuple2.comonad(), { Tuple2.pure(it) }, Eq.any()))
testLaws(FoldableLaws.laws(Tuple2.foldable(), { Tuple2.pure(it) }, Eq.any()))
testLaws(TraverseLaws.laws(Tuple2.traverse(), Tuple2.functor(), { Tuple2.pure(it) }, Eq.any()))
testLaws(
EqLaws.laws { Tuple2(it, it) },
EqLaws.laws { Tuple3(it, it, it) },
Expand Down
46 changes: 43 additions & 3 deletions arrow-instances/src/main/kotlin/arrow/instances/tuple.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,49 @@
package arrow.instances

import arrow.typeclasses.Eq
import arrow.typeclasses.Monoid
import arrow.*
import arrow.core.*
import arrow.instance
import arrow.typeclasses.*

@instance(Tuple2::class)
interface Tuple2FunctorInstance : Functor<Tuple2KindPartial<*>> {
override fun <A, B> map(fa: Tuple2Kind<*, A>, f: (A) -> B) = fa.ev().map(f)
}

@instance(Tuple2::class)
interface Tuple2ApplicativeInstance : Applicative<Tuple2KindPartial<*>> {
override fun <A, B> map(fa: Tuple2Kind<*, A>, f: (A) -> B) = fa.ev().map(f)
override fun <A, B> ap(fa: Tuple2Kind<*, A>, ff: Tuple2Kind<*, (A) -> B>) = fa.ev().ap(ff.ev())
override fun <A> pure(a: A) = Tuple2.pure(a)
}

@instance(Tuple2::class)
interface Tuple2MonadInstance : Monad<Tuple2KindPartial<*>> {
override fun <A, B> map(fa: Tuple2Kind<*, A>, f: (A) -> B) = fa.ev().map(f)
override fun <A, B> ap(fa: Tuple2Kind<*, A>, ff: Tuple2Kind<*, (A) -> B>) = fa.ev().ap(ff.ev())
override fun <A> pure(a: A) = Tuple2.pure(a)
override fun <A, B> flatMap(fa: Tuple2Kind<*, A>, f: (A) -> Tuple2Kind<*, B>) = fa.ev().flatMap { f(it).ev() }
override fun <A, B> tailRecM(a: A, f: (A) -> Tuple2Kind<*, Either<A, B>>) = Tuple2.tailRecM(a, f)
}

@instance(Tuple2::class)
interface Tuple2ComonadInstance : Comonad<Tuple2KindPartial<*>> {
override fun <A, B> map(fa: Tuple2Kind<*, A>, f: (A) -> B) = fa.ev().map(f)
override fun <A, B> coflatMap(fa: Tuple2Kind<*, A>, f: (Tuple2Kind<*, A>) -> B): Tuple2<*, B> = fa.ev().coflatMap(f)
override fun <A> extract(fa: Tuple2Kind<*, A>) = fa.ev().extract()
}

@instance(Tuple2::class)
interface Tuple2FoldableInstance : Foldable<Tuple2KindPartial<*>> {
override fun <A, B> foldLeft(fa: Tuple2Kind<*, A>, b: B, f: (B, A) -> B) = fa.ev().foldL(b, f)
override fun <A, B> foldRight(fa: Tuple2Kind<*, A>, lb: Eval<B>, f: (A, Eval<B>) -> Eval<B>) = fa.ev().foldR(lb, f)
}

@instance(Tuple2::class)
interface Tuple2TraverseInstance : Traverse<Tuple2KindPartial<*>> {
override fun <A, B> foldLeft(fa: Tuple2Kind<*, A>, b: B, f: (B, A) -> B) = fa.ev().foldL(b, f)
override fun <A, B> foldRight(fa: Tuple2Kind<*, A>, lb: Eval<B>, f: (A, Eval<B>) -> Eval<B>) = fa.ev().foldR(lb, f)
override fun <G, A, B> traverse(fa: Tuple2Kind<*, A>, f: (A) -> HK<G, B>, GA: Applicative<G>) = fa.ev().run { GA.map(f(b), a::toT) }
}

@instance(Tuple2::class)
interface Tuple2MonoidInstance<A, B> : Monoid<Tuple2<A, B>> {
Expand Down

0 comments on commit 834e561

Please sign in to comment.