From 1db391dab1e176223f9fd7e1f81ae7be78019e14 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Wed, 19 Jun 2019 22:33:01 -0400 Subject: [PATCH 1/9] wip --- .travis.yml | 2 +- build.sbt | 2 +- .../scala-2.12-/cats/compat/StreamOps.scala | 10 +++++++ .../scala-2.13+/cats/compat/StreamOps.scala | 9 +++++++ core/src/main/scala/cats/data/OneAnd.scala | 1 + core/src/main/scala/cats/data/ZipStream.scala | 7 ++--- core/src/main/scala/cats/data/package.scala | 6 +++-- .../main/scala/cats/instances/parallel.scala | 4 ++- .../main/scala/cats/instances/sortedSet.scala | 7 ++--- .../main/scala/cats/instances/stream.scala | 19 +++++++------- .../scala/cats/kernel/laws/LawTests.scala | 2 +- .../cats/kernel/compat/package.scala | 5 ++++ .../cats/kernel/compat/package.scala | 8 ++++++ .../kernel/instances/StreamInstances.scala | 1 + .../cats/laws/discipline/Arbitrary.scala | 2 +- .../cats/tests/StreamMonadSuite.scala | 14 ++++++++++ .../cats/tests/LazyListMonadSuite.scala | 13 ++++++++++ .../BinCodecInvariantMonoidalSuite.scala | 2 ++ .../test/scala/cats/tests/FoldableSuite.scala | 26 ++++++++++++------- .../test/scala/cats/tests/OneAndSuite.scala | 9 ++++--- .../test/scala/cats/tests/ParallelSuite.scala | 1 + .../scala/cats/tests/RegressionSuite.scala | 2 ++ .../test/scala/cats/tests/StreamSuite.scala | 19 +++++--------- .../test/scala/cats/tests/TraverseSuite.scala | 4 ++- 24 files changed, 125 insertions(+), 50 deletions(-) create mode 100644 core/src/main/scala-2.12-/cats/compat/StreamOps.scala create mode 100644 core/src/main/scala-2.13+/cats/compat/StreamOps.scala create mode 100644 kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala create mode 100644 kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala create mode 100644 tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala create mode 100644 tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala diff --git a/.travis.yml b/.travis.yml index 600debd826..f980fe6ab8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ jdk: - oraclejdk8 scala_version_211: &scala_version_211 2.11.12 -scala_version_212: &scala_version_212 2.12.7 +scala_version_212: &scala_version_212 2.12.8 scala_version_213: &scala_version_213 2.13.0 before_install: diff --git a/build.sbt b/build.sbt index 12c1d4bbb2..dd4f230ab8 100644 --- a/build.sbt +++ b/build.sbt @@ -40,7 +40,7 @@ crossScalaVersionsFromTravis in Global := { def scalaVersionSpecificFolders(srcName: String, srcBaseDir: java.io.File, scalaVersion: String) = { def extraDirs(suffix: String) = - CrossType.Pure.sharedSrcDir(srcBaseDir, "main").toList.map(f => file(f.getPath + suffix)) + List(CrossType.Pure, CrossType.Full).flatMap(_.sharedSrcDir(srcBaseDir, srcName).toList.map(f => file(f.getPath + suffix))) CrossVersion.partialVersion(scalaVersion) match { case Some((2, y)) if y <= 12 => extraDirs("-2.12-") diff --git a/core/src/main/scala-2.12-/cats/compat/StreamOps.scala b/core/src/main/scala-2.12-/cats/compat/StreamOps.scala new file mode 100644 index 0000000000..51b67fb430 --- /dev/null +++ b/core/src/main/scala-2.12-/cats/compat/StreamOps.scala @@ -0,0 +1,10 @@ +package cats.compat + +object StreamOps { + def toStream[A](traversableOnce: TraversableOnce[A]): Stream[A] = traversableOnce.toStream + + def emptyStream[A]: Stream[A] = Stream.empty[A] + + def streamString: String = "Stream" + +} diff --git a/core/src/main/scala-2.13+/cats/compat/StreamOps.scala b/core/src/main/scala-2.13+/cats/compat/StreamOps.scala new file mode 100644 index 0000000000..edba673d00 --- /dev/null +++ b/core/src/main/scala-2.13+/cats/compat/StreamOps.scala @@ -0,0 +1,9 @@ +package cats.compat + +object StreamOps { + def toStream[A](io: IterableOnce[A]): LazyList[A] = LazyList.from(io) + + def emptyStream[A]: LazyList[A] = LazyList.empty[A] + + def streamString: String = "LazyList" +} diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 466934a011..1f21f33c03 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -4,6 +4,7 @@ package data import scala.annotation.tailrec import scala.collection.mutable.Builder import cats.instances.stream._ +import kernel.compat.Stream /** * A data type which represents a single element (head) and some other diff --git a/core/src/main/scala/cats/data/ZipStream.scala b/core/src/main/scala/cats/data/ZipStream.scala index 775d870a58..114e557efb 100644 --- a/core/src/main/scala/cats/data/ZipStream.scala +++ b/core/src/main/scala/cats/data/ZipStream.scala @@ -1,7 +1,8 @@ -package cats.data +package cats +package data -import cats.{Alternative, CommutativeApplicative, Eq} -import cats.instances.stream._ +import instances.stream._ +import kernel.compat.Stream class ZipStream[A](val value: Stream[A]) extends AnyVal diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index 8613e1b306..c4a2d25412 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -1,6 +1,8 @@ package cats - +import kernel.compat.Stream +import compat.StreamOps.toStream package object data { + type NonEmptyStream[A] = OneAnd[Stream, A] type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A] type IorNel[+B, +A] = Ior[NonEmptyList[B], A] @@ -14,7 +16,7 @@ package object data { def NonEmptyStream[A](head: A, tail: Stream[A] = Stream.empty): NonEmptyStream[A] = OneAnd(head, tail) def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] = - OneAnd(head, tail.toStream) + OneAnd(head, toStream(tail)) type NonEmptyMap[K, +A] = NonEmptyMapImpl.Type[K, A] val NonEmptyMap = NonEmptyMapImpl diff --git a/core/src/main/scala/cats/instances/parallel.scala b/core/src/main/scala/cats/instances/parallel.scala index 5240b8c54b..3c85561bc1 100644 --- a/core/src/main/scala/cats/instances/parallel.scala +++ b/core/src/main/scala/cats/instances/parallel.scala @@ -1,9 +1,11 @@ -package cats.instances +package cats +package instances import cats.data._ import cats.kernel.Semigroup import cats.syntax.either._ import cats.{~>, Applicative, Apply, FlatMap, Functor, Monad, NonEmptyParallel, Parallel} +import kernel.compat.Stream trait ParallelInstances extends ParallelInstances1 { implicit def catsParallelForEitherValidated[E: Semigroup]: Parallel[Either[E, ?], Validated[E, ?]] = diff --git a/core/src/main/scala/cats/instances/sortedSet.scala b/core/src/main/scala/cats/instances/sortedSet.scala index c7c62cf455..9ab67b59b8 100644 --- a/core/src/main/scala/cats/instances/sortedSet.scala +++ b/core/src/main/scala/cats/instances/sortedSet.scala @@ -5,6 +5,7 @@ import cats.kernel.{BoundedSemilattice, Hash, Order} import scala.collection.immutable.SortedSet import scala.annotation.tailrec import cats.implicits._ +import compat.StreamOps._ trait SortedSetInstances extends SortedSetInstances1 { @@ -92,13 +93,13 @@ trait SortedSetInstancesBinCompat0 { class SortedSetOrder[A: Order] extends Order[SortedSet[A]] { def compare(a1: SortedSet[A], a2: SortedSet[A]): Int = Order[Int].compare(a1.size, a2.size) match { - case 0 => Order.compare(a1.toStream, a2.toStream) + case 0 => Order.compare(toStream(a1), toStream(a2)) case x => x } override def eqv(s1: SortedSet[A], s2: SortedSet[A]): Boolean = { implicit val x = Order[A].toOrdering - s1.toStream.corresponds(s2.toStream)(Order[A].eqv) + toStream(s1).corresponds(toStream(s2))(Order[A].eqv) } } @@ -125,7 +126,7 @@ class SortedSetHash[A: Order: Hash] extends Hash[SortedSet[A]] { } override def eqv(s1: SortedSet[A], s2: SortedSet[A]): Boolean = { implicit val x = Order[A].toOrdering - s1.toStream.corresponds(s2.toStream)(Order[A].eqv) + toStream(s1).corresponds(toStream(s2))(Order[A].eqv) } } diff --git a/core/src/main/scala/cats/instances/stream.scala b/core/src/main/scala/cats/instances/stream.scala index e8c38a50ac..5ac853fb25 100644 --- a/core/src/main/scala/cats/instances/stream.scala +++ b/core/src/main/scala/cats/instances/stream.scala @@ -1,6 +1,7 @@ package cats package instances - +import kernel.compat.Stream +import compat.StreamOps._ import cats.syntax.show._ import scala.annotation.tailrec @@ -10,9 +11,9 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { : Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] = new Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] { - def empty[A]: Stream[A] = Stream.Empty + def empty[A]: Stream[A] = emptyStream - def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x #::: y + def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x lazyAppendedAll y def pure[A](x: A): Stream[A] = Stream(x) @@ -31,7 +32,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { else fb.map(fb => map2(fa, fb)(f)) def coflatMap[A, B](fa: Stream[A])(f: Stream[A] => B): Stream[B] = - fa.tails.toStream.init.map(f) + toStream(fa.tails).init.map(f) def foldLeft[A, B](fa: Stream[A], b: B)(f: (B, A) => B): B = fa.foldLeft(b)(f) @@ -71,7 +72,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { stack = tail state = Right(Some(b)) case Left(a) #:: tail => - stack = fn(a) #::: tail + stack = (fn(a) #::: tail).force advance() case empty => state = Right(None) @@ -98,7 +99,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { } } - it.toStream + toStream(it) } override def exists[A](fa: Stream[A])(p: A => Boolean): Boolean = @@ -153,7 +154,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { implicit def catsStdShowForStream[A: Show]: Show[Stream[A]] = new Show[Stream[A]] { - def show(fa: Stream[A]): String = if (fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)" + def show(fa: Stream[A]): String = if (fa.isEmpty) s"$streamString()" else s"$streamString(${fa.head.show}, ?)" } } @@ -171,13 +172,13 @@ trait StreamInstancesBinCompat0 { override def flattenOption[A](fa: Stream[Option[A]]): Stream[A] = fa.flatten def traverseFilter[G[_], A, B](fa: Stream[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Stream[B]] = - fa.foldRight(Eval.now(G.pure(Stream.empty[B])))( + fa.foldRight(Eval.now(G.pure(emptyStream[B])))( (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) ) .value override def filterA[G[_], A](fa: Stream[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Stream[A]] = - fa.foldRight(Eval.now(G.pure(Stream.empty[A])))( + fa.foldRight(Eval.now(G.pure(emptyStream[A])))( (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) ) .value diff --git a/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala index 92edc5f58e..4fc8f23405 100644 --- a/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala @@ -14,7 +14,7 @@ import org.scalatest.funsuite.AnyFunSuiteLike import scala.concurrent.duration.{Duration, FiniteDuration} import scala.collection.immutable.{BitSet, Queue} import scala.util.Random - +import compat.Stream import java.util.UUID import java.util.concurrent.TimeUnit.{DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS} diff --git a/kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala b/kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala new file mode 100644 index 0000000000..00f46bc94a --- /dev/null +++ b/kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala @@ -0,0 +1,5 @@ +package cats.kernel + +package object compat { + type Stream[+A] = scala.Stream[A] +} diff --git a/kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala b/kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala new file mode 100644 index 0000000000..087e04d649 --- /dev/null +++ b/kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala @@ -0,0 +1,8 @@ +package cats +package kernel + +package object compat { + type Stream[+A] = scala.LazyList[A] + + val Stream = LazyList +} diff --git a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala index 3aff14e137..c683f6f3ac 100644 --- a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala +++ b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala @@ -1,5 +1,6 @@ package cats.kernel package instances +import compat.Stream trait StreamInstances extends StreamInstances1 { implicit def catsKernelStdOrderForStream[A: Order]: Order[Stream[A]] = diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index a1025c9219..134c531547 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -1,7 +1,7 @@ package cats package laws package discipline - +import kernel.compat.Stream import cats.data.NonEmptyList.ZipNonEmptyList import cats.data.NonEmptyVector.ZipNonEmptyVector import scala.util.{Failure, Success, Try} diff --git a/tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala b/tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala new file mode 100644 index 0000000000..735b04aa7a --- /dev/null +++ b/tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala @@ -0,0 +1,14 @@ +package cats +package tests + +import laws.discipline.MonadTests +import data.NonEmptyStream +import cats.laws.discipline.arbitrary._ + + +class StreamMonadSuite extends CatsSuite { + checkAll("Stream[Int]", MonadTests[Stream].monad[Int, Int, Int]) + checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].monad[Int, Int, Int]) + + +} diff --git a/tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala b/tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala new file mode 100644 index 0000000000..7ba15cc678 --- /dev/null +++ b/tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala @@ -0,0 +1,13 @@ +package cats +package tests + +import cats.laws.discipline.MonadTests +import cats.data.NonEmptyStream +import cats.laws.discipline.arbitrary._ + +class LazyListMonadSuite extends CatsSuite { + + //todo: fix Monad[LazyList] implementation to make it stack safe + checkAll("LazyList[Int]", MonadTests[LazyList].monad[Int, Int, Int]) + checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].stackUnsafeMonad[Int, Int, Int]) +} diff --git a/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala b/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala index 768668d213..9e3d4b4588 100644 --- a/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala +++ b/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala @@ -8,6 +8,8 @@ import cats.implicits._ import cats.Eq import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests} import org.scalacheck.{Arbitrary, Gen} +import kernel.compat.Stream + object BinCodecInvariantMonoidalSuite { final case class MiniList[+A] private (val toList: List[A]) extends AnyVal { diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index af4dc6c704..d28b32a5c1 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -7,6 +7,8 @@ import scala.collection.immutable._ import cats.instances.all._ import cats.data._ import cats.laws.discipline.arbitrary._ +import kernel.compat.Stream +import compat.StreamOps.toStream abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit ArbFInt: Arbitrary[F[Int]], ArbFString: Arbitrary[F[String]]) @@ -336,7 +338,7 @@ class FoldableSuiteAdditional extends CatsSuite { } test("Foldable[Stream].foldM stack safety") { - checkMonadicFoldsStackSafety[Stream](_.toStream) + checkMonadicFoldsStackSafety[Stream](toStream) } test("Foldable[Vector].foldM/existsM/forallM/findM/collectFirstSomeM stack safety") { @@ -365,32 +367,36 @@ class FoldableSuiteAdditional extends CatsSuite { checkMonadicFoldsStackSafety[NonEmptyStream](xs => NonEmptyStream(xs.head, xs.tail: _*)) } - test("Foldable[Stream]") { - val F = Foldable[Stream] + val F = Foldable[Stream] + def bomb[A]: A = sys.error("boom") + val dangerous = 0 #:: 1 #:: 2 #:: bomb[Stream[Int]] - def bomb[A]: A = sys.error("boom") - val dangerous = 0 #:: 1 #:: 2 #:: bomb[Stream[Int]] + test("Foldable[Stream] doesn't blow up") { // doesn't blow up - this also ensures it works for infinite streams. assert(contains(dangerous, 2).value) + } - // lazy results don't blow up unless you call .value on them. + test("lazy results don't blow up unless you call .value on them") { val doom: Eval[Boolean] = contains(dangerous, -1) + } - // ensure that the Lazy[B] param to foldRight is actually being - // handled lazily. it only needs to be evaluated if we reach the + test("Lazy[B] param to foldRight is actually being handled lazily") { + // ensure that the . it only needs to be evaluated if we reach the // "end" of the fold. val trap = Eval.later(bomb[Boolean]) val result = F.foldRight(1 #:: 2 #:: Stream.empty, trap) { (n, lb) => if (n == 2) Now(true) else lb } assert(result.value) + } - // test trampolining + test("trampolining") { val large = Stream((1 to 10000): _*) assert(contains(large, 10000).value) + } - // test laziness of foldM + test("laziness of foldM"){ dangerous.foldM(0)((acc, a) => if (a < 2) Some(acc + a) else None) should ===(None) } diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index f73f0e20f7..d563ba68cf 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -19,7 +19,8 @@ import cats.laws.discipline.{ TraverseTests } import cats.laws.discipline.arbitrary._ - +import kernel.compat.Stream +import compat.StreamOps.toStream class OneAndSuite extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit implicit override val generatorDrivenConfig: PropertyCheckConfiguration = @@ -87,7 +88,7 @@ class OneAndSuite extends CatsSuite { implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[Stream, ?]] - checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].monad[Int, Int, Int]) + checkAll("Monad[NonEmptyStream[A]]", SerializableTests.serializable(Monad[NonEmptyStream])) checkAll("NonEmptyStream[Int]", ComonadTests[NonEmptyStream].comonad[Int, Int, Int]) @@ -110,7 +111,7 @@ class OneAndSuite extends CatsSuite { test("Show is formatted correctly") { val oneAnd = NonEmptyStream("Test") - oneAnd.show should ===("OneAnd(Test, Stream())") + oneAnd.show should ===(s"OneAnd(Test, ${compat.StreamOps.streamString}())") } test("Creating OneAnd + unwrap is identity") { @@ -224,6 +225,6 @@ class ReducibleNonEmptyStreamSuite extends ReducibleSuite[NonEmptyStream]("NonEm // if we inline this we get a bewildering implicit numeric widening // error message in Scala 2.10 val tailStart: Long = start + 1L - NonEmptyStream(start, (tailStart).to(endInclusive).toStream) + NonEmptyStream(start, toStream(tailStart.to(endInclusive))) } } diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index dabb16cfb9..3c12235048 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -11,6 +11,7 @@ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ import org.typelevel.discipline.scalatest.Discipline import scala.collection.immutable.SortedSet +import kernel.compat.Stream class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { diff --git a/tests/src/test/scala/cats/tests/RegressionSuite.scala b/tests/src/test/scala/cats/tests/RegressionSuite.scala index a04d5f08c4..cc62c52a71 100644 --- a/tests/src/test/scala/cats/tests/RegressionSuite.scala +++ b/tests/src/test/scala/cats/tests/RegressionSuite.scala @@ -4,6 +4,8 @@ package tests import cats.data.{Const, NonEmptyList, StateT} import scala.collection.mutable import scala.collection.immutable.SortedMap +import kernel.compat.Stream + class RegressionSuite extends CatsSuite { // toy state class diff --git a/tests/src/test/scala/cats/tests/StreamSuite.scala b/tests/src/test/scala/cats/tests/StreamSuite.scala index b26c744464..2cfc145b7f 100644 --- a/tests/src/test/scala/cats/tests/StreamSuite.scala +++ b/tests/src/test/scala/cats/tests/StreamSuite.scala @@ -1,18 +1,12 @@ package cats package tests -import cats.laws.discipline.{ - AlternativeTests, - CoflatMapTests, - CommutativeApplyTests, - MonadTests, - SemigroupalTests, - SerializableTests, - TraverseFilterTests, - TraverseTests -} +import cats.laws.discipline.{AlternativeTests, CoflatMapTests, CommutativeApplyTests, MonadTests, SemigroupalTests, SerializableTests, TraverseFilterTests, TraverseTests} import cats.data.ZipStream import cats.laws.discipline.arbitrary._ +import kernel.compat.Stream +import compat.StreamOps.streamString + class StreamSuite extends CatsSuite { checkAll("Stream[Int]", SemigroupalTests[Stream].semigroupal[Int, Int, Int]) @@ -24,7 +18,6 @@ class StreamSuite extends CatsSuite { checkAll("Stream[Int]", AlternativeTests[Stream].alternative[Int, Int, Int]) checkAll("Alternative[Stream]", SerializableTests.serializable(Alternative[Stream])) - checkAll("Stream[Int]", MonadTests[Stream].monad[Int, Int, Int]) checkAll("Monad[Stream]", SerializableTests.serializable(Monad[Stream])) checkAll("Stream[Int] with Option", TraverseTests[Stream].traverse[Int, Int, Int, Set[Int], Option, Option]) @@ -37,8 +30,8 @@ class StreamSuite extends CatsSuite { checkAll("ZipStream[Int]", CommutativeApplyTests[ZipStream].apply[Int, Int, Int]) test("show") { - Stream(1, 2, 3).show should ===("Stream(1, ?)") - Stream.empty[Int].show should ===("Stream()") + Stream(1, 2, 3).show should ===(s"$streamString(1, ?)") + Stream.empty[Int].show should ===(s"$streamString()") } test("Show[Stream] is referentially transparent, unlike Stream.toString") { diff --git a/tests/src/test/scala/cats/tests/TraverseSuite.scala b/tests/src/test/scala/cats/tests/TraverseSuite.scala index de0cda385e..fba9023fca 100644 --- a/tests/src/test/scala/cats/tests/TraverseSuite.scala +++ b/tests/src/test/scala/cats/tests/TraverseSuite.scala @@ -4,6 +4,8 @@ package tests import org.scalacheck.Arbitrary import cats.instances.all._ +import kernel.compat.Stream +import compat.StreamOps.toStream abstract class TraverseSuite[F[_]: Traverse](name: String)(implicit ArbFInt: Arbitrary[F[Int]]) extends CatsSuite { @@ -66,7 +68,7 @@ class TraverseSuiteAdditional extends CatsSuite { } test("Traverse[Stream].zipWithIndex stack safety") { - checkZipWithIndexedStackSafety[Stream](_.toStream) + checkZipWithIndexedStackSafety[Stream](toStream) } test("Traverse[Vector].zipWithIndex stack safety") { From 86118405ddbaa98d083c15b86095bcbfc6b5ab5d Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Wed, 19 Jun 2019 22:46:54 -0400 Subject: [PATCH 2/9] wip --- build.sbt | 1 + core/src/main/scala/cats/instances/all.scala | 2 +- .../main/scala/cats/instances/lazyList.scala | 185 +++++++++++++++++ .../main/scala/cats/instances/package.scala | 2 +- .../main/scala/cats/instances/parallel.scala | 2 +- .../main/scala/cats/instances/stream.scala | 187 ------------------ .../cats/kernel/instances/AllInstances.scala | 2 +- ...nstances.scala => LazyListInstances.scala} | 6 +- .../kernel/instances/stream/package.scala | 2 +- .../test/scala/cats/tests/FoldableSuite.scala | 4 +- 10 files changed, 196 insertions(+), 197 deletions(-) create mode 100644 core/src/main/scala/cats/instances/lazyList.scala delete mode 100644 core/src/main/scala/cats/instances/stream.scala rename kernel/src/main/scala/cats/kernel/instances/{StreamInstances.scala => LazyListInstances.scala} (93%) diff --git a/build.sbt b/build.sbt index dd4f230ab8..74d9790e26 100644 --- a/build.sbt +++ b/build.sbt @@ -52,6 +52,7 @@ def scalaVersionSpecificFolders(srcName: String, srcBaseDir: java.io.File, scala lazy val commonSettings = Seq( crossScalaVersions := (crossScalaVersionsFromTravis in Global).value, + scalaVersion := crossScalaVersions.value.find(_.contains("2.13")).get, scalacOptions ++= commonScalacOptions(scalaVersion.value), Compile / unmanagedSourceDirectories ++= scalaVersionSpecificFolders("main", baseDirectory.value, scalaVersion.value), Test / unmanagedSourceDirectories ++= scalaVersionSpecificFolders("test", baseDirectory.value, scalaVersion.value), diff --git a/core/src/main/scala/cats/instances/all.scala b/core/src/main/scala/cats/instances/all.scala index ccc50d259b..d489fc768f 100644 --- a/core/src/main/scala/cats/instances/all.scala +++ b/core/src/main/scala/cats/instances/all.scala @@ -25,7 +25,7 @@ trait AllInstances with SetInstances with SortedMapInstances with SortedSetInstances - with StreamInstances + with LazyListInstances with StringInstances with SymbolInstances with TryInstances diff --git a/core/src/main/scala/cats/instances/lazyList.scala b/core/src/main/scala/cats/instances/lazyList.scala new file mode 100644 index 0000000000..e85988c030 --- /dev/null +++ b/core/src/main/scala/cats/instances/lazyList.scala @@ -0,0 +1,185 @@ +package cats +package instances +import cats.syntax.show._ + +import scala.annotation.tailrec + +trait LazyListInstances extends cats.kernel.instances.LazyListInstances { + implicit val catsStdInstancesForLazyList + : Traverse[LazyList] with Alternative[LazyList] with Monad[LazyList] with CoflatMap[LazyList] = + new Traverse[LazyList] with Alternative[LazyList] with Monad[LazyList] with CoflatMap[LazyList] { + + def empty[A]: LazyList[A] = LazyList.empty + + def combineK[A](x: LazyList[A], y: LazyList[A]): LazyList[A] = x lazyAppendedAll y + + def pure[A](x: A): LazyList[A] = LazyList(x) + + override def map[A, B](fa: LazyList[A])(f: A => B): LazyList[B] = + fa.map(f) + + def flatMap[A, B](fa: LazyList[A])(f: A => LazyList[B]): LazyList[B] = + fa.flatMap(f) + + override def map2[A, B, Z](fa: LazyList[A], fb: LazyList[B])(f: (A, B) => Z): LazyList[Z] = + if (fb.isEmpty) LazyList.empty // do O(1) work if fb is empty + else fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty + + override def map2Eval[A, B, Z](fa: LazyList[A], fb: Eval[LazyList[B]])(f: (A, B) => Z): Eval[LazyList[Z]] = + if (fa.isEmpty) Eval.now(LazyList.empty) // no need to evaluate fb + else fb.map(fb => map2(fa, fb)(f)) + + def coflatMap[A, B](fa: LazyList[A])(f: LazyList[A] => B): LazyList[B] = + fa.tails.to(LazyList).init.map(f) + + def foldLeft[A, B](fa: LazyList[A], b: B)(f: (B, A) => B): B = + fa.foldLeft(b)(f) + + def foldRight[A, B](fa: LazyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + Now(fa).flatMap { s => + // Note that we don't use pattern matching to deconstruct the + // stream, since that would needlessly force the tail. + if (s.isEmpty) lb else f(s.head, Eval.defer(foldRight(s.tail, lb)(f))) + } + + override def foldMap[A, B](fa: LazyList[A])(f: A => B)(implicit B: Monoid[B]): B = + B.combineAll(fa.iterator.map(f)) + + def traverse[G[_], A, B](fa: LazyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[LazyList[B]] = + // We use foldRight to avoid possible stack overflows. Since + // we don't want to return a Eval[_] instance, we call .value + // at the end. + foldRight(fa, Always(G.pure(LazyList.empty[B]))) { (a, lgsb) => + G.map2Eval(f(a), lgsb)(_ #:: _) + }.value + + override def mapWithIndex[A, B](fa: LazyList[A])(f: (A, Int) => B): LazyList[B] = + fa.zipWithIndex.map(ai => f(ai._1, ai._2)) + + override def zipWithIndex[A](fa: LazyList[A]): LazyList[(A, Int)] = + fa.zipWithIndex + + def tailRecM[A, B](a: A)(fn: A => LazyList[Either[A, B]]): LazyList[B] = { + val it: Iterator[B] = new Iterator[B] { + var stack: LazyList[Either[A, B]] = fn(a) + var state: Either[Unit, Option[B]] = Left(()) + + @tailrec + def advance(): Unit = stack match { + case Right(b) #:: tail => + stack = tail + state = Right(Some(b)) + case Left(a) #:: tail => + stack = (fn(a) #::: tail).force + advance() + case empty => + state = Right(None) + } + + @tailrec + def hasNext: Boolean = state match { + case Left(()) => + advance() + hasNext + case Right(o) => + o.isDefined + } + + @tailrec + def next(): B = state match { + case Left(()) => + advance() + next() + case Right(o) => + val b = o.get + advance() + b + } + } + + it.to(LazyList) + } + + override def exists[A](fa: LazyList[A])(p: A => Boolean): Boolean = + fa.exists(p) + + override def forall[A](fa: LazyList[A])(p: A => Boolean): Boolean = + fa.forall(p) + + override def get[A](fa: LazyList[A])(idx: Long): Option[A] = { + @tailrec + def go(idx: Long, s: LazyList[A]): Option[A] = + s match { + case h #:: tail => + if (idx == 0L) Some(h) else go(idx - 1L, tail) + case _ => None + } + if (idx < 0L) None else go(idx, fa) + } + + override def isEmpty[A](fa: LazyList[A]): Boolean = fa.isEmpty + + override def foldM[G[_], A, B](fa: LazyList[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = { + def step(in: (LazyList[A], B)): G[Either[(LazyList[A], B), B]] = { + val (s, b) = in + if (s.isEmpty) + G.pure(Right(b)) + else + G.map(f(b, s.head)) { bnext => + Left((s.tail, bnext)) + } + } + + G.tailRecM((fa, z))(step) + } + + override def fold[A](fa: LazyList[A])(implicit A: Monoid[A]): A = A.combineAll(fa) + + override def toList[A](fa: LazyList[A]): List[A] = fa.toList + + override def reduceLeftOption[A](fa: LazyList[A])(f: (A, A) => A): Option[A] = + fa.reduceLeftOption(f) + + override def find[A](fa: LazyList[A])(f: A => Boolean): Option[A] = fa.find(f) + + override def algebra[A]: Monoid[LazyList[A]] = new kernel.instances.LazyListMonoid[A] + + override def collectFirst[A, B](fa: LazyList[A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(pf) + + override def collectFirstSome[A, B](fa: LazyList[A])(f: A => Option[B]): Option[B] = + fa.collectFirst(Function.unlift(f)) + } + + implicit def catsStdShowForStream[A: Show]: Show[LazyList[A]] = + new Show[LazyList[A]] { + def show(fa: LazyList[A]): String = if (fa.isEmpty) "LazyList()" else s"LazyList(${fa.head.show}, ?)" + } +} + +trait StreamInstancesBinCompat0 { + implicit val catsStdTraverseFilterForLazyList: TraverseFilter[LazyList] = new TraverseFilter[LazyList] { + val traverse: Traverse[LazyList] = cats.instances.stream.catsStdInstancesForLazyList + + override def mapFilter[A, B](fa: LazyList[A])(f: (A) => Option[B]): LazyList[B] = + fa.collect(Function.unlift(f)) + + override def filter[A](fa: LazyList[A])(f: (A) => Boolean): LazyList[A] = fa.filter(f) + + override def collect[A, B](fa: LazyList[A])(f: PartialFunction[A, B]): LazyList[B] = fa.collect(f) + + override def flattenOption[A](fa: LazyList[Option[A]]): LazyList[A] = fa.flatten + + def traverseFilter[G[_], A, B](fa: LazyList[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[LazyList[B]] = + fa.foldRight(Eval.now(G.pure(LazyList.empty[B])))( + (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) + ) + .value + + override def filterA[G[_], A](fa: LazyList[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[LazyList[A]] = + fa.foldRight(Eval.now(G.pure(LazyList.empty[A])))( + (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) + ) + .value + + } +} diff --git a/core/src/main/scala/cats/instances/package.scala b/core/src/main/scala/cats/instances/package.scala index c171490fbb..ffcfad1b39 100644 --- a/core/src/main/scala/cats/instances/package.scala +++ b/core/src/main/scala/cats/instances/package.scala @@ -40,7 +40,7 @@ package object instances { object short extends ShortInstances object sortedMap extends SortedMapInstances with SortedMapInstancesBinCompat0 with SortedMapInstancesBinCompat1 object sortedSet extends SortedSetInstances with SortedSetInstancesBinCompat0 - object stream extends StreamInstances with StreamInstancesBinCompat0 + object stream extends LazyListInstances with StreamInstancesBinCompat0 object string extends StringInstances object try_ extends TryInstances object tuple extends TupleInstances with Tuple2InstancesBinCompat0 diff --git a/core/src/main/scala/cats/instances/parallel.scala b/core/src/main/scala/cats/instances/parallel.scala index 3c85561bc1..f8f3c33c87 100644 --- a/core/src/main/scala/cats/instances/parallel.scala +++ b/core/src/main/scala/cats/instances/parallel.scala @@ -69,7 +69,7 @@ trait ParallelInstances extends ParallelInstances1 { implicit def catsStdParallelForZipStream[A]: Parallel[Stream, ZipStream] = new Parallel[Stream, ZipStream] { - def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream + def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForLazyList def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream def sequential: ZipStream ~> Stream = diff --git a/core/src/main/scala/cats/instances/stream.scala b/core/src/main/scala/cats/instances/stream.scala deleted file mode 100644 index 5ac853fb25..0000000000 --- a/core/src/main/scala/cats/instances/stream.scala +++ /dev/null @@ -1,187 +0,0 @@ -package cats -package instances -import kernel.compat.Stream -import compat.StreamOps._ -import cats.syntax.show._ - -import scala.annotation.tailrec - -trait StreamInstances extends cats.kernel.instances.StreamInstances { - implicit val catsStdInstancesForStream - : Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] = - new Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] { - - def empty[A]: Stream[A] = emptyStream - - def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x lazyAppendedAll y - - def pure[A](x: A): Stream[A] = Stream(x) - - override def map[A, B](fa: Stream[A])(f: A => B): Stream[B] = - fa.map(f) - - def flatMap[A, B](fa: Stream[A])(f: A => Stream[B]): Stream[B] = - fa.flatMap(f) - - override def map2[A, B, Z](fa: Stream[A], fb: Stream[B])(f: (A, B) => Z): Stream[Z] = - if (fb.isEmpty) Stream.empty // do O(1) work if fb is empty - else fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty - - override def map2Eval[A, B, Z](fa: Stream[A], fb: Eval[Stream[B]])(f: (A, B) => Z): Eval[Stream[Z]] = - if (fa.isEmpty) Eval.now(Stream.empty) // no need to evaluate fb - else fb.map(fb => map2(fa, fb)(f)) - - def coflatMap[A, B](fa: Stream[A])(f: Stream[A] => B): Stream[B] = - toStream(fa.tails).init.map(f) - - def foldLeft[A, B](fa: Stream[A], b: B)(f: (B, A) => B): B = - fa.foldLeft(b)(f) - - def foldRight[A, B](fa: Stream[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - Now(fa).flatMap { s => - // Note that we don't use pattern matching to deconstruct the - // stream, since that would needlessly force the tail. - if (s.isEmpty) lb else f(s.head, Eval.defer(foldRight(s.tail, lb)(f))) - } - - override def foldMap[A, B](fa: Stream[A])(f: A => B)(implicit B: Monoid[B]): B = - B.combineAll(fa.iterator.map(f)) - - def traverse[G[_], A, B](fa: Stream[A])(f: A => G[B])(implicit G: Applicative[G]): G[Stream[B]] = - // We use foldRight to avoid possible stack overflows. Since - // we don't want to return a Eval[_] instance, we call .value - // at the end. - foldRight(fa, Always(G.pure(Stream.empty[B]))) { (a, lgsb) => - G.map2Eval(f(a), lgsb)(_ #:: _) - }.value - - override def mapWithIndex[A, B](fa: Stream[A])(f: (A, Int) => B): Stream[B] = - fa.zipWithIndex.map(ai => f(ai._1, ai._2)) - - override def zipWithIndex[A](fa: Stream[A]): Stream[(A, Int)] = - fa.zipWithIndex - - def tailRecM[A, B](a: A)(fn: A => Stream[Either[A, B]]): Stream[B] = { - val it: Iterator[B] = new Iterator[B] { - var stack: Stream[Either[A, B]] = fn(a) - var state: Either[Unit, Option[B]] = Left(()) - - @tailrec - def advance(): Unit = stack match { - case Right(b) #:: tail => - stack = tail - state = Right(Some(b)) - case Left(a) #:: tail => - stack = (fn(a) #::: tail).force - advance() - case empty => - state = Right(None) - } - - @tailrec - def hasNext: Boolean = state match { - case Left(()) => - advance() - hasNext - case Right(o) => - o.isDefined - } - - @tailrec - def next(): B = state match { - case Left(()) => - advance() - next() - case Right(o) => - val b = o.get - advance() - b - } - } - - toStream(it) - } - - override def exists[A](fa: Stream[A])(p: A => Boolean): Boolean = - fa.exists(p) - - override def forall[A](fa: Stream[A])(p: A => Boolean): Boolean = - fa.forall(p) - - override def get[A](fa: Stream[A])(idx: Long): Option[A] = { - @tailrec - def go(idx: Long, s: Stream[A]): Option[A] = - s match { - case h #:: tail => - if (idx == 0L) Some(h) else go(idx - 1L, tail) - case _ => None - } - if (idx < 0L) None else go(idx, fa) - } - - override def isEmpty[A](fa: Stream[A]): Boolean = fa.isEmpty - - override def foldM[G[_], A, B](fa: Stream[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = { - def step(in: (Stream[A], B)): G[Either[(Stream[A], B), B]] = { - val (s, b) = in - if (s.isEmpty) - G.pure(Right(b)) - else - G.map(f(b, s.head)) { bnext => - Left((s.tail, bnext)) - } - } - - G.tailRecM((fa, z))(step) - } - - override def fold[A](fa: Stream[A])(implicit A: Monoid[A]): A = A.combineAll(fa) - - override def toList[A](fa: Stream[A]): List[A] = fa.toList - - override def reduceLeftOption[A](fa: Stream[A])(f: (A, A) => A): Option[A] = - fa.reduceLeftOption(f) - - override def find[A](fa: Stream[A])(f: A => Boolean): Option[A] = fa.find(f) - - override def algebra[A]: Monoid[Stream[A]] = new kernel.instances.StreamMonoid[A] - - override def collectFirst[A, B](fa: Stream[A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(pf) - - override def collectFirstSome[A, B](fa: Stream[A])(f: A => Option[B]): Option[B] = - fa.collectFirst(Function.unlift(f)) - } - - implicit def catsStdShowForStream[A: Show]: Show[Stream[A]] = - new Show[Stream[A]] { - def show(fa: Stream[A]): String = if (fa.isEmpty) s"$streamString()" else s"$streamString(${fa.head.show}, ?)" - } -} - -trait StreamInstancesBinCompat0 { - implicit val catsStdTraverseFilterForStream: TraverseFilter[Stream] = new TraverseFilter[Stream] { - val traverse: Traverse[Stream] = cats.instances.stream.catsStdInstancesForStream - - override def mapFilter[A, B](fa: Stream[A])(f: (A) => Option[B]): Stream[B] = - fa.collect(Function.unlift(f)) - - override def filter[A](fa: Stream[A])(f: (A) => Boolean): Stream[A] = fa.filter(f) - - override def collect[A, B](fa: Stream[A])(f: PartialFunction[A, B]): Stream[B] = fa.collect(f) - - override def flattenOption[A](fa: Stream[Option[A]]): Stream[A] = fa.flatten - - def traverseFilter[G[_], A, B](fa: Stream[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Stream[B]] = - fa.foldRight(Eval.now(G.pure(emptyStream[B])))( - (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) - ) - .value - - override def filterA[G[_], A](fa: Stream[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Stream[A]] = - fa.foldRight(Eval.now(G.pure(emptyStream[A])))( - (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) - ) - .value - - } -} diff --git a/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala b/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala index d66ba81365..6eb5a5e8dd 100644 --- a/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala +++ b/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala @@ -25,7 +25,7 @@ trait AllInstances with QueueInstances with SetInstances with ShortInstances - with StreamInstances + with LazyListInstances with StringInstances with SymbolInstances with TupleInstances diff --git a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala b/kernel/src/main/scala/cats/kernel/instances/LazyListInstances.scala similarity index 93% rename from kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala rename to kernel/src/main/scala/cats/kernel/instances/LazyListInstances.scala index c683f6f3ac..4930aabcb8 100644 --- a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala +++ b/kernel/src/main/scala/cats/kernel/instances/LazyListInstances.scala @@ -2,11 +2,11 @@ package cats.kernel package instances import compat.Stream -trait StreamInstances extends StreamInstances1 { +trait LazyListInstances extends StreamInstances1 { implicit def catsKernelStdOrderForStream[A: Order]: Order[Stream[A]] = new StreamOrder[A] implicit def catsKernelStdMonoidForStream[A]: Monoid[Stream[A]] = - new StreamMonoid[A] + new LazyListMonoid[A] } trait StreamInstances1 extends StreamInstances2 { @@ -44,7 +44,7 @@ class StreamEq[A](implicit ev: Eq[A]) extends Eq[Stream[A]] { else StaticMethods.iteratorEq(xs.iterator, ys.iterator) } -class StreamMonoid[A] extends Monoid[Stream[A]] { +class LazyListMonoid[A] extends Monoid[Stream[A]] { def empty: Stream[A] = Stream.empty def combine(x: Stream[A], y: Stream[A]): Stream[A] = x ++ y diff --git a/kernel/src/main/scala/cats/kernel/instances/stream/package.scala b/kernel/src/main/scala/cats/kernel/instances/stream/package.scala index 53bbfe510d..18a9b3ec1d 100644 --- a/kernel/src/main/scala/cats/kernel/instances/stream/package.scala +++ b/kernel/src/main/scala/cats/kernel/instances/stream/package.scala @@ -1,4 +1,4 @@ package cats.kernel package instances -package object stream extends StreamInstances +package object stream extends LazyListInstances diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index d28b32a5c1..5f5370d7ce 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -403,10 +403,10 @@ class FoldableSuiteAdditional extends CatsSuite { def foldableStreamWithDefaultImpl = new Foldable[Stream] { def foldLeft[A, B](fa: Stream[A], b: B)(f: (B, A) => B): B = - instances.stream.catsStdInstancesForStream.foldLeft(fa, b)(f) + instances.stream.catsStdInstancesForLazyList.foldLeft(fa, b)(f) def foldRight[A, B](fa: Stream[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - instances.stream.catsStdInstancesForStream.foldRight(fa, lb)(f) + instances.stream.catsStdInstancesForLazyList.foldRight(fa, lb)(f) } test(".foldLeftM short-circuiting") { From 24f6fb1a41223507f92771c03b3ae4fff48648f6 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Wed, 19 Jun 2019 18:07:49 -1000 Subject: [PATCH 3/9] fix LazyList tests --- .../main/scala/cats/instances/lazyList.scala | 46 +++++++++++++------ .../test/scala/cats/tests/FoldableSuite.scala | 8 ++-- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/core/src/main/scala/cats/instances/lazyList.scala b/core/src/main/scala/cats/instances/lazyList.scala index e85988c030..fd5b951ee8 100644 --- a/core/src/main/scala/cats/instances/lazyList.scala +++ b/core/src/main/scala/cats/instances/lazyList.scala @@ -61,24 +61,35 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { def tailRecM[A, B](a: A)(fn: A => LazyList[Either[A, B]]): LazyList[B] = { val it: Iterator[B] = new Iterator[B] { - var stack: LazyList[Either[A, B]] = fn(a) - var state: Either[Unit, Option[B]] = Left(()) + var stack: List[Iterator[Either[A, B]]] = Nil + var state: Either[A, Option[B]] = Left(a) @tailrec def advance(): Unit = stack match { - case Right(b) #:: tail => - stack = tail - state = Right(Some(b)) - case Left(a) #:: tail => - stack = (fn(a) #::: tail).force - advance() - case empty => + case head :: tail => + if (head.hasNext) { + head.next match { + case Right(b) => + state = Right(Some(b)) + case Left(a) => + val nextFront = fn(a).iterator + stack = nextFront :: stack + advance() + } + } + else { + stack = tail + advance() + } + case Nil => state = Right(None) } @tailrec def hasNext: Boolean = state match { - case Left(()) => + case Left(a) => + // this is the first run + stack = fn(a).iterator :: Nil advance() hasNext case Right(o) => @@ -87,7 +98,9 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { @tailrec def next(): B = state match { - case Left(()) => + case Left(a) => + // this is the first run + stack = fn(a).iterator :: Nil advance() next() case Right(o) => @@ -97,7 +110,7 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { } } - it.to(LazyList) + LazyList.from(it) } override def exists[A](fa: LazyList[A])(p: A => Boolean): Boolean = @@ -124,10 +137,13 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { val (s, b) = in if (s.isEmpty) G.pure(Right(b)) - else - G.map(f(b, s.head)) { bnext => - Left((s.tail, bnext)) + else { + val h = s.head + val t = s.tail + G.map(f(b, h)) { bnext => + Left((t, bnext)) } + } } G.tailRecM((fa, z))(step) diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index 5f5370d7ce..25811b3428 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -369,7 +369,7 @@ class FoldableSuiteAdditional extends CatsSuite { val F = Foldable[Stream] def bomb[A]: A = sys.error("boom") - val dangerous = 0 #:: 1 #:: 2 #:: bomb[Stream[Int]] + val dangerous = 0 #:: 1 #:: 2 #:: bomb[Int] #:: Stream.empty test("Foldable[Stream] doesn't blow up") { @@ -428,7 +428,7 @@ class FoldableSuiteAdditional extends CatsSuite { if (s == stop) Left(acc) else Right(acc + s) } - def boom: Stream[String] = sys.error("boom") + def boom: Stream[String] = sys.error("boom") #:: Stream.empty assert(concatUntil("STOP" #:: boom, "STOP") == Left("")) assert(concatUntil("Zero" #:: "STOP" #:: boom, "STOP") == Left("Zero")) assert(concatUntil("Zero" #:: "One" #:: "STOP" #:: boom, "STOP") == Left("ZeroOne")) @@ -436,14 +436,14 @@ class FoldableSuiteAdditional extends CatsSuite { test(".existsM/.forallM short-circuiting") { implicit val F = foldableStreamWithDefaultImpl - def boom: Stream[Boolean] = sys.error("boom") + def boom: Stream[Boolean] = sys.error("boom") #:: Stream.empty assert(F.existsM[Id, Boolean](true #:: boom)(identity) == true) assert(F.forallM[Id, Boolean](false #:: boom)(identity) == false) } test(".findM/.collectFirstSomeM short-circuiting") { implicit val F = foldableStreamWithDefaultImpl - def boom: Stream[Int] = sys.error("boom") + def boom: Stream[Int] = sys.error("boom") #:: Stream.empty assert((1 #:: boom).findM[Id](_ > 0) == Some(1)) assert((1 #:: boom).collectFirstSomeM[Id, Int](Option.apply) == Some(1)) } From f291661c83c01bd3202d584f850864eff9942a49 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 13:13:59 -0400 Subject: [PATCH 4/9] all core tests passes except OneAnd tailRecM Stacksafety on 2.13 --- build.sbt | 30 +-- .../scala-2.12-/cats/instances/stream.scala | 193 ++++++++++++++++++ .../cats/instances/lazyList.scala | 41 ++-- core/src/main/scala/cats/instances/all.scala | 2 +- .../main/scala/cats/instances/package.scala | 3 +- .../main/scala/cats/instances/parallel.scala | 2 +- .../cats/kernel/instances/AllInstances.scala | 2 +- ...tInstances.scala => StreamInstances.scala} | 6 +- .../kernel/instances/stream/package.scala | 2 +- .../cats/tests/StreamMonadSuite.scala | 14 -- .../cats/tests/LazyListMonadSuite.scala | 13 -- .../test/scala/cats/tests/FoldableSuite.scala | 14 +- .../test/scala/cats/tests/OneAndSuite.scala | 2 +- .../test/scala/cats/tests/StreamSuite.scala | 2 + 14 files changed, 253 insertions(+), 73 deletions(-) create mode 100644 core/src/main/scala-2.12-/cats/instances/stream.scala rename core/src/main/{scala => scala-2.13+}/cats/instances/lazyList.scala (86%) rename kernel/src/main/scala/cats/kernel/instances/{LazyListInstances.scala => StreamInstances.scala} (93%) delete mode 100644 tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala delete mode 100644 tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala diff --git a/build.sbt b/build.sbt index 74d9790e26..63a23e7163 100644 --- a/build.sbt +++ b/build.sbt @@ -26,6 +26,16 @@ val isTravisBuild = settingKey[Boolean]("Flag indicating whether the current bui val crossScalaVersionsFromTravis = settingKey[Seq[String]]("Scala versions set in .travis.yml as scala_version_XXX") isTravisBuild in Global := sys.env.get("TRAVIS").isDefined +val scalatestVersion = "3.1.0-SNAP13" + +val scalatestplusScalaCheckVersion = "1.0.0-SNAP8" + +val scalaCheckVersion = "1.14.0" + +val disciplineVersion = "0.12.0-M3" + +val kindProjectorVersion = "0.10.3" + crossScalaVersionsFromTravis in Global := { val manifest = (baseDirectory in ThisBuild).value / ".travis.yml" import collection.JavaConverters._ @@ -49,10 +59,12 @@ def scalaVersionSpecificFolders(srcName: String, srcBaseDir: java.io.File, scala case _ => Nil } } - -lazy val commonSettings = Seq( +lazy val commonScalaVersionSettings = Seq( crossScalaVersions := (crossScalaVersionsFromTravis in Global).value, - scalaVersion := crossScalaVersions.value.find(_.contains("2.13")).get, + scalaVersion := crossScalaVersions.value.find(_.contains("2.12")).get +) + +lazy val commonSettings = commonScalaVersionSettings ++ Seq( scalacOptions ++= commonScalacOptions(scalaVersion.value), Compile / unmanagedSourceDirectories ++= scalaVersionSpecificFolders("main", baseDirectory.value, scalaVersion.value), Test / unmanagedSourceDirectories ++= scalaVersionSpecificFolders("test", baseDirectory.value, scalaVersion.value), @@ -84,7 +96,7 @@ lazy val catsSettings = Seq( incOptions := incOptions.value.withLogRecompileOnMacro(false), libraryDependencies ++= Seq( "org.typelevel" %%% "machinist" % "0.6.8", - compilerPlugin("org.typelevel" %% "kind-projector" % "0.10.3") + compilerPlugin("org.typelevel" %% "kind-projector" % kindProjectorVersion) ) ++ macroDependencies(scalaVersion.value), ) ++ commonSettings ++ publishSettings ++ scoverageSettings ++ simulacrumSettings @@ -151,13 +163,7 @@ lazy val includeGeneratedSrc: Setting[_] = { } } -val scalatestVersion = "3.1.0-SNAP13" -val scalatestplusScalaCheckVersion = "1.0.0-SNAP8" - -val scalaCheckVersion = "1.14.0" - -val disciplineVersion = "0.12.0-M3" lazy val disciplineDependencies = Seq( libraryDependencies ++= Seq("org.scalacheck" %%% "scalacheck" % scalaCheckVersion, @@ -633,8 +639,8 @@ lazy val binCompatTest = project .disablePlugins(CoursierPlugin) .settings(noPublishSettings) .settings( - crossScalaVersions := (crossScalaVersionsFromTravis in Global).value, - addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.9"), + commonScalaVersionSettings, + addCompilerPlugin("org.typelevel" %% "kind-projector" % kindProjectorVersion), libraryDependencies ++= List( { if (priorTo2_13(scalaVersion.value)) diff --git a/core/src/main/scala-2.12-/cats/instances/stream.scala b/core/src/main/scala-2.12-/cats/instances/stream.scala new file mode 100644 index 0000000000..05a014c781 --- /dev/null +++ b/core/src/main/scala-2.12-/cats/instances/stream.scala @@ -0,0 +1,193 @@ +package cats +package instances + +import cats.syntax.show._ + +import scala.annotation.tailrec + +//For cross compile with backward compatibility +trait LazyListInstances extends StreamInstances with StreamInstancesBinCompat0 { + val catsStdInstancesForLazyList = catsStdInstancesForStream +} + +trait StreamInstances extends cats.kernel.instances.StreamInstances { + + implicit val catsStdInstancesForStream + : Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] = + new Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] { + + def empty[A]: Stream[A] = Stream.Empty + + def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x #::: y + + def pure[A](x: A): Stream[A] = Stream(x) + + override def map[A, B](fa: Stream[A])(f: A => B): Stream[B] = + fa.map(f) + + def flatMap[A, B](fa: Stream[A])(f: A => Stream[B]): Stream[B] = + fa.flatMap(f) + + override def map2[A, B, Z](fa: Stream[A], fb: Stream[B])(f: (A, B) => Z): Stream[Z] = + if (fb.isEmpty) Stream.empty // do O(1) work if fb is empty + else fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty + + override def map2Eval[A, B, Z](fa: Stream[A], fb: Eval[Stream[B]])(f: (A, B) => Z): Eval[Stream[Z]] = + if (fa.isEmpty) Eval.now(Stream.empty) // no need to evaluate fb + else fb.map(fb => map2(fa, fb)(f)) + + def coflatMap[A, B](fa: Stream[A])(f: Stream[A] => B): Stream[B] = + fa.tails.toStream.init.map(f) + + def foldLeft[A, B](fa: Stream[A], b: B)(f: (B, A) => B): B = + fa.foldLeft(b)(f) + + def foldRight[A, B](fa: Stream[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + Now(fa).flatMap { s => + // Note that we don't use pattern matching to deconstruct the + // stream, since that would needlessly force the tail. + if (s.isEmpty) lb else f(s.head, Eval.defer(foldRight(s.tail, lb)(f))) + } + + override def foldMap[A, B](fa: Stream[A])(f: A => B)(implicit B: Monoid[B]): B = + B.combineAll(fa.iterator.map(f)) + + def traverse[G[_], A, B](fa: Stream[A])(f: A => G[B])(implicit G: Applicative[G]): G[Stream[B]] = + // We use foldRight to avoid possible stack overflows. Since + // we don't want to return a Eval[_] instance, we call .value + // at the end. + foldRight(fa, Always(G.pure(Stream.empty[B]))) { (a, lgsb) => + G.map2Eval(f(a), lgsb)(_ #:: _) + }.value + + override def mapWithIndex[A, B](fa: Stream[A])(f: (A, Int) => B): Stream[B] = + fa.zipWithIndex.map(ai => f(ai._1, ai._2)) + + override def zipWithIndex[A](fa: Stream[A]): Stream[(A, Int)] = + fa.zipWithIndex + + def tailRecM[A, B](a: A)(fn: A => Stream[Either[A, B]]): Stream[B] = { + val it: Iterator[B] = new Iterator[B] { + var stack: Stream[Either[A, B]] = fn(a) + var state: Either[Unit, Option[B]] = Left(()) + + @tailrec + def advance(): Unit = stack match { + case Right(b) #:: tail => + stack = tail + state = Right(Some(b)) + case Left(a) #:: tail => + stack = fn(a) #::: tail + advance() + case empty => + state = Right(None) + } + + @tailrec + def hasNext: Boolean = state match { + case Left(()) => + advance() + hasNext + case Right(o) => + o.isDefined + } + + @tailrec + def next(): B = state match { + case Left(()) => + advance() + next() + case Right(o) => + val b = o.get + advance() + b + } + } + + it.toStream + } + + override def exists[A](fa: Stream[A])(p: A => Boolean): Boolean = + fa.exists(p) + + override def forall[A](fa: Stream[A])(p: A => Boolean): Boolean = + fa.forall(p) + + override def get[A](fa: Stream[A])(idx: Long): Option[A] = { + @tailrec + def go(idx: Long, s: Stream[A]): Option[A] = + s match { + case h #:: tail => + if (idx == 0L) Some(h) else go(idx - 1L, tail) + case _ => None + } + if (idx < 0L) None else go(idx, fa) + } + + override def isEmpty[A](fa: Stream[A]): Boolean = fa.isEmpty + + override def foldM[G[_], A, B](fa: Stream[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = { + def step(in: (Stream[A], B)): G[Either[(Stream[A], B), B]] = { + val (s, b) = in + if (s.isEmpty) + G.pure(Right(b)) + else + G.map(f(b, s.head)) { bnext => + Left((s.tail, bnext)) + } + } + + G.tailRecM((fa, z))(step) + } + + override def fold[A](fa: Stream[A])(implicit A: Monoid[A]): A = A.combineAll(fa) + + override def toList[A](fa: Stream[A]): List[A] = fa.toList + + override def reduceLeftOption[A](fa: Stream[A])(f: (A, A) => A): Option[A] = + fa.reduceLeftOption(f) + + override def find[A](fa: Stream[A])(f: A => Boolean): Option[A] = fa.find(f) + + override def algebra[A]: Monoid[Stream[A]] = new kernel.instances.StreamMonoid[A] + + override def collectFirst[A, B](fa: Stream[A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(pf) + + override def collectFirstSome[A, B](fa: Stream[A])(f: A => Option[B]): Option[B] = + fa.collectFirst(Function.unlift(f)) + } + + implicit def catsStdShowForStream[A: Show]: Show[Stream[A]] = + new Show[Stream[A]] { + def show(fa: Stream[A]): String = if (fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)" + } + +} + +trait StreamInstancesBinCompat0 { + implicit val catsStdTraverseFilterForStream: TraverseFilter[Stream] = new TraverseFilter[Stream] { + val traverse: Traverse[Stream] = cats.instances.stream.catsStdInstancesForStream + + override def mapFilter[A, B](fa: Stream[A])(f: (A) => Option[B]): Stream[B] = + fa.collect(Function.unlift(f)) + + override def filter[A](fa: Stream[A])(f: (A) => Boolean): Stream[A] = fa.filter(f) + + override def collect[A, B](fa: Stream[A])(f: PartialFunction[A, B]): Stream[B] = fa.collect(f) + + override def flattenOption[A](fa: Stream[Option[A]]): Stream[A] = fa.flatten + + def traverseFilter[G[_], A, B](fa: Stream[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Stream[B]] = + fa.foldRight(Eval.now(G.pure(Stream.empty[B])))( + (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) + ) + .value + + override def filterA[G[_], A](fa: Stream[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Stream[A]] = + fa.foldRight(Eval.now(G.pure(Stream.empty[A])))( + (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) + ) + .value + + } +} diff --git a/core/src/main/scala/cats/instances/lazyList.scala b/core/src/main/scala-2.13+/cats/instances/lazyList.scala similarity index 86% rename from core/src/main/scala/cats/instances/lazyList.scala rename to core/src/main/scala-2.13+/cats/instances/lazyList.scala index fd5b951ee8..a33fb71fba 100644 --- a/core/src/main/scala/cats/instances/lazyList.scala +++ b/core/src/main/scala-2.13+/cats/instances/lazyList.scala @@ -1,12 +1,21 @@ package cats package instances +import cats.kernel import cats.syntax.show._ import scala.annotation.tailrec -trait LazyListInstances extends cats.kernel.instances.LazyListInstances { +//For cross compile with backward compatibility +trait StreamInstancesBinCompat0 + +//For cross compile with backward compatibility +trait StreamInstances extends LazyListInstances { + val catsStdInstancesForStream = catsStdInstancesForLazyList +} + +trait LazyListInstances extends cats.kernel.instances.StreamInstances { implicit val catsStdInstancesForLazyList - : Traverse[LazyList] with Alternative[LazyList] with Monad[LazyList] with CoflatMap[LazyList] = + : Traverse[LazyList] with Alternative[LazyList] with Monad[LazyList] with CoflatMap[LazyList] = new Traverse[LazyList] with Alternative[LazyList] with Monad[LazyList] with CoflatMap[LazyList] { def empty[A]: LazyList[A] = LazyList.empty @@ -46,9 +55,9 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { B.combineAll(fa.iterator.map(f)) def traverse[G[_], A, B](fa: LazyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[LazyList[B]] = - // We use foldRight to avoid possible stack overflows. Since - // we don't want to return a Eval[_] instance, we call .value - // at the end. + // We use foldRight to avoid possible stack overflows. Since + // we don't want to return a Eval[_] instance, we call .value + // at the end. foldRight(fa, Always(G.pure(LazyList.empty[B]))) { (a, lgsb) => G.map2Eval(f(a), lgsb)(_ #:: _) }.value @@ -138,10 +147,8 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { if (s.isEmpty) G.pure(Right(b)) else { - val h = s.head - val t = s.tail - G.map(f(b, h)) { bnext => - Left((t, bnext)) + G.map(f(b, s.head)) { bnext => + Left((s.tail, bnext)) } } } @@ -158,7 +165,7 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { override def find[A](fa: LazyList[A])(f: A => Boolean): Option[A] = fa.find(f) - override def algebra[A]: Monoid[LazyList[A]] = new kernel.instances.LazyListMonoid[A] + override def algebra[A]: Monoid[LazyList[A]] = new kernel.instances.StreamMonoid[A] override def collectFirst[A, B](fa: LazyList[A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(pf) @@ -166,15 +173,13 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { fa.collectFirst(Function.unlift(f)) } - implicit def catsStdShowForStream[A: Show]: Show[LazyList[A]] = + implicit def catsStdShowForLazyList[A: Show]: Show[LazyList[A]] = new Show[LazyList[A]] { def show(fa: LazyList[A]): String = if (fa.isEmpty) "LazyList()" else s"LazyList(${fa.head.show}, ?)" } -} -trait StreamInstancesBinCompat0 { implicit val catsStdTraverseFilterForLazyList: TraverseFilter[LazyList] = new TraverseFilter[LazyList] { - val traverse: Traverse[LazyList] = cats.instances.stream.catsStdInstancesForLazyList + val traverse: Traverse[LazyList] = catsStdInstancesForLazyList override def mapFilter[A, B](fa: LazyList[A])(f: (A) => Option[B]): LazyList[B] = fa.collect(Function.unlift(f)) @@ -187,14 +192,14 @@ trait StreamInstancesBinCompat0 { def traverseFilter[G[_], A, B](fa: LazyList[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[LazyList[B]] = fa.foldRight(Eval.now(G.pure(LazyList.empty[B])))( - (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) - ) + (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) + ) .value override def filterA[G[_], A](fa: LazyList[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[LazyList[A]] = fa.foldRight(Eval.now(G.pure(LazyList.empty[A])))( - (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) - ) + (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) + ) .value } diff --git a/core/src/main/scala/cats/instances/all.scala b/core/src/main/scala/cats/instances/all.scala index d489fc768f..ccc50d259b 100644 --- a/core/src/main/scala/cats/instances/all.scala +++ b/core/src/main/scala/cats/instances/all.scala @@ -25,7 +25,7 @@ trait AllInstances with SetInstances with SortedMapInstances with SortedSetInstances - with LazyListInstances + with StreamInstances with StringInstances with SymbolInstances with TryInstances diff --git a/core/src/main/scala/cats/instances/package.scala b/core/src/main/scala/cats/instances/package.scala index ffcfad1b39..bd880a9cb1 100644 --- a/core/src/main/scala/cats/instances/package.scala +++ b/core/src/main/scala/cats/instances/package.scala @@ -40,7 +40,8 @@ package object instances { object short extends ShortInstances object sortedMap extends SortedMapInstances with SortedMapInstancesBinCompat0 with SortedMapInstancesBinCompat1 object sortedSet extends SortedSetInstances with SortedSetInstancesBinCompat0 - object stream extends LazyListInstances with StreamInstancesBinCompat0 + object stream extends StreamInstances with StreamInstancesBinCompat0 + object lazyList extends LazyListInstances object string extends StringInstances object try_ extends TryInstances object tuple extends TupleInstances with Tuple2InstancesBinCompat0 diff --git a/core/src/main/scala/cats/instances/parallel.scala b/core/src/main/scala/cats/instances/parallel.scala index f8f3c33c87..3c85561bc1 100644 --- a/core/src/main/scala/cats/instances/parallel.scala +++ b/core/src/main/scala/cats/instances/parallel.scala @@ -69,7 +69,7 @@ trait ParallelInstances extends ParallelInstances1 { implicit def catsStdParallelForZipStream[A]: Parallel[Stream, ZipStream] = new Parallel[Stream, ZipStream] { - def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForLazyList + def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream def sequential: ZipStream ~> Stream = diff --git a/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala b/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala index 6eb5a5e8dd..d66ba81365 100644 --- a/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala +++ b/kernel/src/main/scala/cats/kernel/instances/AllInstances.scala @@ -25,7 +25,7 @@ trait AllInstances with QueueInstances with SetInstances with ShortInstances - with LazyListInstances + with StreamInstances with StringInstances with SymbolInstances with TupleInstances diff --git a/kernel/src/main/scala/cats/kernel/instances/LazyListInstances.scala b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala similarity index 93% rename from kernel/src/main/scala/cats/kernel/instances/LazyListInstances.scala rename to kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala index 4930aabcb8..c683f6f3ac 100644 --- a/kernel/src/main/scala/cats/kernel/instances/LazyListInstances.scala +++ b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala @@ -2,11 +2,11 @@ package cats.kernel package instances import compat.Stream -trait LazyListInstances extends StreamInstances1 { +trait StreamInstances extends StreamInstances1 { implicit def catsKernelStdOrderForStream[A: Order]: Order[Stream[A]] = new StreamOrder[A] implicit def catsKernelStdMonoidForStream[A]: Monoid[Stream[A]] = - new LazyListMonoid[A] + new StreamMonoid[A] } trait StreamInstances1 extends StreamInstances2 { @@ -44,7 +44,7 @@ class StreamEq[A](implicit ev: Eq[A]) extends Eq[Stream[A]] { else StaticMethods.iteratorEq(xs.iterator, ys.iterator) } -class LazyListMonoid[A] extends Monoid[Stream[A]] { +class StreamMonoid[A] extends Monoid[Stream[A]] { def empty: Stream[A] = Stream.empty def combine(x: Stream[A], y: Stream[A]): Stream[A] = x ++ y diff --git a/kernel/src/main/scala/cats/kernel/instances/stream/package.scala b/kernel/src/main/scala/cats/kernel/instances/stream/package.scala index 18a9b3ec1d..53bbfe510d 100644 --- a/kernel/src/main/scala/cats/kernel/instances/stream/package.scala +++ b/kernel/src/main/scala/cats/kernel/instances/stream/package.scala @@ -1,4 +1,4 @@ package cats.kernel package instances -package object stream extends LazyListInstances +package object stream extends StreamInstances diff --git a/tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala b/tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala deleted file mode 100644 index 735b04aa7a..0000000000 --- a/tests/src/test/scala-2.12-/cats/tests/StreamMonadSuite.scala +++ /dev/null @@ -1,14 +0,0 @@ -package cats -package tests - -import laws.discipline.MonadTests -import data.NonEmptyStream -import cats.laws.discipline.arbitrary._ - - -class StreamMonadSuite extends CatsSuite { - checkAll("Stream[Int]", MonadTests[Stream].monad[Int, Int, Int]) - checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].monad[Int, Int, Int]) - - -} diff --git a/tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala b/tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala deleted file mode 100644 index 7ba15cc678..0000000000 --- a/tests/src/test/scala-2.13+/cats/tests/LazyListMonadSuite.scala +++ /dev/null @@ -1,13 +0,0 @@ -package cats -package tests - -import cats.laws.discipline.MonadTests -import cats.data.NonEmptyStream -import cats.laws.discipline.arbitrary._ - -class LazyListMonadSuite extends CatsSuite { - - //todo: fix Monad[LazyList] implementation to make it stack safe - checkAll("LazyList[Int]", MonadTests[LazyList].monad[Int, Int, Int]) - checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].stackUnsafeMonad[Int, Int, Int]) -} diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index 25811b3428..4694b76c3d 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -401,16 +401,16 @@ class FoldableSuiteAdditional extends CatsSuite { } - def foldableStreamWithDefaultImpl = new Foldable[Stream] { + def foldableLazyListWithDefaultImpl = new Foldable[Stream] { def foldLeft[A, B](fa: Stream[A], b: B)(f: (B, A) => B): B = - instances.stream.catsStdInstancesForLazyList.foldLeft(fa, b)(f) + instances.lazyList.catsStdInstancesForLazyList.foldLeft(fa, b)(f) def foldRight[A, B](fa: Stream[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - instances.stream.catsStdInstancesForLazyList.foldRight(fa, lb)(f) + instances.lazyList.catsStdInstancesForLazyList.foldRight(fa, lb)(f) } test(".foldLeftM short-circuiting") { - implicit val F = foldableStreamWithDefaultImpl + implicit val F = foldableLazyListWithDefaultImpl val ns = Stream.continually(1) val res = F.foldLeftM[Either[Int, ?], Int, Int](ns, 0) { (sum, n) => if (sum >= 100000) Left(sum) else Right(sum + n) @@ -419,7 +419,7 @@ class FoldableSuiteAdditional extends CatsSuite { } test(".foldLeftM short-circuiting optimality") { - implicit val F = foldableStreamWithDefaultImpl + implicit val F = foldableLazyListWithDefaultImpl // test that no more elements are evaluated than absolutely necessary @@ -435,14 +435,14 @@ class FoldableSuiteAdditional extends CatsSuite { } test(".existsM/.forallM short-circuiting") { - implicit val F = foldableStreamWithDefaultImpl + implicit val F = foldableLazyListWithDefaultImpl def boom: Stream[Boolean] = sys.error("boom") #:: Stream.empty assert(F.existsM[Id, Boolean](true #:: boom)(identity) == true) assert(F.forallM[Id, Boolean](false #:: boom)(identity) == false) } test(".findM/.collectFirstSomeM short-circuiting") { - implicit val F = foldableStreamWithDefaultImpl + implicit val F = foldableLazyListWithDefaultImpl def boom: Stream[Int] = sys.error("boom") #:: Stream.empty assert((1 #:: boom).findM[Id](_ > 0) == Some(1)) assert((1 #:: boom).collectFirstSomeM[Id, Int](Option.apply) == Some(1)) diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index d563ba68cf..df27896a0a 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -88,7 +88,7 @@ class OneAndSuite extends CatsSuite { implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[Stream, ?]] - + checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].monad[Int, Int, Int]) checkAll("Monad[NonEmptyStream[A]]", SerializableTests.serializable(Monad[NonEmptyStream])) checkAll("NonEmptyStream[Int]", ComonadTests[NonEmptyStream].comonad[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/StreamSuite.scala b/tests/src/test/scala/cats/tests/StreamSuite.scala index 2cfc145b7f..ee2dfc0ad4 100644 --- a/tests/src/test/scala/cats/tests/StreamSuite.scala +++ b/tests/src/test/scala/cats/tests/StreamSuite.scala @@ -18,6 +18,8 @@ class StreamSuite extends CatsSuite { checkAll("Stream[Int]", AlternativeTests[Stream].alternative[Int, Int, Int]) checkAll("Alternative[Stream]", SerializableTests.serializable(Alternative[Stream])) + + checkAll("Stream[Int]", MonadTests[Stream].monad[Int, Int, Int]) checkAll("Monad[Stream]", SerializableTests.serializable(Monad[Stream])) checkAll("Stream[Int] with Option", TraverseTests[Stream].traverse[Int, Int, Int, Set[Int], Option, Option]) From fa997cab578b999818c0ff9423db882f20e55055 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 13:40:53 -0400 Subject: [PATCH 5/9] temporarily disable NoneEmptyStream tailRecM stack safety test --- tests/src/test/scala/cats/tests/OneAndSuite.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index df27896a0a..05c8468319 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -88,7 +88,8 @@ class OneAndSuite extends CatsSuite { implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[Stream, ?]] - checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].monad[Int, Int, Int]) + //OneAnd's tailRecM fails on LazyList due to the fact that. todo: replace NonEmptyStream with NonEmptyLazyList using newtype + checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].stackUnsafeMonad[Int, Int, Int]) checkAll("Monad[NonEmptyStream[A]]", SerializableTests.serializable(Monad[NonEmptyStream])) checkAll("NonEmptyStream[Int]", ComonadTests[NonEmptyStream].comonad[Int, Int, Int]) From 0a884e2cadadc926be2462a68fcabd323476b9af Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 17:35:31 -0400 Subject: [PATCH 6/9] all tests passes, it seems --- .../scala-2.12-/cats/compat/StreamOps.scala | 10 ---- .../scala-2.12-/cats/compat/lazyList.scala | 10 ++++ .../scala-2.13+/cats/compat/StreamOps.scala | 9 --- .../scala-2.13+/cats/compat/lazyList.scala | 7 +++ core/src/main/scala/cats/data/OneAnd.scala | 16 +++--- core/src/main/scala/cats/data/ZipStream.scala | 12 ++-- core/src/main/scala/cats/data/package.scala | 13 +++-- .../main/scala/cats/instances/parallel.scala | 16 +++--- .../main/scala/cats/instances/sortedSet.scala | 8 +-- .../scala/cats/kernel/laws/LawTests.scala | 14 ++--- .../cats/kernel/compat/lazyList.scala | 6 ++ .../cats/kernel/compat/package.scala | 5 -- .../cats/kernel/compat/lazyList.scala | 9 +++ .../cats/kernel/compat/package.scala | 8 --- .../kernel/instances/StreamInstances.scala | 43 +++++++------- .../cats/laws/discipline/Arbitrary.scala | 4 +- .../BinCodecInvariantMonoidalSuite.scala | 4 +- .../test/scala/cats/tests/FoldableSuite.scala | 55 +++++++++--------- .../test/scala/cats/tests/LazyListSuite.scala | 56 +++++++++++++++++++ .../test/scala/cats/tests/OneAndSuite.scala | 28 +++++----- .../test/scala/cats/tests/ParallelSuite.scala | 10 ++-- .../scala/cats/tests/RegressionSuite.scala | 6 +- .../test/scala/cats/tests/StreamSuite.scala | 56 ------------------- .../test/scala/cats/tests/TraverseSuite.scala | 10 ++-- 24 files changed, 208 insertions(+), 207 deletions(-) delete mode 100644 core/src/main/scala-2.12-/cats/compat/StreamOps.scala create mode 100644 core/src/main/scala-2.12-/cats/compat/lazyList.scala delete mode 100644 core/src/main/scala-2.13+/cats/compat/StreamOps.scala create mode 100644 core/src/main/scala-2.13+/cats/compat/lazyList.scala create mode 100644 kernel/src/main/scala-2.12-/cats/kernel/compat/lazyList.scala delete mode 100644 kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala create mode 100644 kernel/src/main/scala-2.13+/cats/kernel/compat/lazyList.scala delete mode 100644 kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala create mode 100644 tests/src/test/scala/cats/tests/LazyListSuite.scala delete mode 100644 tests/src/test/scala/cats/tests/StreamSuite.scala diff --git a/core/src/main/scala-2.12-/cats/compat/StreamOps.scala b/core/src/main/scala-2.12-/cats/compat/StreamOps.scala deleted file mode 100644 index 51b67fb430..0000000000 --- a/core/src/main/scala-2.12-/cats/compat/StreamOps.scala +++ /dev/null @@ -1,10 +0,0 @@ -package cats.compat - -object StreamOps { - def toStream[A](traversableOnce: TraversableOnce[A]): Stream[A] = traversableOnce.toStream - - def emptyStream[A]: Stream[A] = Stream.empty[A] - - def streamString: String = "Stream" - -} diff --git a/core/src/main/scala-2.12-/cats/compat/lazyList.scala b/core/src/main/scala-2.12-/cats/compat/lazyList.scala new file mode 100644 index 0000000000..7b72469239 --- /dev/null +++ b/core/src/main/scala-2.12-/cats/compat/lazyList.scala @@ -0,0 +1,10 @@ +package cats.compat + +object lazyList { + + + def toLazyList[A](traversableOnce: TraversableOnce[A]): Stream[A] = traversableOnce.toStream + + def lazyListString: String = "Stream" + +} diff --git a/core/src/main/scala-2.13+/cats/compat/StreamOps.scala b/core/src/main/scala-2.13+/cats/compat/StreamOps.scala deleted file mode 100644 index edba673d00..0000000000 --- a/core/src/main/scala-2.13+/cats/compat/StreamOps.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.compat - -object StreamOps { - def toStream[A](io: IterableOnce[A]): LazyList[A] = LazyList.from(io) - - def emptyStream[A]: LazyList[A] = LazyList.empty[A] - - def streamString: String = "LazyList" -} diff --git a/core/src/main/scala-2.13+/cats/compat/lazyList.scala b/core/src/main/scala-2.13+/cats/compat/lazyList.scala new file mode 100644 index 0000000000..d9f95edff9 --- /dev/null +++ b/core/src/main/scala-2.13+/cats/compat/lazyList.scala @@ -0,0 +1,7 @@ +package cats.compat + +object lazyList { + def toLazyList[A](io: IterableOnce[A]): LazyList[A] = LazyList.from(io) + + def lazyListString: String = "LazyList" +} diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 1f21f33c03..8cf8497436 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -4,7 +4,7 @@ package data import scala.annotation.tailrec import scala.collection.mutable.Builder import cats.instances.stream._ -import kernel.compat.Stream +import kernel.compat.lazyList._ /** * A data type which represents a single element (head) and some other @@ -192,22 +192,22 @@ sealed abstract private[data] class OneAndInstances extends OneAndLowPriority0 { } sealed abstract private[data] class OneAndLowPriority4 { - implicit val catsDataComonadForNonEmptyStream: Comonad[OneAnd[Stream, ?]] = - new Comonad[OneAnd[Stream, ?]] { - def coflatMap[A, B](fa: OneAnd[Stream, A])(f: OneAnd[Stream, A] => B): OneAnd[Stream, B] = { - @tailrec def consume(as: Stream[A], buf: Builder[B, Stream[B]]): Stream[B] = + implicit val catsDataComonadForNonEmptyStream: Comonad[OneAnd[LazyList, ?]] = + new Comonad[OneAnd[LazyList, ?]] { + def coflatMap[A, B](fa: OneAnd[LazyList, A])(f: OneAnd[LazyList, A] => B): OneAnd[LazyList, B] = { + @tailrec def consume(as: LazyList[A], buf: Builder[B, LazyList[B]]): LazyList[B] = if (as.isEmpty) buf.result else { val tail = as.tail consume(tail, buf += f(OneAnd(as.head, tail))) } - OneAnd(f(fa), consume(fa.tail, Stream.newBuilder)) + OneAnd(f(fa), consume(fa.tail, LazyList.newBuilder)) } - def extract[A](fa: OneAnd[Stream, A]): A = + def extract[A](fa: OneAnd[LazyList, A]): A = fa.head - def map[A, B](fa: OneAnd[Stream, A])(f: A => B): OneAnd[Stream, B] = + def map[A, B](fa: OneAnd[LazyList, A])(f: A => B): OneAnd[LazyList, B] = fa.map(f) } } diff --git a/core/src/main/scala/cats/data/ZipStream.scala b/core/src/main/scala/cats/data/ZipStream.scala index 114e557efb..5509318478 100644 --- a/core/src/main/scala/cats/data/ZipStream.scala +++ b/core/src/main/scala/cats/data/ZipStream.scala @@ -2,17 +2,17 @@ package cats package data import instances.stream._ -import kernel.compat.Stream +import kernel.compat.lazyList._ -class ZipStream[A](val value: Stream[A]) extends AnyVal +class ZipStream[A](val value: LazyList[A]) extends AnyVal object ZipStream { - def apply[A](value: Stream[A]): ZipStream[A] = new ZipStream(value) + def apply[A](value: LazyList[A]): ZipStream[A] = new ZipStream(value) implicit val catsDataAlternativeForZipStream: Alternative[ZipStream] with CommutativeApplicative[ZipStream] = new Alternative[ZipStream] with CommutativeApplicative[ZipStream] { - def pure[A](x: A): ZipStream[A] = new ZipStream(Stream.continually(x)) + def pure[A](x: A): ZipStream[A] = new ZipStream(LazyList.continually(x)) override def map[A, B](fa: ZipStream[A])(f: (A) => B): ZipStream[B] = ZipStream(fa.value.map(f)) @@ -23,10 +23,10 @@ object ZipStream { override def product[A, B](fa: ZipStream[A], fb: ZipStream[B]): ZipStream[(A, B)] = ZipStream(fa.value.zip(fb.value)) - def empty[A]: ZipStream[A] = ZipStream(Stream.empty[A]) + def empty[A]: ZipStream[A] = ZipStream(LazyList.empty[A]) def combineK[A](x: ZipStream[A], y: ZipStream[A]): ZipStream[A] = - ZipStream(Alternative[Stream].combineK(x.value, y.value)) + ZipStream(Alternative[LazyList].combineK(x.value, y.value)) } implicit def catsDataEqForZipStream[A: Eq]: Eq[ZipStream[A]] = Eq.by(_.value) diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index c4a2d25412..35751571d9 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -1,9 +1,9 @@ package cats -import kernel.compat.Stream -import compat.StreamOps.toStream +import kernel.compat.lazyList._ +import compat.lazyList.toLazyList package object data { - type NonEmptyStream[A] = OneAnd[Stream, A] + type NonEmptyStream[A] = OneAnd[LazyList, A] type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A] type IorNel[+B, +A] = Ior[NonEmptyList[B], A] type IorNec[+B, +A] = Ior[NonEmptyChain[B], A] @@ -13,10 +13,10 @@ package object data { type EitherNes[E, +A] = Either[NonEmptySet[E], A] type ValidatedNec[+E, +A] = Validated[NonEmptyChain[E], A] - def NonEmptyStream[A](head: A, tail: Stream[A] = Stream.empty): NonEmptyStream[A] = + def NonEmptyStream[A](head: A, tail: LazyList[A] = LazyList.empty): NonEmptyStream[A] = OneAnd(head, tail) def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] = - OneAnd(head, toStream(tail)) + OneAnd(head, toLazyList(tail)) type NonEmptyMap[K, +A] = NonEmptyMapImpl.Type[K, A] val NonEmptyMap = NonEmptyMapImpl @@ -85,4 +85,7 @@ package object data { def apply[S, A](f: S => A, s: S): Store[S, A] = RepresentableStore[S => ?, S, A](f, s) } + + type ZipLazyList[A] = ZipStream[A] + val ZipLazyList = ZipStream } diff --git a/core/src/main/scala/cats/instances/parallel.scala b/core/src/main/scala/cats/instances/parallel.scala index 3c85561bc1..845bf43bd2 100644 --- a/core/src/main/scala/cats/instances/parallel.scala +++ b/core/src/main/scala/cats/instances/parallel.scala @@ -5,7 +5,7 @@ import cats.data._ import cats.kernel.Semigroup import cats.syntax.either._ import cats.{~>, Applicative, Apply, FlatMap, Functor, Monad, NonEmptyParallel, Parallel} -import kernel.compat.Stream +import kernel.compat.lazyList._ trait ParallelInstances extends ParallelInstances1 { implicit def catsParallelForEitherValidated[E: Semigroup]: Parallel[Either[E, ?], Validated[E, ?]] = @@ -66,17 +66,17 @@ trait ParallelInstances extends ParallelInstances1 { λ[Vector ~> ZipVector](v => new ZipVector(v)) } - implicit def catsStdParallelForZipStream[A]: Parallel[Stream, ZipStream] = - new Parallel[Stream, ZipStream] { + implicit def catsStdParallelForZipStream[A]: Parallel[LazyList, ZipStream] = + new Parallel[LazyList, ZipStream] { - def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream + def monad: Monad[LazyList] = cats.instances.stream.catsStdInstancesForStream def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream - def sequential: ZipStream ~> Stream = - λ[ZipStream ~> Stream](_.value) + def sequential: ZipStream ~> LazyList = + λ[ZipStream ~> LazyList](_.value) - def parallel: Stream ~> ZipStream = - λ[Stream ~> ZipStream](v => new ZipStream(v)) + def parallel: LazyList ~> ZipStream = + λ[LazyList ~> ZipStream](v => new ZipStream(v)) } implicit def catsParallelForEitherTNestedParallelValidated[F[_], M[_], E: Semigroup]( diff --git a/core/src/main/scala/cats/instances/sortedSet.scala b/core/src/main/scala/cats/instances/sortedSet.scala index 9ab67b59b8..24823a6ccc 100644 --- a/core/src/main/scala/cats/instances/sortedSet.scala +++ b/core/src/main/scala/cats/instances/sortedSet.scala @@ -5,7 +5,7 @@ import cats.kernel.{BoundedSemilattice, Hash, Order} import scala.collection.immutable.SortedSet import scala.annotation.tailrec import cats.implicits._ -import compat.StreamOps._ +import compat.lazyList._ trait SortedSetInstances extends SortedSetInstances1 { @@ -93,13 +93,13 @@ trait SortedSetInstancesBinCompat0 { class SortedSetOrder[A: Order] extends Order[SortedSet[A]] { def compare(a1: SortedSet[A], a2: SortedSet[A]): Int = Order[Int].compare(a1.size, a2.size) match { - case 0 => Order.compare(toStream(a1), toStream(a2)) + case 0 => Order.compare(toLazyList(a1), toLazyList(a2)) case x => x } override def eqv(s1: SortedSet[A], s2: SortedSet[A]): Boolean = { implicit val x = Order[A].toOrdering - toStream(s1).corresponds(toStream(s2))(Order[A].eqv) + toLazyList(s1).corresponds(toLazyList(s2))(Order[A].eqv) } } @@ -126,7 +126,7 @@ class SortedSetHash[A: Order: Hash] extends Hash[SortedSet[A]] { } override def eqv(s1: SortedSet[A], s2: SortedSet[A]): Boolean = { implicit val x = Order[A].toOrdering - toStream(s1).corresponds(toStream(s2))(Order[A].eqv) + toLazyList(s1).corresponds(toLazyList(s2))(Order[A].eqv) } } diff --git a/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala index 4fc8f23405..156bdea44a 100644 --- a/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/shared/src/test/scala/cats/kernel/laws/LawTests.scala @@ -14,7 +14,7 @@ import org.scalatest.funsuite.AnyFunSuiteLike import scala.concurrent.duration.{Duration, FiniteDuration} import scala.collection.immutable.{BitSet, Queue} import scala.util.Random -import compat.Stream +import compat.lazyList._ import java.util.UUID import java.util.concurrent.TimeUnit.{DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, SECONDS} @@ -131,7 +131,7 @@ class Tests extends AnyFunSuiteLike with Discipline { checkAll("Eq[List[HasEq[Int]]]", EqTests[List[HasEq[Int]]].eqv) checkAll("Eq[Option[HasEq[Int]]]", EqTests[Option[HasEq[Int]]].eqv) checkAll("Eq[Vector[HasEq[Int]]]", EqTests[Vector[HasEq[Int]]].eqv) - checkAll("Eq[Stream[HasEq[Int]]]", EqTests[Stream[HasEq[Int]]].eqv) + checkAll("Eq[Stream[HasEq[Int]]]", EqTests[LazyList[HasEq[Int]]].eqv) checkAll("Eq[Queue[HasEq[Int]]]", EqTests[Queue[HasEq[Int]]].eqv) checkAll("PartialOrder[Set[Int]]", PartialOrderTests[Set[Int]].partialOrder) @@ -144,7 +144,7 @@ class Tests extends AnyFunSuiteLike with Discipline { checkAll("PartialOrder[Option[HasPartialOrder[Int]]]", PartialOrderTests[Option[HasPartialOrder[Int]]].partialOrder) checkAll("PartialOrder[List[HasPartialOrder[Int]]]", PartialOrderTests[List[HasPartialOrder[Int]]].partialOrder) checkAll("PartialOrder[Vector[HasPartialOrder[Int]]]", PartialOrderTests[Vector[HasPartialOrder[Int]]].partialOrder) - checkAll("PartialOrder[Stream[HasPartialOrder[Int]]]", PartialOrderTests[Stream[HasPartialOrder[Int]]].partialOrder) + checkAll("PartialOrder[Stream[HasPartialOrder[Int]]]", PartialOrderTests[LazyList[HasPartialOrder[Int]]].partialOrder) checkAll("PartialOrder[Queue[HasPartialOrder[Int]]]", PartialOrderTests[Queue[HasPartialOrder[Int]]].partialOrder) checkAll("Semilattice.asMeetPartialOrder[Set[Int]]", PartialOrderTests(Semilattice.asMeetPartialOrder[Set[Int]]).partialOrder) @@ -169,7 +169,7 @@ class Tests extends AnyFunSuiteLike with Discipline { checkAll("Order[Option[String]]", OrderTests[Option[String]].order) checkAll("Order[List[String]", OrderTests[List[String]].order) checkAll("Order[Vector[Int]]", OrderTests[Vector[Int]].order) - checkAll("Order[Stream[Int]]", OrderTests[Stream[Int]].order) + checkAll("Order[Stream[Int]]", OrderTests[LazyList[Int]].order) checkAll("Order[Queue[Int]]", OrderTests[Queue[Int]].order) checkAll("fromOrdering[Int]", OrderTests(Order.fromOrdering[Int]).order) checkAll("Order.reverse(Order[Int])", OrderTests(Order.reverse(Order[Int])).order) @@ -186,8 +186,8 @@ class Tests extends AnyFunSuiteLike with Discipline { checkAll("Monoid[List[Int]]", SerializableTests.serializable(Monoid[List[Int]])) checkAll("Monoid[Vector[Int]]", MonoidTests[Vector[Int]].monoid) checkAll("Monoid[Vector[Int]]", SerializableTests.serializable(Monoid[Vector[Int]])) - checkAll("Monoid[Stream[Int]]", MonoidTests[Stream[Int]].monoid) - checkAll("Monoid[Stream[Int]]", SerializableTests.serializable(Monoid[Stream[Int]])) + checkAll("Monoid[Stream[Int]]", MonoidTests[LazyList[Int]].monoid) + checkAll("Monoid[Stream[Int]]", SerializableTests.serializable(Monoid[LazyList[Int]])) checkAll("Monoid[List[String]]", MonoidTests[List[String]].monoid) checkAll("Monoid[List[String]]", SerializableTests.serializable(Monoid[List[String]])) checkAll("Monoid[Map[String, String]]", MonoidTests[Map[String, String]].monoid) @@ -245,7 +245,7 @@ class Tests extends AnyFunSuiteLike with Discipline { checkAll("Hash[Option[String]]", HashTests[Option[String]].hash) checkAll("Hash[List[String]]", HashTests[List[String]].hash) checkAll("Hash[Vector[Int]]", HashTests[Vector[Int]].hash) - checkAll("Hash[Stream[Int]]", HashTests[Stream[Int]].hash) + checkAll("Hash[Stream[Int]]", HashTests[LazyList[Int]].hash) checkAll("Hash[Set[Int]]", HashTests[Set[Int]].hash) checkAll("Hash[(Int, String)]", HashTests[(Int, String)].hash) checkAll("Hash[Either[Int, String]]", HashTests[Either[Int, String]].hash) diff --git a/kernel/src/main/scala-2.12-/cats/kernel/compat/lazyList.scala b/kernel/src/main/scala-2.12-/cats/kernel/compat/lazyList.scala new file mode 100644 index 0000000000..59c263f43b --- /dev/null +++ b/kernel/src/main/scala-2.12-/cats/kernel/compat/lazyList.scala @@ -0,0 +1,6 @@ +package cats.kernel.compat + +object lazyList { + type LazyList[+A] = Stream[A] + val LazyList = Stream +} diff --git a/kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala b/kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala deleted file mode 100644 index 00f46bc94a..0000000000 --- a/kernel/src/main/scala-2.12-/cats/kernel/compat/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package cats.kernel - -package object compat { - type Stream[+A] = scala.Stream[A] -} diff --git a/kernel/src/main/scala-2.13+/cats/kernel/compat/lazyList.scala b/kernel/src/main/scala-2.13+/cats/kernel/compat/lazyList.scala new file mode 100644 index 0000000000..5b8fcead5f --- /dev/null +++ b/kernel/src/main/scala-2.13+/cats/kernel/compat/lazyList.scala @@ -0,0 +1,9 @@ +package cats +package kernel + +package compat + +object lazyList { + type LazyList[+A] = scala.LazyList[A] //this is needed only to avoid unused import warnings on Scala 2.13 + val LazyList = scala.LazyList +} diff --git a/kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala b/kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala deleted file mode 100644 index 087e04d649..0000000000 --- a/kernel/src/main/scala-2.13+/cats/kernel/compat/package.scala +++ /dev/null @@ -1,8 +0,0 @@ -package cats -package kernel - -package object compat { - type Stream[+A] = scala.LazyList[A] - - val Stream = LazyList -} diff --git a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala index c683f6f3ac..ff21b0ffbd 100644 --- a/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala +++ b/kernel/src/main/scala/cats/kernel/instances/StreamInstances.scala @@ -1,56 +1,55 @@ package cats.kernel package instances -import compat.Stream +import compat.lazyList._ trait StreamInstances extends StreamInstances1 { - implicit def catsKernelStdOrderForStream[A: Order]: Order[Stream[A]] = + implicit def catsKernelStdOrderForStream[A: Order]: Order[LazyList[A]] = new StreamOrder[A] - implicit def catsKernelStdMonoidForStream[A]: Monoid[Stream[A]] = + implicit def catsKernelStdMonoidForStream[A]: Monoid[LazyList[A]] = new StreamMonoid[A] } trait StreamInstances1 extends StreamInstances2 { - implicit def catsKernelStdPartialOrderForStream[A: PartialOrder]: PartialOrder[Stream[A]] = + implicit def catsKernelStdPartialOrderForStream[A: PartialOrder]: PartialOrder[LazyList[A]] = new StreamPartialOrder[A] - implicit def catsKernelStdHashForStream[A: Hash]: Hash[Stream[A]] = + implicit def catsKernelStdHashForStream[A: Hash]: Hash[LazyList[A]] = new StreamHash[A] } trait StreamInstances2 { - implicit def catsKernelStdEqForStream[A: Eq]: Eq[Stream[A]] = + implicit def catsKernelStdEqForStream[A: Eq]: Eq[LazyList[A]] = new StreamEq[A] } -class StreamOrder[A](implicit ev: Order[A]) extends Order[Stream[A]] { - def compare(xs: Stream[A], ys: Stream[A]): Int = +class StreamOrder[A](implicit ev: Order[A]) extends Order[LazyList[A]] { + def compare(xs: LazyList[A], ys: LazyList[A]): Int = if (xs eq ys) 0 else StaticMethods.iteratorCompare(xs.iterator, ys.iterator) } -class StreamPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Stream[A]] { - def partialCompare(xs: Stream[A], ys: Stream[A]): Double = +class StreamPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[LazyList[A]] { + def partialCompare(xs: LazyList[A], ys: LazyList[A]): Double = if (xs eq ys) 0.0 else StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) } -class StreamHash[A](implicit ev: Hash[A]) extends StreamEq[A]()(ev) with Hash[Stream[A]] { - def hash(xs: Stream[A]): Int = StaticMethods.orderedHash(xs) +class StreamHash[A](implicit ev: Hash[A]) extends StreamEq[A]()(ev) with Hash[LazyList[A]] { + def hash(xs: LazyList[A]): Int = StaticMethods.orderedHash(xs) } -class StreamEq[A](implicit ev: Eq[A]) extends Eq[Stream[A]] { - def eqv(xs: Stream[A], ys: Stream[A]): Boolean = +class StreamEq[A](implicit ev: Eq[A]) extends Eq[LazyList[A]] { + def eqv(xs: LazyList[A], ys: LazyList[A]): Boolean = if (xs eq ys) true else StaticMethods.iteratorEq(xs.iterator, ys.iterator) } -class StreamMonoid[A] extends Monoid[Stream[A]] { - def empty: Stream[A] = Stream.empty - def combine(x: Stream[A], y: Stream[A]): Stream[A] = x ++ y +class StreamMonoid[A] extends Monoid[LazyList[A]] { + def empty: LazyList[A] = LazyList.empty + def combine(x: LazyList[A], y: LazyList[A]): LazyList[A] = x ++ y + override def combineN(x: LazyList[A], n: Int): LazyList[A] = + StaticMethods.combineNIterable(LazyList.newBuilder[A], x, n) - override def combineN(x: Stream[A], n: Int): Stream[A] = - StaticMethods.combineNIterable(Stream.newBuilder[A], x, n) - - override def combineAll(xs: TraversableOnce[Stream[A]]): Stream[A] = - StaticMethods.combineAllIterable(Stream.newBuilder[A], xs) + override def combineAll(xs: TraversableOnce[LazyList[A]]): LazyList[A] = + StaticMethods.combineAllIterable(LazyList.newBuilder[A], xs) } diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index 134c531547..c2fd56ec3c 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -1,7 +1,7 @@ package cats package laws package discipline -import kernel.compat.Stream +import kernel.compat.lazyList._ import cats.data.NonEmptyList.ZipNonEmptyList import cats.data.NonEmptyVector.ZipNonEmptyVector import scala.util.{Failure, Success, Try} @@ -68,7 +68,7 @@ object arbitrary extends ArbitraryInstances0 { Arbitrary(implicitly[Arbitrary[List[A]]].arbitrary.map(v => new ZipList(v))) implicit def catsLawsArbitraryForZipStream[A](implicit A: Arbitrary[A]): Arbitrary[ZipStream[A]] = - Arbitrary(implicitly[Arbitrary[Stream[A]]].arbitrary.map(v => new ZipStream(v))) + Arbitrary(implicitly[Arbitrary[LazyList[A]]].arbitrary.map(v => new ZipStream(v))) implicit def catsLawsArbitraryForZipNonEmptyVector[A](implicit A: Arbitrary[A]): Arbitrary[ZipNonEmptyVector[A]] = Arbitrary(implicitly[Arbitrary[NonEmptyVector[A]]].arbitrary.map(nev => new ZipNonEmptyVector(nev))) diff --git a/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala b/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala index 9e3d4b4588..908bb789de 100644 --- a/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala +++ b/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala @@ -8,7 +8,7 @@ import cats.implicits._ import cats.Eq import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests} import org.scalacheck.{Arbitrary, Gen} -import kernel.compat.Stream +import kernel.compat.lazyList._ object BinCodecInvariantMonoidalSuite { @@ -124,7 +124,7 @@ object BinCodecInvariantMonoidalSuite { bitCount <- Gen.oneOf(1, 2, 3) shuffleSeed <- Gen.choose(Long.MinValue, Long.MaxValue) } yield { - val binValues: Stream[Bin] = Stream(false, true).replicateA(bitCount).map(MiniList.unsafe(_)) + val binValues: LazyList[Bin] = LazyList(false, true).replicateA(bitCount).map(MiniList.unsafe(_)) val pairs: List[(A, Bin)] = new scala.util.Random(seed = shuffleSeed).shuffle(exA.allValues).toList.zip(binValues) val aToBin: Map[A, Bin] = pairs.toMap val binToA: Map[Bin, A] = pairs.map(_.swap).toMap diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index 4694b76c3d..a1ef5ef1fe 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -3,12 +3,12 @@ package tests import org.scalacheck.Arbitrary import scala.util.Try -import scala.collection.immutable._ +import scala.collection.immutable.{SortedSet, SortedMap} import cats.instances.all._ import cats.data._ import cats.laws.discipline.arbitrary._ -import kernel.compat.Stream -import compat.StreamOps.toStream +import kernel.compat.lazyList._ +import compat.lazyList.toLazyList abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit ArbFInt: Arbitrary[F[Int]], ArbFString: Arbitrary[F[String]]) @@ -338,7 +338,7 @@ class FoldableSuiteAdditional extends CatsSuite { } test("Foldable[Stream].foldM stack safety") { - checkMonadicFoldsStackSafety[Stream](toStream) + checkMonadicFoldsStackSafety[LazyList](toLazyList) } test("Foldable[Vector].foldM/existsM/forallM/findM/collectFirstSomeM stack safety") { @@ -367,10 +367,12 @@ class FoldableSuiteAdditional extends CatsSuite { checkMonadicFoldsStackSafety[NonEmptyStream](xs => NonEmptyStream(xs.head, xs.tail: _*)) } - val F = Foldable[Stream] + val F = Foldable[LazyList] def bomb[A]: A = sys.error("boom") - val dangerous = 0 #:: 1 #:: 2 #:: bomb[Int] #:: Stream.empty - + val dangerous = 0 #:: 1 #:: 2 #:: bomb[Int] #:: LazyList.empty + def boom[A]: LazyList[A] = { + bomb[A] #:: LazyList.empty + } test("Foldable[Stream] doesn't blow up") { // doesn't blow up - this also ensures it works for infinite streams. @@ -378,74 +380,71 @@ class FoldableSuiteAdditional extends CatsSuite { } test("lazy results don't blow up unless you call .value on them") { - val doom: Eval[Boolean] = contains(dangerous, -1) + contains(dangerous, -1) } test("Lazy[B] param to foldRight is actually being handled lazily") { // ensure that the . it only needs to be evaluated if we reach the // "end" of the fold. val trap = Eval.later(bomb[Boolean]) - val result = F.foldRight(1 #:: 2 #:: Stream.empty, trap) { (n, lb) => + val result = F.foldRight(1 #:: 2 #:: LazyList.empty, trap) { (n, lb) => if (n == 2) Now(true) else lb } assert(result.value) } test("trampolining") { - val large = Stream((1 to 10000): _*) + val large = LazyList((1 to 10000): _*) assert(contains(large, 10000).value) } test("laziness of foldM"){ dangerous.foldM(0)((acc, a) => if (a < 2) Some(acc + a) else None) should ===(None) - } - def foldableLazyListWithDefaultImpl = new Foldable[Stream] { - def foldLeft[A, B](fa: Stream[A], b: B)(f: (B, A) => B): B = + def foldableLazyListWithDefaultImpl = new Foldable[LazyList] { + def foldLeft[A, B](fa: LazyList[A], b: B)(f: (B, A) => B): B = instances.lazyList.catsStdInstancesForLazyList.foldLeft(fa, b)(f) - def foldRight[A, B](fa: Stream[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + def foldRight[A, B](fa: LazyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = instances.lazyList.catsStdInstancesForLazyList.foldRight(fa, lb)(f) } test(".foldLeftM short-circuiting") { implicit val F = foldableLazyListWithDefaultImpl - val ns = Stream.continually(1) + val ns = LazyList.continually(1) val res = F.foldLeftM[Either[Int, ?], Int, Int](ns, 0) { (sum, n) => if (sum >= 100000) Left(sum) else Right(sum + n) } assert(res == Left(100000)) } + test(".foldLeftM short-circuiting optimality") { implicit val F = foldableLazyListWithDefaultImpl // test that no more elements are evaluated than absolutely necessary - def concatUntil(ss: Stream[String], stop: String): Either[String, String] = + def concatUntil(ss: LazyList[String], stop: String): Either[String, String] = F.foldLeftM[Either[String, ?], String, String](ss, "") { (acc, s) => if (s == stop) Left(acc) else Right(acc + s) } - def boom: Stream[String] = sys.error("boom") #:: Stream.empty - assert(concatUntil("STOP" #:: boom, "STOP") == Left("")) - assert(concatUntil("Zero" #:: "STOP" #:: boom, "STOP") == Left("Zero")) - assert(concatUntil("Zero" #:: "One" #:: "STOP" #:: boom, "STOP") == Left("ZeroOne")) + assert(concatUntil("STOP" #:: boom[String], "STOP") == Left("")) + assert(concatUntil("Zero" #:: "STOP" #:: boom[String], "STOP") == Left("Zero")) + assert(concatUntil("Zero" #:: "One" #:: "STOP" #:: boom[String], "STOP") == Left("ZeroOne")) } test(".existsM/.forallM short-circuiting") { implicit val F = foldableLazyListWithDefaultImpl - def boom: Stream[Boolean] = sys.error("boom") #:: Stream.empty - assert(F.existsM[Id, Boolean](true #:: boom)(identity) == true) - assert(F.forallM[Id, Boolean](false #:: boom)(identity) == false) + assert(F.existsM[Id, Boolean](true #:: boom[Boolean])(identity) == true) + assert(F.forallM[Id, Boolean](false #:: boom[Boolean])(identity) == false) } test(".findM/.collectFirstSomeM short-circuiting") { implicit val F = foldableLazyListWithDefaultImpl - def boom: Stream[Int] = sys.error("boom") #:: Stream.empty - assert((1 #:: boom).findM[Id](_ > 0) == Some(1)) - assert((1 #:: boom).collectFirstSomeM[Id, Int](Option.apply) == Some(1)) + assert((1 #:: boom[Int]).findM[Id](_ > 0) == Some(1)) + assert((1 #:: boom[Int]).collectFirstSomeM[Id, Int](Option.apply) == Some(1)) } test("Foldable[List] doesn't break substitution") { @@ -467,8 +466,8 @@ class FoldableSortedSetSuite extends FoldableSuite[SortedSet]("sortedSet") { def iterator[T](set: SortedSet[T]): Iterator[T] = set.iterator } -class FoldableStreamSuite extends FoldableSuite[Stream]("stream") { - def iterator[T](stream: Stream[T]): Iterator[T] = stream.iterator +class FoldableStreamSuite extends FoldableSuite[LazyList]("stream") { + def iterator[T](stream: LazyList[T]): Iterator[T] = stream.iterator } class FoldableSortedMapSuite extends FoldableSuite[SortedMap[Int, ?]]("sortedMap") { diff --git a/tests/src/test/scala/cats/tests/LazyListSuite.scala b/tests/src/test/scala/cats/tests/LazyListSuite.scala new file mode 100644 index 0000000000..e4f7e35e7f --- /dev/null +++ b/tests/src/test/scala/cats/tests/LazyListSuite.scala @@ -0,0 +1,56 @@ +package cats +package tests + +import cats.laws.discipline.{AlternativeTests, CoflatMapTests, CommutativeApplyTests, MonadTests, SemigroupalTests, SerializableTests, TraverseFilterTests, TraverseTests} +import cats.data.ZipStream +import cats.laws.discipline.arbitrary._ +import kernel.compat.lazyList._ +import compat.lazyList.lazyListString + + +class LazyListSuite extends CatsSuite { + checkAll("LazyList[Int]", SemigroupalTests[LazyList].semigroupal[Int, Int, Int]) + checkAll("Semigroupal[LazyList]", SerializableTests.serializable(Semigroupal[LazyList])) + + checkAll("LazyList[Int]", CoflatMapTests[LazyList].coflatMap[Int, Int, Int]) + checkAll("CoflatMap[LazyList]", SerializableTests.serializable(CoflatMap[LazyList])) + + checkAll("LazyList[Int]", AlternativeTests[LazyList].alternative[Int, Int, Int]) + checkAll("Alternative[LazyList]", SerializableTests.serializable(Alternative[LazyList])) + + + checkAll("LazyList[Int]", MonadTests[LazyList].monad[Int, Int, Int]) + checkAll("Monad[LazyList]", SerializableTests.serializable(Monad[LazyList])) + + checkAll("LazyList[Int] with Option", TraverseTests[LazyList].traverse[Int, Int, Int, Set[Int], Option, Option]) + checkAll("Traverse[LazyList]", SerializableTests.serializable(Traverse[LazyList])) + + checkAll("LazyList[Int]", TraverseFilterTests[LazyList].traverseFilter[Int, Int, Int]) + checkAll("TraverseFilter[LazyList]", SerializableTests.serializable(TraverseFilter[LazyList])) + + // Can't test applicative laws as they don't terminate + checkAll("ZipStream[Int]", CommutativeApplyTests[ZipStream].apply[Int, Int, Int]) + + test("show") { + LazyList(1, 2, 3).show should ===(s"$lazyListString(1, ?)") + LazyList.empty[Int].show should ===(s"$lazyListString()") + } + + test("Show[Stream] is referentially transparent, unlike Stream.toString") { + forAll { lazyList: LazyList[Int] => + if (!lazyList.isEmpty) { + val unevaluatedLL = lazyList.map(identity) + val initialShow = unevaluatedLL.show + + // Evaluating the tail can cause Stream.toString to return different values, + // depending on the internal state of the Stream. Show[Stream] should return + // consistent values independent of internal state. + unevaluatedLL.tail + initialShow should ===(unevaluatedLL.show) + } else { + lazyList.show should ===(lazyList.toString) + } + } + } + +} diff --git a/tests/src/test/scala/cats/tests/OneAndSuite.scala b/tests/src/test/scala/cats/tests/OneAndSuite.scala index 05c8468319..d5bccdfe19 100644 --- a/tests/src/test/scala/cats/tests/OneAndSuite.scala +++ b/tests/src/test/scala/cats/tests/OneAndSuite.scala @@ -19,18 +19,18 @@ import cats.laws.discipline.{ TraverseTests } import cats.laws.discipline.arbitrary._ -import kernel.compat.Stream -import compat.StreamOps.toStream +import kernel.compat.lazyList._ +import compat.lazyList.toLazyList class OneAndSuite extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit implicit override val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) - checkAll("OneAnd[Stream, Int]", EqTests[OneAnd[Stream, Int]].eqv) + checkAll("OneAnd[Stream, Int]", EqTests[OneAnd[LazyList, Int]].eqv) checkAll("OneAnd[Stream, Int] with Option", - NonEmptyTraverseTests[OneAnd[Stream, ?]].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) - checkAll("NonEmptyTraverse[OneAnd[Stream, A]]", SerializableTests.serializable(NonEmptyTraverse[OneAnd[Stream, ?]])) + NonEmptyTraverseTests[OneAnd[LazyList, ?]].nonEmptyTraverse[Option, Int, Int, Int, Int, Option, Option]) + checkAll("NonEmptyTraverse[OneAnd[Stream, A]]", SerializableTests.serializable(NonEmptyTraverse[OneAnd[LazyList, ?]])) { implicit val traverse = OneAnd.catsDataTraverseForOneAnd(ListWrapper.traverse) @@ -39,8 +39,8 @@ class OneAndSuite extends CatsSuite { checkAll("Traverse[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Traverse[OneAnd[ListWrapper, ?]])) } - checkAll("OneAnd[Stream, Int]", ReducibleTests[OneAnd[Stream, ?]].reducible[Option, Int, Int]) - checkAll("Reducible[OneAnd[Stream, ?]]", SerializableTests.serializable(Reducible[OneAnd[Stream, ?]])) + checkAll("OneAnd[Stream, Int]", ReducibleTests[OneAnd[LazyList, ?]].reducible[Option, Int, Int]) + checkAll("Reducible[OneAnd[Stream, ?]]", SerializableTests.serializable(Reducible[OneAnd[LazyList, ?]])) implicit val iso = SemigroupalTests.Isomorphisms .invariant[OneAnd[ListWrapper, ?]](OneAnd.catsDataFunctorForOneAnd(ListWrapper.functor)) @@ -68,9 +68,9 @@ class OneAndSuite extends CatsSuite { { implicit val alternative = ListWrapper.alternative checkAll("OneAnd[ListWrapper, Int]", SemigroupKTests[OneAnd[ListWrapper, ?]].semigroupK[Int]) - checkAll("OneAnd[Stream, Int]", SemigroupTests[OneAnd[Stream, Int]].semigroup) + checkAll("OneAnd[Stream, Int]", SemigroupTests[OneAnd[LazyList, Int]].semigroup) checkAll("SemigroupK[OneAnd[ListWrapper, A]]", SerializableTests.serializable(SemigroupK[OneAnd[ListWrapper, ?]])) - checkAll("Semigroup[NonEmptyStream[Int]]", SerializableTests.serializable(Semigroup[OneAnd[Stream, Int]])) + checkAll("Semigroup[NonEmptyStream[Int]]", SerializableTests.serializable(Semigroup[OneAnd[LazyList, Int]])) } { @@ -86,9 +86,9 @@ class OneAndSuite extends CatsSuite { implicitly[Comonad[NonEmptyStream]] } - implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[Stream, ?]] + implicit val iso2 = SemigroupalTests.Isomorphisms.invariant[OneAnd[LazyList, ?]] - //OneAnd's tailRecM fails on LazyList due to the fact that. todo: replace NonEmptyStream with NonEmptyLazyList using newtype + //OneAnd's tailRecM fails on LazyList due to the fact that. todo: replace NonEmptyStream with NonEmptyLazyList using newtype https://github.com/typelevel/cats/issues/2903 checkAll("NonEmptyStream[Int]", MonadTests[NonEmptyStream].stackUnsafeMonad[Int, Int, Int]) checkAll("Monad[NonEmptyStream[A]]", SerializableTests.serializable(Monad[NonEmptyStream])) @@ -112,11 +112,11 @@ class OneAndSuite extends CatsSuite { test("Show is formatted correctly") { val oneAnd = NonEmptyStream("Test") - oneAnd.show should ===(s"OneAnd(Test, ${compat.StreamOps.streamString}())") + oneAnd.show should ===(s"OneAnd(Test, ${compat.lazyList.lazyListString}())") } test("Creating OneAnd + unwrap is identity") { - forAll { (i: Int, tail: Stream[Int]) => + forAll { (i: Int, tail: LazyList[Int]) => val stream = i #:: tail val oneAnd = NonEmptyStream(i, tail: _*) stream should ===(oneAnd.unwrap) @@ -226,6 +226,6 @@ class ReducibleNonEmptyStreamSuite extends ReducibleSuite[NonEmptyStream]("NonEm // if we inline this we get a bewildering implicit numeric widening // error message in Scala 2.10 val tailStart: Long = start + 1L - NonEmptyStream(start, toStream(tailStart.to(endInclusive))) + NonEmptyStream(start, toLazyList(tailStart.to(endInclusive))) } } diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index 3c12235048..d8c0b4194f 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -11,7 +11,7 @@ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ import org.typelevel.discipline.scalatest.Discipline import scala.collection.immutable.SortedSet -import kernel.compat.Stream +import kernel.compat.lazyList._ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { @@ -303,7 +303,7 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { } test("ParMap over Stream should be consistent with zip") { - forAll { (as: Stream[Int], bs: Stream[Int], cs: Stream[Int]) => + forAll { (as: LazyList[Int], bs: LazyList[Int], cs: LazyList[Int]) => val zipped = as .zip(bs) .map { @@ -343,7 +343,7 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { } test("ParTupled of Stream should be consistent with ParMap of Tuple.apply") { - forAll { (fa: Stream[Int], fb: Stream[Int], fc: Stream[Int], fd: Stream[Int]) => + forAll { (fa: LazyList[Int], fb: LazyList[Int], fc: LazyList[Int], fd: LazyList[Int]) => (fa, fb, fc, fd).parTupled should ===((fa, fb, fc, fd).parMapN(Tuple4.apply)) } } @@ -361,7 +361,7 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { } test("ParTupled of Stream should be consistent with zip") { - forAll { (fa: Stream[Int], fb: Stream[Int], fc: Stream[Int], fd: Stream[Int]) => + forAll { (fa: LazyList[Int], fb: LazyList[Int], fc: LazyList[Int], fd: LazyList[Int]) => (fa, fb, fc, fd).parTupled should ===(fa.zip(fb).zip(fc).zip(fd).map { case (((a, b), c), d) => (a, b, c, d) }) } } @@ -443,7 +443,7 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest { NonEmptyParallelTests[Vector, ZipVector].nonEmptyParallel[Int, String]) checkAll("NonEmptyParallel[List, ZipList]", NonEmptyParallelTests[List, ZipList].nonEmptyParallel[Int, String]) // Can't test Parallel here, as Applicative[ZipStream].pure doesn't terminate - checkAll("Parallel[Stream, ZipStream]", NonEmptyParallelTests[Stream, ZipStream].nonEmptyParallel[Int, String]) + checkAll("Parallel[Stream, ZipStream]", NonEmptyParallelTests[LazyList, ZipStream].nonEmptyParallel[Int, String]) checkAll("NonEmptyParallel[NonEmptyVector, ZipNonEmptyVector]", NonEmptyParallelTests[NonEmptyVector, ZipNonEmptyVector].nonEmptyParallel[Int, String]) checkAll("NonEmptyParallel[NonEmptyList, ZipNonEmptyList]", diff --git a/tests/src/test/scala/cats/tests/RegressionSuite.scala b/tests/src/test/scala/cats/tests/RegressionSuite.scala index cc62c52a71..5f55a68e60 100644 --- a/tests/src/test/scala/cats/tests/RegressionSuite.scala +++ b/tests/src/test/scala/cats/tests/RegressionSuite.scala @@ -4,7 +4,7 @@ package tests import cats.data.{Const, NonEmptyList, StateT} import scala.collection.mutable import scala.collection.immutable.SortedMap -import kernel.compat.Stream +import kernel.compat.lazyList._ class RegressionSuite extends CatsSuite { @@ -114,7 +114,7 @@ class RegressionSuite extends CatsSuite { // shouldn't have ever evaluted validate(8) checkAndResetCount(3) - Stream(1, 2, 6, 8).traverse(validate) should ===(Either.left("6 is greater than 5")) + LazyList(1, 2, 6, 8).traverse(validate) should ===(Either.left("6 is greater than 5")) checkAndResetCount(3) type StringMap[A] = SortedMap[String, A] @@ -134,7 +134,7 @@ class RegressionSuite extends CatsSuite { List(1, 2, 6, 8).traverse_(validate) should ===(Either.left("6 is greater than 5")) checkAndResetCount(3) - Stream(1, 2, 6, 8).traverse_(validate) should ===(Either.left("6 is greater than 5")) + LazyList(1, 2, 6, 8).traverse_(validate) should ===(Either.left("6 is greater than 5")) checkAndResetCount(3) Vector(1, 2, 6, 8).traverse_(validate) should ===(Either.left("6 is greater than 5")) diff --git a/tests/src/test/scala/cats/tests/StreamSuite.scala b/tests/src/test/scala/cats/tests/StreamSuite.scala deleted file mode 100644 index ee2dfc0ad4..0000000000 --- a/tests/src/test/scala/cats/tests/StreamSuite.scala +++ /dev/null @@ -1,56 +0,0 @@ -package cats -package tests - -import cats.laws.discipline.{AlternativeTests, CoflatMapTests, CommutativeApplyTests, MonadTests, SemigroupalTests, SerializableTests, TraverseFilterTests, TraverseTests} -import cats.data.ZipStream -import cats.laws.discipline.arbitrary._ -import kernel.compat.Stream -import compat.StreamOps.streamString - - -class StreamSuite extends CatsSuite { - checkAll("Stream[Int]", SemigroupalTests[Stream].semigroupal[Int, Int, Int]) - checkAll("Semigroupal[Stream]", SerializableTests.serializable(Semigroupal[Stream])) - - checkAll("Stream[Int]", CoflatMapTests[Stream].coflatMap[Int, Int, Int]) - checkAll("CoflatMap[Stream]", SerializableTests.serializable(CoflatMap[Stream])) - - checkAll("Stream[Int]", AlternativeTests[Stream].alternative[Int, Int, Int]) - checkAll("Alternative[Stream]", SerializableTests.serializable(Alternative[Stream])) - - - checkAll("Stream[Int]", MonadTests[Stream].monad[Int, Int, Int]) - checkAll("Monad[Stream]", SerializableTests.serializable(Monad[Stream])) - - checkAll("Stream[Int] with Option", TraverseTests[Stream].traverse[Int, Int, Int, Set[Int], Option, Option]) - checkAll("Traverse[Stream]", SerializableTests.serializable(Traverse[Stream])) - - checkAll("Stream[Int]", TraverseFilterTests[Stream].traverseFilter[Int, Int, Int]) - checkAll("TraverseFilter[Stream]", SerializableTests.serializable(TraverseFilter[Stream])) - - // Can't test applicative laws as they don't terminate - checkAll("ZipStream[Int]", CommutativeApplyTests[ZipStream].apply[Int, Int, Int]) - - test("show") { - Stream(1, 2, 3).show should ===(s"$streamString(1, ?)") - Stream.empty[Int].show should ===(s"$streamString()") - } - - test("Show[Stream] is referentially transparent, unlike Stream.toString") { - forAll { stream: Stream[Int] => - if (!stream.isEmpty) { - val unevaluatedStream = stream.map(identity) - val initialShow = unevaluatedStream.show - - // Evaluating the tail can cause Stream.toString to return different values, - // depending on the internal state of the Stream. Show[Stream] should return - // consistent values independent of internal state. - unevaluatedStream.tail - initialShow should ===(unevaluatedStream.show) - } else { - stream.show should ===(stream.toString) - } - } - } - -} diff --git a/tests/src/test/scala/cats/tests/TraverseSuite.scala b/tests/src/test/scala/cats/tests/TraverseSuite.scala index fba9023fca..05d9784db7 100644 --- a/tests/src/test/scala/cats/tests/TraverseSuite.scala +++ b/tests/src/test/scala/cats/tests/TraverseSuite.scala @@ -4,8 +4,8 @@ package tests import org.scalacheck.Arbitrary import cats.instances.all._ -import kernel.compat.Stream -import compat.StreamOps.toStream +import kernel.compat.lazyList._ +import compat.lazyList.toLazyList abstract class TraverseSuite[F[_]: Traverse](name: String)(implicit ArbFInt: Arbitrary[F[Int]]) extends CatsSuite { @@ -49,11 +49,11 @@ object TraverseSuite { } class TraverseListSuite extends TraverseSuite[List]("List") -class TraverseStreamSuite extends TraverseSuite[Stream]("Stream") +class TraverseStreamSuite extends TraverseSuite[LazyList]("Stream") class TraverseVectorSuite extends TraverseSuite[Vector]("Vector") class TraverseListSuiteUnderlying extends TraverseSuite.Underlying[List]("List") -class TraverseStreamSuiteUnderlying extends TraverseSuite.Underlying[Stream]("Stream") +class TraverseStreamSuiteUnderlying extends TraverseSuite.Underlying[LazyList]("Stream") class TraverseVectorSuiteUnderlying extends TraverseSuite.Underlying[Vector]("Vector") class TraverseSuiteAdditional extends CatsSuite { @@ -68,7 +68,7 @@ class TraverseSuiteAdditional extends CatsSuite { } test("Traverse[Stream].zipWithIndex stack safety") { - checkZipWithIndexedStackSafety[Stream](toStream) + checkZipWithIndexedStackSafety[LazyList](toLazyList) } test("Traverse[Vector].zipWithIndex stack safety") { From 706c6290975ddee5cba0b091f167bb1199774302 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 17:36:04 -0400 Subject: [PATCH 7/9] reformat --- build.sbt | 5 ++--- .../main/scala-2.12-/cats/compat/lazyList.scala | 1 - .../main/scala-2.12-/cats/instances/stream.scala | 16 ++++++++-------- .../tests/BinCodecInvariantMonoidalSuite.scala | 1 - .../test/scala/cats/tests/FoldableSuite.scala | 8 +++----- .../test/scala/cats/tests/LazyListSuite.scala | 13 ++++++++++--- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/build.sbt b/build.sbt index 63a23e7163..8ee90fe25c 100644 --- a/build.sbt +++ b/build.sbt @@ -50,7 +50,8 @@ crossScalaVersionsFromTravis in Global := { def scalaVersionSpecificFolders(srcName: String, srcBaseDir: java.io.File, scalaVersion: String) = { def extraDirs(suffix: String) = - List(CrossType.Pure, CrossType.Full).flatMap(_.sharedSrcDir(srcBaseDir, srcName).toList.map(f => file(f.getPath + suffix))) + List(CrossType.Pure, CrossType.Full) + .flatMap(_.sharedSrcDir(srcBaseDir, srcName).toList.map(f => file(f.getPath + suffix))) CrossVersion.partialVersion(scalaVersion) match { case Some((2, y)) if y <= 12 => extraDirs("-2.12-") @@ -163,8 +164,6 @@ lazy val includeGeneratedSrc: Setting[_] = { } } - - lazy val disciplineDependencies = Seq( libraryDependencies ++= Seq("org.scalacheck" %%% "scalacheck" % scalaCheckVersion, "org.typelevel" %%% "discipline-core" % disciplineVersion) diff --git a/core/src/main/scala-2.12-/cats/compat/lazyList.scala b/core/src/main/scala-2.12-/cats/compat/lazyList.scala index 7b72469239..71b7a8131d 100644 --- a/core/src/main/scala-2.12-/cats/compat/lazyList.scala +++ b/core/src/main/scala-2.12-/cats/compat/lazyList.scala @@ -2,7 +2,6 @@ package cats.compat object lazyList { - def toLazyList[A](traversableOnce: TraversableOnce[A]): Stream[A] = traversableOnce.toStream def lazyListString: String = "Stream" diff --git a/core/src/main/scala-2.12-/cats/instances/stream.scala b/core/src/main/scala-2.12-/cats/instances/stream.scala index 05a014c781..3609570884 100644 --- a/core/src/main/scala-2.12-/cats/instances/stream.scala +++ b/core/src/main/scala-2.12-/cats/instances/stream.scala @@ -13,7 +13,7 @@ trait LazyListInstances extends StreamInstances with StreamInstancesBinCompat0 { trait StreamInstances extends cats.kernel.instances.StreamInstances { implicit val catsStdInstancesForStream - : Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] = + : Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] = new Traverse[Stream] with Alternative[Stream] with Monad[Stream] with CoflatMap[Stream] { def empty[A]: Stream[A] = Stream.Empty @@ -53,9 +53,9 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { B.combineAll(fa.iterator.map(f)) def traverse[G[_], A, B](fa: Stream[A])(f: A => G[B])(implicit G: Applicative[G]): G[Stream[B]] = - // We use foldRight to avoid possible stack overflows. Since - // we don't want to return a Eval[_] instance, we call .value - // at the end. + // We use foldRight to avoid possible stack overflows. Since + // we don't want to return a Eval[_] instance, we call .value + // at the end. foldRight(fa, Always(G.pure(Stream.empty[B]))) { (a, lgsb) => G.map2Eval(f(a), lgsb)(_ #:: _) }.value @@ -179,14 +179,14 @@ trait StreamInstancesBinCompat0 { def traverseFilter[G[_], A, B](fa: Stream[A])(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Stream[B]] = fa.foldRight(Eval.now(G.pure(Stream.empty[B])))( - (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) - ) + (x, xse) => G.map2Eval(f(x), xse)((i, o) => i.fold(o)(_ +: o)) + ) .value override def filterA[G[_], A](fa: Stream[A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Stream[A]] = fa.foldRight(Eval.now(G.pure(Stream.empty[A])))( - (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) - ) + (x, xse) => G.map2Eval(f(x), xse)((b, as) => if (b) x +: as else as) + ) .value } diff --git a/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala b/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala index 908bb789de..a66c8c3c98 100644 --- a/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala +++ b/tests/src/test/scala/cats/tests/BinCodecInvariantMonoidalSuite.scala @@ -10,7 +10,6 @@ import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests} import org.scalacheck.{Arbitrary, Gen} import kernel.compat.lazyList._ - object BinCodecInvariantMonoidalSuite { final case class MiniList[+A] private (val toList: List[A]) extends AnyVal { import MiniList.truncated diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index a1ef5ef1fe..3f85ca1b96 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -3,7 +3,7 @@ package tests import org.scalacheck.Arbitrary import scala.util.Try -import scala.collection.immutable.{SortedSet, SortedMap} +import scala.collection.immutable.{SortedMap, SortedSet} import cats.instances.all._ import cats.data._ import cats.laws.discipline.arbitrary._ @@ -370,9 +370,8 @@ class FoldableSuiteAdditional extends CatsSuite { val F = Foldable[LazyList] def bomb[A]: A = sys.error("boom") val dangerous = 0 #:: 1 #:: 2 #:: bomb[Int] #:: LazyList.empty - def boom[A]: LazyList[A] = { + def boom[A]: LazyList[A] = bomb[A] #:: LazyList.empty - } test("Foldable[Stream] doesn't blow up") { // doesn't blow up - this also ensures it works for infinite streams. @@ -398,7 +397,7 @@ class FoldableSuiteAdditional extends CatsSuite { assert(contains(large, 10000).value) } - test("laziness of foldM"){ + test("laziness of foldM") { dangerous.foldM(0)((acc, a) => if (a < 2) Some(acc + a) else None) should ===(None) } @@ -419,7 +418,6 @@ class FoldableSuiteAdditional extends CatsSuite { assert(res == Left(100000)) } - test(".foldLeftM short-circuiting optimality") { implicit val F = foldableLazyListWithDefaultImpl diff --git a/tests/src/test/scala/cats/tests/LazyListSuite.scala b/tests/src/test/scala/cats/tests/LazyListSuite.scala index e4f7e35e7f..f07dbdf1e6 100644 --- a/tests/src/test/scala/cats/tests/LazyListSuite.scala +++ b/tests/src/test/scala/cats/tests/LazyListSuite.scala @@ -1,13 +1,21 @@ package cats package tests -import cats.laws.discipline.{AlternativeTests, CoflatMapTests, CommutativeApplyTests, MonadTests, SemigroupalTests, SerializableTests, TraverseFilterTests, TraverseTests} +import cats.laws.discipline.{ + AlternativeTests, + CoflatMapTests, + CommutativeApplyTests, + MonadTests, + SemigroupalTests, + SerializableTests, + TraverseFilterTests, + TraverseTests +} import cats.data.ZipStream import cats.laws.discipline.arbitrary._ import kernel.compat.lazyList._ import compat.lazyList.lazyListString - class LazyListSuite extends CatsSuite { checkAll("LazyList[Int]", SemigroupalTests[LazyList].semigroupal[Int, Int, Int]) checkAll("Semigroupal[LazyList]", SerializableTests.serializable(Semigroupal[LazyList])) @@ -18,7 +26,6 @@ class LazyListSuite extends CatsSuite { checkAll("LazyList[Int]", AlternativeTests[LazyList].alternative[Int, Int, Int]) checkAll("Alternative[LazyList]", SerializableTests.serializable(Alternative[LazyList])) - checkAll("LazyList[Int]", MonadTests[LazyList].monad[Int, Int, Int]) checkAll("Monad[LazyList]", SerializableTests.serializable(Monad[LazyList])) From 8ed73821213d2b59fe633b4f3678d4600e23e5a9 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 18:52:29 -0400 Subject: [PATCH 8/9] more stream => LazyList --- .../test/scala/cats/tests/FoldableSuite.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/src/test/scala/cats/tests/FoldableSuite.scala b/tests/src/test/scala/cats/tests/FoldableSuite.scala index 3f85ca1b96..9eeb216727 100644 --- a/tests/src/test/scala/cats/tests/FoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/FoldableSuite.scala @@ -337,7 +337,7 @@ class FoldableSuiteAdditional extends CatsSuite { checkMonadicFoldsStackSafety[List](_.toList) } - test("Foldable[Stream].foldM stack safety") { + test("Foldable[LazyList].foldM stack safety") { checkMonadicFoldsStackSafety[LazyList](toLazyList) } @@ -372,17 +372,17 @@ class FoldableSuiteAdditional extends CatsSuite { val dangerous = 0 #:: 1 #:: 2 #:: bomb[Int] #:: LazyList.empty def boom[A]: LazyList[A] = bomb[A] #:: LazyList.empty - test("Foldable[Stream] doesn't blow up") { + test("Foldable[LazyList] doesn't blow up") { // doesn't blow up - this also ensures it works for infinite streams. assert(contains(dangerous, 2).value) } - test("lazy results don't blow up unless you call .value on them") { + test("Foldable[LazyList] lazy results don't blow up unless you call .value on them") { contains(dangerous, -1) } - test("Lazy[B] param to foldRight is actually being handled lazily") { + test("Foldable[LazyList] param to foldRight is actually being handled lazily") { // ensure that the . it only needs to be evaluated if we reach the // "end" of the fold. val trap = Eval.later(bomb[Boolean]) @@ -392,12 +392,12 @@ class FoldableSuiteAdditional extends CatsSuite { assert(result.value) } - test("trampolining") { + test("Foldable[LazyList] trampolining") { val large = LazyList((1 to 10000): _*) assert(contains(large, 10000).value) } - test("laziness of foldM") { + test("Foldable[LazyList] laziness of foldM") { dangerous.foldM(0)((acc, a) => if (a < 2) Some(acc + a) else None) should ===(None) } @@ -464,8 +464,8 @@ class FoldableSortedSetSuite extends FoldableSuite[SortedSet]("sortedSet") { def iterator[T](set: SortedSet[T]): Iterator[T] = set.iterator } -class FoldableStreamSuite extends FoldableSuite[LazyList]("stream") { - def iterator[T](stream: LazyList[T]): Iterator[T] = stream.iterator +class FoldableLazyListSuite extends FoldableSuite[LazyList]("lazyList") { + def iterator[T](list: LazyList[T]): Iterator[T] = list.iterator } class FoldableSortedMapSuite extends FoldableSuite[SortedMap[Int, ?]]("sortedMap") { From 9f6fff6143a044a3b5115414c19fda835af9f647 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 20 Jun 2019 19:19:57 -0400 Subject: [PATCH 9/9] fix format --- core/src/main/scala-2.12-/cats/instances/stream.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala-2.12-/cats/instances/stream.scala b/core/src/main/scala-2.12-/cats/instances/stream.scala index 3609570884..83c4f9b18d 100644 --- a/core/src/main/scala-2.12-/cats/instances/stream.scala +++ b/core/src/main/scala-2.12-/cats/instances/stream.scala @@ -5,7 +5,9 @@ import cats.syntax.show._ import scala.annotation.tailrec -//For cross compile with backward compatibility +/** + * For cross compile with backward compatibility + */ trait LazyListInstances extends StreamInstances with StreamInstancesBinCompat0 { val catsStdInstancesForLazyList = catsStdInstancesForStream }