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/ListTests.scala b/tests/src/test/scala/cats/tests/ListTests.scala index 308e0672789..ffa5c342c09 100644 --- a/tests/src/test/scala/cats/tests/ListTests.scala +++ b/tests/src/test/scala/cats/tests/ListTests.scala @@ -42,4 +42,10 @@ class ListTests extends CatsSuite { l.show should === (l.toString) } } + + test("indexed consistent with zipWithIndex") { + forAll { (fa: List[String]) => + Traverse[List].indexed(fa) should === (fa.zipWithIndex) + } + } }