Skip to content

Commit

Permalink
Add Foldable extension collectFirstSomeM (#2366)
Browse files Browse the repository at this point in the history
* Add Foldable extension collectFirstSomeM

* Implement without OptionT

* Optimize pattern matching

* Empty commit
  • Loading branch information
catostrophe authored and kailuowang committed Aug 15, 2018
1 parent 21aa369 commit f2bc669
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 1 deletion.
28 changes: 28 additions & 0 deletions core/src/main/scala/cats/syntax/foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,32 @@ final class FoldableOps[F[_], A](val fa: F[A]) extends AnyVal {
b.toString.dropRight(delim.length)
} + suffix
}

/**
* Monadic version of `collectFirstSome`.
* {{{
* scala> import cats.implicits._
* scala> def parseInt(s: String): Either[String, Int] = Either.catchOnly[NumberFormatException](s.toInt).leftMap(_.getMessage)
* scala> val keys1 = List("1", "2", "4", "5")
* scala> val map1 = Map(4 -> "Four", 5 -> "Five")
* scala> keys1.collectFirstSomeM(parseInt(_) map map1.get)
* res1: scala.util.Either[String,Option[String]] = Right(Some(Four))
* scala> val map2 = Map(6 -> "Six", 7 -> "Seven")
* scala> keys1.collectFirstSomeM(parseInt(_) map map2.get)
* res2: scala.util.Either[String,Option[String]] = Right(None)
* scala> val keys2 = List("1", "x", "4", "5")
* scala> keys2.collectFirstSomeM(parseInt(_) map map1.get)
* res3: scala.util.Either[String,Option[String]] = Left(For input string: "x")
* scala> val keys3 = List("1", "2", "4", "x")
* scala> keys3.collectFirstSomeM(parseInt(_) map map1.get)
* res4: scala.util.Either[String,Option[String]] = Right(Some(Four))
* }}}
*/
def collectFirstSomeM[G[_], B](f: A => G[Option[B]])(implicit F: Foldable[F], G: Monad[G]): G[Option[B]] =
F.foldRight(fa, Eval.now(G.pure(Option.empty[B])))((a, lb) =>
Eval.now(G.flatMap(f(a)) {
case None => lb.value
case s => G.pure(s)
})
).value
}
10 changes: 9 additions & 1 deletion tests/src/test/scala/cats/tests/FoldableSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,20 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(
fa.toList should === (iterator(fa).toList)
}
}

test(s"Foldable[$name] mkString_") {
forAll { (fa: F[Int]) =>
fa.mkString_("L[", ";", "]") should === (fa.toList.mkString("L[", ";", "]"))
}
}

test(s"Foldable[$name].collectFirstSomeM") {
forAll { (fa: F[Int], n: Int) =>
fa.collectFirstSomeM(x => (x > n).guard[Option].as(x).asRight[String]) should === (fa.toList.collectFirst {
case x if x > n => x
}.asRight[String])
}
}
}

class FoldableSuiteAdditional extends CatsSuite {
Expand Down

0 comments on commit f2bc669

Please sign in to comment.