Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add leftTraverse and leftSequence to Bitraverse typeclass #2646

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ trait AllSyntaxBinCompat2
with ListSyntaxBinCompat0
with ValidatedSyntaxBincompat0

trait AllSyntaxBinCompat3 extends UnorderedFoldableSyntax with Function1Syntax
trait AllSyntaxBinCompat3 extends UnorderedFoldableSyntax with Function1Syntax with BitraverseSyntaxBinCompat0
40 changes: 40 additions & 0 deletions core/src/main/scala/cats/syntax/bitraverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,43 @@ final class NestedBitraverseOps[F[_, _], G[_], A, B](private val fgagb: F[G[A],
def bisequence(implicit F: Bitraverse[F], G: Applicative[G]): G[F[A, B]] =
F.bisequence(fgagb)
}

trait BitraverseSyntaxBinCompat0 extends BisequenceSyntaxBinCompat0 {
implicit final def catsSyntaxBitraverseBinCompat0[F[_, _], A, B](fab: F[A, B]): BitraverseOpsBinCompat0[F, A, B] =
new BitraverseOpsBinCompat0(fab)
}

final class BitraverseOpsBinCompat0[F[_, _], A, B](private val value: F[A, B]) extends AnyVal {

/**
* Traverse the left side of the structure with the given function.
*
* Example:
* {{{
* scala> import cats.implicits._
*
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
*
* scala> ("1", "2").leftTraverse(parseInt)
* res0: Option[(Int, String)] = Some((1,2))
*
* scala> ("two", "2").leftTraverse(parseInt)
* res2: Option[(Int, String)] = None
*
* }}}
*/
def leftTraverse[G[_], C](f: A => G[C])(implicit F: Bitraverse[F], G: Applicative[G]): G[F[C, B]] =
F.bitraverse(value)(f, G.pure)
}

trait BisequenceSyntaxBinCompat0 {
implicit final def catsSyntaxBisequenceBinCompat0[F[_, _], G[_], A, B](
fgab: F[G[A], B]
): BisequenceOpsBinCompat0[F, G, A, B] =
new BisequenceOpsBinCompat0(fgab)
}

final class BisequenceOpsBinCompat0[F[_, _], G[_], A, B](private val value: F[G[A], B]) extends AnyVal {
def leftSequence(implicit F: Bitraverse[F], G: Applicative[G]): G[F[A, B]] =
new BitraverseOpsBinCompat0(value).leftTraverse(identity)
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ package object syntax {
object bifunctor extends BifunctorSyntax
object bifoldable extends BifoldableSyntax
object binested extends BinestedSyntax
object bitraverse extends BitraverseSyntax
object bitraverse extends BitraverseSyntax with BitraverseSyntaxBinCompat0
@deprecated("use cats.syntax.semigroupal instead", "1.0.0-RC1")
object cartesian extends SemigroupalSyntax
object choice extends ChoiceSyntax
Expand Down
18 changes: 17 additions & 1 deletion laws/src/main/scala/cats/laws/BitraverseLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package cats
package laws

import cats.data.Nested
import cats.syntax.BitraverseSyntaxBinCompat0

trait BitraverseLaws[F[_, _]] extends BifoldableLaws[F] with BifunctorLaws[F] {
trait BitraverseLaws[F[_, _]] extends BifoldableLaws[F] with BifunctorLaws[F] with BitraverseSyntaxBinCompat0 {
implicit override def F: Bitraverse[F]

def bitraverseIdentity[A, B](fab: F[A, B]): IsEq[F[A, B]] =
Expand All @@ -28,6 +29,21 @@ trait BitraverseLaws[F[_, _]] extends BifoldableLaws[F] with BifunctorLaws[F] {

hi <-> c.value
}

def leftTraverseIdentity[A, B](fab: F[A, B]): IsEq[F[A, B]] =
fab <-> fab.leftTraverse[Id, A](identity)

def leftTraverseCompose[G[_], A, B, C, D](
fab: F[A, B],
f: A => G[C],
g: C => G[D]
)(implicit G: Applicative[G]): IsEq[G[G[F[D, B]]]] = {
val fg = G.map(fab.leftTraverse(f))(f => f.leftTraverse(g))
val fg2 = fab.leftTraverse(a => Nested(G.map(f(a))(g)))

fg <-> fg2.value
}

}

object BitraverseLaws {
Expand Down
5 changes: 5 additions & 0 deletions tests/src/test/scala/cats/tests/SyntaxSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,14 @@ object SyntaxSuite extends AllSyntaxBinCompat with AllInstances with AllSyntax {

val fab = mock[F[A, B]]
val gfcd = fab.bitraverse(f, g)
val gfcb = fab.leftTraverse(f)

val fgagb = mock[F[G[A], G[B]]]
val gfab = fgagb.bisequence

val fgab = mock[F[G[A], B]]
val gfab2 = fgab.leftSequence

}

def testAlternativeMonad[F[_]: Alternative: Monad, G[_]: Foldable, H[_, _]: Bifoldable, A, B]: Unit = {
Expand Down