Skip to content

Commit

Permalink
adding get to Foldable
Browse files Browse the repository at this point in the history
  • Loading branch information
yilinwei committed May 13, 2017
1 parent 5340ee6 commit b500bf1
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 3 deletions.
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cats

import scala.collection.mutable
import cats.instances.either._
import cats.instances.long._
import simulacrum.typeclass

Expand Down Expand Up @@ -151,6 +152,19 @@ import simulacrum.typeclass
*/
def size[A](fa: F[A]): Long = foldMap(fa)(_ => 1)

/**
* Get the element at the index of the `Foldable`.
*/
def get[A](fa: F[A])(idx: Long): Option[A] =
if (idx < 0) None
else
foldM[Either[A, ?], A, Int](fa, 0) { (i, a) =>
if (i == idx) Left(a) else Right(i + 1)
} match {
case Left(a) => Some(a)
case Right(_) => None
}

/**
* Fold implemented using the given Monoid[A] instance.
*/
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0
override def toList[A](fa: NonEmptyList[A]): List[A] = fa.toList

override def toNonEmptyList[A](fa: NonEmptyList[A]): NonEmptyList[A] = fa

override def get[A](fa: NonEmptyList[A])(idx: Long): Option[A] =
if (idx == 0) Some(fa.head) else Foldable[List].get(fa.tail)(idx - 1)
}

implicit def catsDataShowForNonEmptyList[A](implicit A: Show[A]): Show[NonEmptyList[A]] =
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ private[data] sealed trait NonEmptyVectorInstances {
override def foldRight[A, B](fa: NonEmptyVector[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fa.foldRight(lb)(f)

override def get[A](fa: NonEmptyVector[A])(idx: Long): Option[A] =
if (idx < Int.MaxValue) fa.get(idx.toInt) else None

def tailRecM[A, B](a: A)(f: A => NonEmptyVector[Either[A, B]]): NonEmptyVector[B] = {
val buf = new VectorBuilder[B]
@tailrec def go(v: NonEmptyVector[Either[A, B]]): Unit = v.head match {
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ trait ListInstances extends cats.kernel.instances.ListInstances {
G.map2Eval(f(a), lglb)(_ :: _)
}.value

@tailrec
override def get[A](fa: List[A])(idx: Long): Option[A] =
fa match {
case Nil => None
case h :: tail =>
if (idx < 0) None
else if (idx == 0) Some(h)
else get(tail)(idx - 1)
}

override def exists[A](fa: List[A])(p: A => Boolean): Boolean =
fa.exists(p)

Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ 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 (idx < Int.MaxValue && 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)(_ +: _)
Expand Down
12 changes: 9 additions & 3 deletions tests/src/test/scala/cats/tests/FoldableTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ abstract class FoldableCheck[F[_]: Foldable](name: String)(implicit ArbFInt: Arb

def iterator[T](fa: F[T]): Iterator[T]

test(s"Foldable[$name].size") {
forAll { (fa: F[Int]) =>
fa.size should === (iterator(fa).size.toLong)
test(s"Foldable[$name].size/get") {
forAll { (fa: F[Int], n: Int) =>
val s = fa.size
s should === (iterator(fa).size.toLong)
if (n < s && n >= 0) {
fa.get(n.toLong) === Some(iterator(fa).take(n + 1).toList.last)
} else {
fa.get(n.toLong) === None
}
}
}

Expand Down

0 comments on commit b500bf1

Please sign in to comment.