Skip to content

Commit

Permalink
Merge pull request #1153 from ceedubs/traverse-syntax
Browse files Browse the repository at this point in the history
Use Simulacrum for Traverse syntax
  • Loading branch information
kailuowang authored Jul 13, 2016
2 parents acd5892 + 2c6690d commit 786f480
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 101 deletions.
55 changes: 55 additions & 0 deletions core/src/main/scala/cats/Traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,87 @@ import simulacrum.typeclass
* Given a function which returns a G effect, thread this effect
* through the running of this function on all the values in F,
* returning an F[B] in a G context.
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* 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, A, B](fa: F[A])(f: A => G[B]): G[F[B]]

/**
* Behaves just like traverse, but uses [[Unapply]] to find the
* Applicative instance for G.
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* 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[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.
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* 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[_], 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]].
*
* Example:
* {{{
* scala> import cats.implicits._
* 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[_]: Applicative, A](fga: F[G[A]]): G[F[A]] =
traverse(fga)(ga => ga)

/**
* Behaves just like sequence, but uses [[Unapply]] to find the
* Applicative instance for G.
*
* Example:
* {{{
* scala> import cats.data.{Validated, ValidatedNel}
* scala> import cats.implicits._
* 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[GA](fga: F[GA])(implicit U: Unapply[Applicative, GA]): U.M[F[U.A]] =
traverse(fga)(U.subst)(U.TC)
Expand Down
107 changes: 6 additions & 101 deletions core/src/main/scala/cats/syntax/traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,106 +2,11 @@ package cats
package syntax

private[syntax] trait TraverseSyntax1 {
implicit def catsSyntaxUTraverse[FA](fa: FA)(implicit U: Unapply[Traverse, FA]): TraverseOps[U.M, U.A] =
new TraverseOps(U.subst(fa))(U.TC)
implicit def catsSyntaxUTraverse[FA](fa: FA)(implicit U: Unapply[Traverse, FA]): Traverse.Ops[U.M, U.A] =
new Traverse.Ops[U.M, U.A]{
val self = U.subst(fa)
val typeClassInstance = U.TC
}
}

trait TraverseSyntax extends TraverseSyntax1 {
// TODO: use simulacrum instances eventually
implicit def catsSyntaxTraverse[F[_]: Traverse, A](fa: F[A]): TraverseOps[F, A] =
new TraverseOps(fa)

implicit def catsSyntaxNestedTraverse[F[_]: Traverse, G[_], A](fga: F[G[A]]): NestedTraverseOps[F, G, A] =
new NestedTraverseOps[F, G, A](fga)
}

final class TraverseOps[F[_], A](fa: F[A])(implicit F: Traverse[F]) {
/**
* @see [[Traverse.traverse]]
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* 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.implicits._
* 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)

/**
* @see [[Traverse.traverseM]]
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
* 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.implicits._
* 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.implicits._
* 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]) {
def sequence(implicit G: Applicative[G]): G[F[A]] = F.sequence(fga)
}
trait TraverseSyntax extends Traverse.ToTraverseOps with TraverseSyntax1

0 comments on commit 786f480

Please sign in to comment.