Skip to content
This repository was archived by the owner on Feb 14, 2021. It is now read-only.

Behavior.ap #25

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion core/src/main/scala/scalaz/reactive/Time.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object Time extends TimeInstances0 {

case object PosInf extends Time

def now = IO.sync(T(System.currentTimeMillis()))
def now: IO[Void, T] = IO.sync(T(System.currentTimeMillis()))
}

trait TimeInstances0 {
Expand Down
57 changes: 57 additions & 0 deletions core/src/test/scala/scalaz/reactive/ReactiveSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package scalaz.reactive

import org.scalacheck.Gen
import org.specs2.{ScalaCheck, Specification}
import scalaz._
import scalaz.reactive.domain.TestDomain._
import scalaz.reactive.Time.T
import scalaz.reactive.laws.ApplicativeLaws
import scalaz.zio.IO

class ReactiveSpec
extends Specification
with ScalaCheck
with ReactiveInstances {

// laws.apIdentityLaw fails, without ever reaching Reactive.ap function

def is = "ReactiveInstances".title ^ s2"""
Generate a mix of K and Fun TimeFuns
`Reactive.ap` holds identity law. ${laws.apIdentityLaw}
"""

def aGen: Gen[A] =
Gen.choose(-5L, 6L).map(i => if (i < 5) Right(i) else STOP)

val stopEvent: Event[A] =
Event(IO.sync { (T(0), Reactive(STOP, stopEvent)) })

def genStopEvent: Gen[Event[A]] =
Gen.oneOf(List(stopEvent)) // how to choose from 1?

def faGenEvent: Gen[Event[A]] =
for {
r <- faGenReactive
} yield Event(IO.sync(((T(0), r))))

def faGenReactive: Gen[Reactive[A]] =
for {
head <- aGen
tail <- if (head.isLeft) genStopEvent else faGenEvent
} yield Reactive(head, tail)

def abGen: Gen[A => A] = ???

def fabGenBehaviour: Gen[Reactive[A => A]] = ???

val laws =
new ApplicativeLaws(
Applicative[Reactive],
aGen,
faGenReactive,
abGen,
fabGenBehaviour,
valueForComparisonReactive
)

}
13 changes: 5 additions & 8 deletions core/src/test/scala/scalaz/reactive/TimeFunSpec.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package scalaz.reactive

import org.scalacheck.Gen
import org.specs2.{ ScalaCheck, Specification }
import org.specs2.{ScalaCheck, Specification}
import scalaz._
import scalaz.reactive.Time.T
import scalaz.reactive.TimeFun._
Expand All @@ -23,8 +23,8 @@ class TimeFunSpec extends Specification with ScalaCheck with TimeFunInstances {

def faGen =
for {
v <- aGen
k <- aGen
v <- aGen
k <- aGen
fun <- Gen.oneOf(K(v), Fun(_.value * k * v))
} yield fun

Expand All @@ -38,11 +38,8 @@ class TimeFunSpec extends Specification with ScalaCheck with TimeFunInstances {
k <- aGen
} yield K((l: Long) => l + k)

def eqGen: Gen[TimeFun[Long] => Long] =
for {
l <- Gen.choose[Long](-100, 100)
t = T(l)
} yield (tf: TimeFun[Long]) => tf.apply(t)
def eqGen: TimeFun[Long] => Long =
tf => tf.apply(T(-1)) + tf.apply(T(0)) + tf.apply(T(1)) + tf.apply(T(10))

val laws =
new ApplicativeLaws(Applicative[TimeFun], aGen, faGen, abGen, fabGen, eqGen)
Expand Down
36 changes: 36 additions & 0 deletions core/src/test/scala/scalaz/reactive/domain/TestDomain.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package scalaz.reactive.domain
import scalaz.reactive.Reactive
import scalaz.zio.{IO, RTS}

object TestDomain extends RTS {

type A = Either[Stop, Long] // type to use in specification
case class Stop()

val STOP = Left(Stop())

/*
Compare two Reactives for equality. reduce reactive to list of its elements
*/
def valueForComparisonReactive: Reactive[A] => List[Long] =
r => {
println(s"cmpReactive: reactive = $r")
val toReduce = reduce(List(), r)
println(s"cmpReactive: toReduce = $toReduce")
unsafeRun(toReduce)
}

def reduce(ls: List[Long], r: Reactive[A]): IO[Void, List[Long]] = {
println(s"Reducing $r")
r match {
case Reactive(Left(Stop()), _) => { println(s"Encountered STOP - result is $ls"); IO.now(ls)}
case Reactive(Right(l), tail) => {
println("flatmapping")
tail.value.flatMap {
case (_, rr: Reactive[A]) => { println("inside flatmap"); reduce(ls :+ l, rr) }
}
}
}
}

}
39 changes: 39 additions & 0 deletions core/src/test/scala/scalaz/reactive/domain/TestDomainSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package scalaz.reactive.domain
import org.specs2.specification.core.SpecStructure
import org.specs2.{ScalaCheck, Specification}
import scalaz.reactive.Time.T
import scalaz.reactive.domain.TestDomain.A
import scalaz.reactive.{Event, Reactive}
import scalaz.zio.IO

class TestDomainSpec extends Specification with ScalaCheck {
override def is: SpecStructure = "TestDomain".title ^ s2"""
Reactive values get reduced correctly. ${testReduceReactive}

"""

def testReduceReactive = {
def r: Reactive[A] =
Reactive[A](
Right(1),
Event(
IO.sync(
(
T(0),
Reactive(
Right(2),
Event(
IO.sync(
(T(0), Reactive(TestDomain.STOP, Event(IO.sync((T(0), r)))))
)
)
)
)
)
)
)

TestDomain.valueForComparisonReactive(r) must beEqualTo(List(1, 2))

}
}
52 changes: 29 additions & 23 deletions core/src/test/scala/scalaz/reactive/laws/ApplicativeLaws.scala
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
package scalaz.reactive.laws
import org.scalacheck.{ Gen, Prop }
import org.scalacheck.{Gen, Prop}
import org.scalacheck.Prop.forAll
import org.specs2.matcher.{ MatchResult, MustMatchers }
import org.specs2.matcher.{MatchResult, MustMatchers}
import scalaz.Applicative

class ApplicativeLaws[F[_], A](
applicative: Applicative[F],
aGen: Gen[A],
faGen: Gen[F[A]],
abGen: Gen[A => A],
fabGen: Gen[F[A => A]],
valueForEqGen: Gen[F[A] => A]
)(implicit
pa: MatchResult[A] => Prop,
pfa: MatchResult[F[A]] => Prop)
extends MustMatchers {
/**
* To run property based testson the Applictive laws on F[_]
*
* @param applicative
*
*/
class ApplicativeLaws[F[_], A, B](applicative: => Applicative[F],
aGen: => Gen[A],
faGen: => Gen[F[A]],
abGen: => Gen[A => A],
fabGen: => Gen[F[A => A]],
fComp: => F[A] => B)(
implicit
pfa: MatchResult[F[A]] => Prop,
pb: MatchResult[B] => Prop
) extends MustMatchers {

implicit class Equalable(v: F[A]) {
def valueForEq(f: F[A] => A) = f(v)
implicit class ExtractValueForEq(v: F[A]) {
def valueForEq: B = fComp(v)
}

// ap(fa)(point(_)) == fa
def apIdentityLaw = forAll(faGen, valueForEqGen) { (fa, v) =>
applicative
.ap(fa)(applicative.point(identity[A](_)))
.valueForEq(v) must beEqualTo(fa.valueForEq(v))
// Identity law: ap(fa)(point(_)) == fa
def apIdentityLaw = forAll(faGen) { fa =>
println(s"Checking identity of $fa")
val right: B = fa.valueForEq
val left: B = applicative.ap(fa)(applicative.point(identity[A](_))).valueForEq
left must beEqualTo(right)
}

// ap(point(a))(point(ab)) == point(ab(a))
Expand All @@ -43,8 +49,8 @@ class ApplicativeLaws[F[_], A](
}

//map(fa)(ab) == ap(fa)(point(ab))
def apDerivedMapLaw = forAll(faGen, abGen, valueForEqGen) { (fa, ab, v) =>
applicative.map(fa)(ab).valueForEq(v) must
beEqualTo(applicative.ap(fa)(applicative.point(ab)).valueForEq(v))
def apDerivedMapLaw = forAll(faGen, abGen) { (fa, ab) =>
applicative.map(fa)(ab).valueForEq must
beEqualTo(applicative.ap(fa)(applicative.point(ab)).valueForEq)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object KeyboardAndTimeApp extends App with RTS {

class KeyboardAndTimeApp(name: String) extends AwtApp(name) {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
val displayArea = new JTextArea
buildPane()

Expand Down