diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 64db5d0ece8..2e62bd57f29 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -151,6 +151,26 @@ import simulacrum.typeclass */ def size[A](fa: F[A]): Long = foldMap(fa)(_ => 1) + /** + * Get the element at the index of the `Foldable`. + * + * The default implementation of this is based on `foldLeft`, and thus will + * always fold across the entire structure. + */ + def get[A](fa: F[A])(idx: Long): Option[A] = + foldLeft(fa, (0L, Option.empty[A])) { (b, a) => + b._1 match { + case i if i == idx => (i + 1, Some(a)) + case i if i < idx => (i + 1, None) + case _ => b + } + }._2 + + /** + * Get the element at the index of the `Foldable` + */ + def apply[A](fa: F[A])(idx: Long): A = get(fa)(idx).get + /** * Fold implemented using the given Monoid[A] instance. */ diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index dfde5b10f7c..589f9dda9a0 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -73,6 +73,8 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { override def size[A](fa: Vector[A]): Long = fa.size.toLong + override def get[A](fa: Vector[A])(idx: Long): Option[A] = if (fa.size > idx && idx > 0) Some(fa(idx.toInt)) else None + override def traverse[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: Applicative[G]): G[Vector[B]] = foldRight[A, G[Vector[B]]](fa, Always(G.pure(Vector.empty))){ (a, lgvb) => G.map2Eval(f(a), lgvb)(_ +: _) diff --git a/tests/src/test/scala/cats/tests/FoldableTests.scala b/tests/src/test/scala/cats/tests/FoldableTests.scala index 381eecb4b00..006188e2934 100644 --- a/tests/src/test/scala/cats/tests/FoldableTests.scala +++ b/tests/src/test/scala/cats/tests/FoldableTests.scala @@ -10,9 +10,16 @@ abstract class FoldableCheck[F[_]: Foldable](name: String)(implicit ArbFInt: Arb def iterator[T](fa: F[T]): Iterator[T] - test("size") { - forAll { (fa: F[Int]) => - fa.size should === (iterator(fa).size.toLong) + test("size/get/apply") { + forAll { (fa: F[Int], n: Int) => + val s = fa.size + s should === (iterator(fa).size.toLong) + if(n < s && n > 0) { + fa(n.toLong) === (iterator(fa).take(n + 1).toList.last) + fa.get(n.toLong) === Some(fa(n.toLong)) + } else { + fa.get(n.toLong) === None + } } }