Skip to content

Commit

Permalink
Adding apply and get for Foldable
Browse files Browse the repository at this point in the history
  • Loading branch information
yilinwei committed Nov 14, 2016
1 parent bffd31f commit 015b927
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 3 deletions.
20 changes: 20 additions & 0 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
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 @@ -220,6 +220,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 @@ -216,6 +216,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] =
fa.get(idx.toInt)

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] =
if (idx < 0 || fa.isEmpty) {
None
} else if (idx < 0) {
get(fa.tail)(idx - 1)
} else {
Some(fa.head)
}

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

Expand Down
2 changes: 2 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,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)(_ +: _)
Expand Down
13 changes: 10 additions & 3 deletions tests/src/test/scala/cats/tests/FoldableTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}

Expand Down

0 comments on commit 015b927

Please sign in to comment.