diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 08c5f810a4..1a4c2fc4cb 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -1,5 +1,7 @@ package cats +import cats.data.Streaming + import scala.collection.mutable import simulacrum.typeclass @@ -206,6 +208,11 @@ import simulacrum.typeclass val F = self val G = ev } + + def toStreaming[A](fa: F[A]): Streaming[A] = + foldRight(fa, Now(Streaming.empty[A])){ (a, ls) => + Now(Streaming.cons(a, ls)) + }.value } /** diff --git a/core/src/main/scala/cats/data/Streaming.scala b/core/src/main/scala/cats/data/Streaming.scala index 7193e2ca7f..4a3598073a 100644 --- a/core/src/main/scala/cats/data/Streaming.scala +++ b/core/src/main/scala/cats/data/Streaming.scala @@ -764,6 +764,9 @@ object Streaming extends StreamingInstances { loop(Empty(), as.reverse) } + def fromFoldable[F[_], A](fa: F[A])(implicit F: Foldable[F]): Streaming[A] = + F.toStreaming(fa) + /** * Create a stream from an iterable. * @@ -919,6 +922,9 @@ trait StreamingInstances extends StreamingInstances1 { override def isEmpty[A](fa: Streaming[A]): Boolean = fa.isEmpty + + override def toStreaming[A](fa: Streaming[A]): Streaming[A] = + fa } implicit def streamOrder[A: Order]: Order[Streaming[A]] = diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index 2096817371..5eb190eb54 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -4,6 +4,7 @@ package std import algebra.Eq import algebra.std.{ListMonoid, ListOrder} +import cats.data.Streaming import cats.syntax.order._ import scala.annotation.tailrec @@ -62,6 +63,9 @@ trait ListInstances extends ListInstances1 { fa.forall(p) override def isEmpty[A](fa: List[A]): Boolean = fa.isEmpty + + override def toStreaming[A](fa: List[A]): Streaming[A] = + Streaming.fromList(fa) } implicit def listAlgebra[A]: Monoid[List[A]] = new ListMonoid[A] diff --git a/core/src/main/scala/cats/std/vector.scala b/core/src/main/scala/cats/std/vector.scala index 9789ab2cb2..9f88565c01 100644 --- a/core/src/main/scala/cats/std/vector.scala +++ b/core/src/main/scala/cats/std/vector.scala @@ -1,6 +1,8 @@ package cats package std +import cats.data.Streaming + trait VectorInstances { implicit val vectorInstance: Traverse[Vector] with MonadCombine[Vector] = new Traverse[Vector] with MonadCombine[Vector] { @@ -36,6 +38,9 @@ trait VectorInstances { fa.exists(p) override def isEmpty[A](fa: Vector[A]): Boolean = fa.isEmpty + + override def toStreaming[A](fa: Vector[A]): Streaming[A] = + Streaming.fromVector(fa) } // TODO: eventually use algebra's instances (which will deal with diff --git a/tests/src/test/scala/cats/tests/FoldableTests.scala b/tests/src/test/scala/cats/tests/FoldableTests.scala index f23c7da9fc..59f60e57d9 100644 --- a/tests/src/test/scala/cats/tests/FoldableTests.scala +++ b/tests/src/test/scala/cats/tests/FoldableTests.scala @@ -36,6 +36,7 @@ abstract class FoldableCheck[F[_]: ArbitraryK: Foldable](name: String) extends C test("toList/isEmpty/nonEmpty") { forAll { (fa: F[Int]) => fa.toList shouldBe iterator(fa).toList + fa.toStreaming.toList shouldBe iterator(fa).toList fa.isEmpty shouldBe iterator(fa).isEmpty fa.nonEmpty shouldBe iterator(fa).nonEmpty } @@ -94,5 +95,8 @@ class FoldableTestsAdditional extends CatsSuite { if (n == 2) Now(true) else lb } assert(result.value) + + // toStreaming should be lazy + assert(dangerous.toStreaming.take(3).toList == List(0, 1, 2)) } }