Skip to content

Commit

Permalink
Merge pull request #1020 from peterneyens/add-traverseM
Browse files Browse the repository at this point in the history
Add Traverse.traverseM
  • Loading branch information
adelbertc committed May 8, 2016
2 parents 1837862 + 7ef5e5b commit 40a1211
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 4 deletions.
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ import simulacrum.typeclass
def traverseU[A, GB](fa: F[A])(f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] =
U.TC.traverse(fa)(a => U.subst(f(a)))(this)

/**
* A traverse followed by flattening the inner result.
*/
def traverseM[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Applicative[G], F: FlatMap[F]): G[F[B]] =
G.map(traverse(fa)(f))(F.flatten)

/**
* Thread all the G effects through the F structure to invert the
* structure from F[G[A]] to G[F[A]].
Expand Down
87 changes: 84 additions & 3 deletions core/src/main/scala/cats/syntax/traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,98 @@ trait TraverseSyntax extends TraverseSyntax1 {
}

final class TraverseOps[F[_], A](fa: F[A])(implicit F: Traverse[F]) {
def traverse[G[_]: Applicative, B](f: A => G[B]): G[F[B]] = F.traverse(fa)(f)
/**
* @see [[Traverse.traverse]]
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.std.list._
* scala> import cats.std.option._
* scala> import cats.syntax.traverse._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> List("1", "2", "3").traverse(parseInt)
* res0: Option[List[Int]] = Some(List(1, 2, 3))
* scala> List("1", "two", "3").traverse(parseInt)
* res1: Option[List[Int]] = None
* }}}
*/
def traverse[G[_]: Applicative, B](f: A => G[B]): G[F[B]] =
F.traverse(fa)(f)

/**
* @see [[Traverse.traverse]]
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.std.list._
* scala> import cats.syntax.traverse._
* scala> def parseInt(s: String): Xor[String, Int] = Xor.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number")
* scala> val ns = List("1", "2", "3")
* scala> ns.traverseU(parseInt)
* res0: Xor[String, List[Int]] = Right(List(1, 2, 3))
* scala> ns.traverse[Xor[String, ?], Int](parseInt)
* res1: Xor[String, List[Int]] = Right(List(1, 2, 3))
* }}}
*/
def traverseU[GB](f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] =
F.traverseU[A, GB](fa)(f)(U)

def sequence[G[_], B](implicit G: Applicative[G], ev: A =:= G[B]): G[F[B]] =
/**
* @see [[Traverse.traverseM]]
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.std.list._
* scala> import cats.std.option._
* scala> import cats.syntax.traverse._
* scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption
* scala> val x = Option(List("1", "two", "3"))
* scala> x.traverseM(_.map(parseInt))
* res0: List[Option[Int]] = List(Some(1), None, Some(3))
* }}}
*/
def traverseM[G[_]: Applicative, B](f: A => G[F[B]])(implicit F2: FlatMap[F]): G[F[B]] =
F.traverseM(fa)(f)

/**
* @see [[Traverse.sequence]]
*
* Example:
* {{{
* scala> import cats.std.list._
* scala> import cats.std.option._
* scala> import cats.syntax.traverse._
* scala> val x: List[Option[Int]] = List(Some(1), Some(2))
* scala> val y: List[Option[Int]] = List(None, Some(2))
* scala> x.sequence
* res0: Option[List[Int]] = Some(List(1, 2))
* scala> y.sequence
* res1: Option[List[Int]] = None
* }}}
*/
def sequence[G[_], B](implicit G: Applicative[G], ev: A =:= G[B]): G[F[B]] =
F.sequence(fa.asInstanceOf[F[G[B]]])

/**
* @see [[Traverse.sequenceU]]
*
* Example:
* {{{
* scala> import cats.data.{Validated, ValidatedNel}
* scala> import cats.std.list._
* scala> import cats.syntax.traverse._
* scala> val x: List[ValidatedNel[String, Int]] = List(Validated.valid(1), Validated.invalid("a"), Validated.invalid("b")).map(_.toValidatedNel)
* scala> x.sequenceU
* res0: cats.data.ValidatedNel[String,List[Int]] = Invalid(OneAnd(a,List(b)))
* scala> x.sequence[ValidatedNel[String, ?], Int]
* res1: cats.data.ValidatedNel[String,List[Int]] = Invalid(OneAnd(a,List(b)))
* }}}
*/
def sequenceU(implicit U: Unapply[Applicative,A]): U.M[F[U.A]] =
F.sequenceU[A](fa)(U)

}

final class NestedTraverseOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Traverse[F]) {
Expand Down
9 changes: 8 additions & 1 deletion tests/src/test/scala/cats/tests/SyntaxTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,14 @@ class SyntaxTests extends AllInstances with AllSyntax {
val as2: List[A] = fa.dropWhile_(f5)
}

def testTraverse[F[_]: Traverse, G[_]: Applicative, A]: Unit = {
def testTraverse[F[_]: Traverse: FlatMap, G[_]: Applicative, A, B]: Unit = {
val fa = mock[F[A]]
val f1 = mock[A => G[B]]
val gfb: G[F[B]] = fa.traverse(f1)

val f2 = mock[A => G[F[B]]]
val gfb2: G[F[B]] = fa.traverseM(f2)

val fga = mock[F[G[A]]]
val gunit: G[F[A]] = fga.sequence
}
Expand Down

0 comments on commit 40a1211

Please sign in to comment.