From eef6979e32d5a62cfa207114481b6826494319f0 Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Thu, 6 Jul 2017 23:58:41 -0700 Subject: [PATCH] Add indexed to Traverse --- core/src/main/scala/cats/Traverse.scala | 12 ++++++ .../test/scala/cats/tests/TraverseTests.scala | 41 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/src/test/scala/cats/tests/TraverseTests.scala diff --git a/core/src/main/scala/cats/Traverse.scala b/core/src/main/scala/cats/Traverse.scala index 24015db6b96..f7fb664f2e7 100644 --- a/core/src/main/scala/cats/Traverse.scala +++ b/core/src/main/scala/cats/Traverse.scala @@ -1,5 +1,7 @@ package cats +import cats.data.State + import simulacrum.typeclass /** @@ -97,4 +99,14 @@ import simulacrum.typeclass override def map[A, B](fa: F[A])(f: A => B): F[B] = traverse[Id, A, B](fa)(f) + + /** + * Traverses through the structure F, pairing the values with + * assigned indices. + * + * The behavior is consistent with the Scala collection library's + * `zipWithIndex` for collections such as `List`. + */ + def indexed[A](fa: F[A]): F[(A, Int)] = + traverse(fa)(a => State((s: Int) => (s + 1, (a, s)))).runA(0).value } diff --git a/tests/src/test/scala/cats/tests/TraverseTests.scala b/tests/src/test/scala/cats/tests/TraverseTests.scala new file mode 100644 index 00000000000..938ae1cc548 --- /dev/null +++ b/tests/src/test/scala/cats/tests/TraverseTests.scala @@ -0,0 +1,41 @@ +package cats +package tests + +import org.scalatest.prop.PropertyChecks +import org.scalacheck.Arbitrary + +import cats.instances.all._ + +abstract class TraverseCheck[F[_]: Traverse](name: String)(implicit ArbFInt: Arbitrary[F[Int]]) extends CatsSuite with PropertyChecks { + + test("Traverse[List].indexed") { + forAll { (fa: F[Int]) => + fa.indexed.toList should === (fa.toList.zipWithIndex) + } + } + +} + +class TraverseListCheck extends TraverseCheck[List]("list") +class TraverseStreamCheck extends TraverseCheck[Stream]("stream") +class TraverseVectorCheck extends TraverseCheck[Vector]("vector") + +class TraverseTestsAdditional extends CatsSuite { + + def checkIndexedStackSafety[F[_]](fromRange: Range => F[Int])(implicit F: Traverse[F]): Unit = { + F.indexed(fromRange(1 to 500000)) + () + } + + test("Traverse[List].indexed stack safety") { + checkIndexedStackSafety[List](_.toList) + } + + test("Traverse[Stream].indexed stack safety") { + checkIndexedStackSafety[Stream](_.toStream) + } + + test("Traverse[Vector].indexed stack safety") { + checkIndexedStackSafety[Vector](_.toVector) + } +}