Skip to content
Merged
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
49 changes: 49 additions & 0 deletions core/src/main/scala/cats/data/Ior.scala
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,55 @@ sealed abstract private[data] class IorInstances0 {

override def map[B, C](fa: A Ior B)(f: B => C): A Ior C =
fa.map(f)

override def as[B, C](fa: A Ior B, c: C): A Ior C =
fa match {
case Ior.Left(_) => fa.asInstanceOf[A Ior C]
case Ior.Right(_) => Ior.Right(c)
case Ior.Both(a, _) => Ior.Both(a, c)
}

override def tupleLeft[B, C](fa: A Ior B, c: C): A Ior (C, B) =
fa match {
case Ior.Left(_) => fa.asInstanceOf[A Ior (C, B)]
case Ior.Right(b) => Ior.Right((c, b))
case Ior.Both(a, b) => Ior.Both(a, (c, b))
}

override def tupleRight[B, C](fa: A Ior B, c: C): A Ior (B, C) =
fa match {
case Ior.Left(_) => fa.asInstanceOf[A Ior (B, C)]
case Ior.Right(b) => Ior.Right((b, c))
case Ior.Both(a, b) => Ior.Both(a, (b, c))
}

override def fproduct[B, C](fa: A Ior B)(f: B => C): A Ior (B, C) =
fa match {
case Ior.Left(_) => fa.asInstanceOf[A Ior (B, C)]
case Ior.Right(b) => Ior.Right((b, f(b)))
case Ior.Both(a, b) => Ior.Both(a, (b, f(b)))
}

override def fproductLeft[B, C](fa: A Ior B)(f: B => C): A Ior (C, B) =
fa match {
case Ior.Left(_) => fa.asInstanceOf[A Ior (C, B)]
case Ior.Right(b) => Ior.Right((f(b), b))
case Ior.Both(a, b) => Ior.Both(a, (f(b), b))
}

override def unzip[B, C](fab: A Ior (B, C)): (A Ior B, A Ior C) =
fab match {
case Ior.Left(a) => (Ior.Left(a), Ior.Left(a))
case Ior.Right((b, c)) => (Ior.Right(b), Ior.Right(c))
case Ior.Both(a, (b, c)) => (Ior.Both(a, b), Ior.Both(a, c))
}

override def void[B](fa: A Ior B): A Ior Unit =
fa match {
case Ior.Left(_) => fa.asInstanceOf[A Ior Unit]
case Ior.Right(_) => Ior.Right(())
case Ior.Both(a, _) => Ior.Both(a, ())
}
}

implicit def catsDataEqForIor[A: Eq, B: Eq]: Eq[A Ior B] = _ === _
Expand Down
37 changes: 37 additions & 0 deletions core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,36 @@ sealed abstract private[data] class ValidatedInstances2 {
override def map[A, B](fa: Validated[E, A])(f: (A) => B): Validated[E, B] =
fa.map(f)

override def as[A, B](fa: Validated[E, A], b: B): Validated[E, B] =
fa match {
case Valid(_) => Valid(b)
case i @ Invalid(_) => i.asInstanceOf[Validated[E, B]]
}

override def tupleLeft[A, B](fa: Validated[E, A], b: B): Validated[E, (B, A)] =
fa match {
case Valid(a) => Valid((b, a))
case i @ Invalid(_) => i.asInstanceOf[Validated[E, (B, A)]]
}

override def tupleRight[A, B](fa: Validated[E, A], b: B): Validated[E, (A, B)] =
fa match {
case Valid(a) => Valid((a, b))
case i @ Invalid(_) => i.asInstanceOf[Validated[E, (A, B)]]
}

override def fproduct[A, B](fa: Validated[E, A])(f: A => B): Validated[E, (A, B)] =
fa match {
case Valid(a) => Valid((a, f(a)))
case i @ Invalid(_) => i.asInstanceOf[Validated[E, (A, B)]]
}

override def fproductLeft[A, B](fa: Validated[E, A])(f: A => B): Validated[E, (B, A)] =
fa match {
case Valid(a) => Valid((f(a), a))
case i @ Invalid(_) => i.asInstanceOf[Validated[E, (B, A)]]
}

override def reduceLeftToOption[A, B](fa: Validated[E, A])(f: A => B)(g: (B, A) => B): Option[B] =
fa.map(f).toOption

Expand Down Expand Up @@ -1052,6 +1082,13 @@ sealed abstract private[data] class ValidatedInstances2 {
case _ => Nil
}

override def unzip[A, B](fab: Validated[E, (A, B)]): (Validated[E, A], Validated[E, B]) =
fab match {
case Valid((a, b)) => (Valid(a), Valid(b))
case i @ Invalid(_) =>
(i.asInstanceOf[Validated[E, A]], i.asInstanceOf[Validated[E, B]])
}

override def isEmpty[A](fa: Validated[E, A]): Boolean = fa.isInvalid

override def void[A](fa: Validated[E, A]): Validated[E, Unit] =
Expand Down
36 changes: 36 additions & 0 deletions core/src/main/scala/cats/instances/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,36 @@ trait EitherInstances extends cats.kernel.instances.EitherInstances {
override def map[B, C](fa: Either[A, B])(f: B => C): Either[A, C] =
fa.map(f)

override def as[B, C](fa: Either[A, B], c: C): Either[A, C] =
fa match {
case Right(_) => Right(c)
case left @ Left(_) => left.rightCast[C]
}

override def tupleLeft[B, C](fa: Either[A, B], c: C): Either[A, (C, B)] =
fa match {
case Right(b) => Right((c, b))
case left @ Left(_) => left.rightCast[(C, B)]
}

override def tupleRight[B, C](fa: Either[A, B], c: C): Either[A, (B, C)] =
fa match {
case Right(b) => Right((b, c))
case left @ Left(_) => left.rightCast[(B, C)]
}

override def fproduct[B, C](fa: Either[A, B])(f: B => C): Either[A, (B, C)] =
fa match {
case Right(b) => Right((b, f(b)))
case left @ Left(_) => left.rightCast[(B, C)]
}

override def fproductLeft[B, C](fa: Either[A, B])(f: B => C): Either[A, (C, B)] =
fa match {
case Right(b) => Right((f(b), b))
case left @ Left(_) => left.rightCast[(C, B)]
}

@tailrec
def tailRecM[B, C](b: B)(f: B => Either[A, Either[B, C]]): Either[A, C] =
f(b) match {
Expand Down Expand Up @@ -210,6 +240,12 @@ trait EitherInstances extends cats.kernel.instances.EitherInstances {
}
}

override def unzip[B, C](fab: Either[A, (B, C)]): (Either[A, B], Either[A, C]) =
fab match {
case Right((b, c)) => (Right(b), Right(c))
case left @ Left(_) => (left.rightCast[B], left.rightCast[C])
}

override def void[B](e: Either[A, B]): Either[A, Unit] =
if (e.isRight) Either.unit
else e.asInstanceOf[Either[A, Unit]] // it is Left(a)
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ trait ListInstances extends cats.kernel.instances.ListInstances {
override def zipWithIndex[A](fa: List[A]): List[(A, Int)] =
fa.zipWithIndex

override def unzip[A, B](fab: List[(A, B)]): (List[A], List[B]) =
fab.unzip

override def partitionEither[A, B, C](
fa: List[A]
)(f: A => Either[B, C])(implicit A: Alternative[List]): (List[B], List[C]) =
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/scala/cats/instances/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ trait MapInstances extends cats.kernel.instances.MapInstances {
override def map[A, B](fa: Map[K, A])(f: A => B): Map[K, B] =
fa.map { case (k, a) => (k, f(a)) }

override def unzip[A, B](fab: Map[K, (A, B)]): (Map[K, A], Map[K, B]) = {
val leftBuilder = Map.newBuilder[K, A]
val rightBuilder = Map.newBuilder[K, B]
leftBuilder.sizeHint(fab.size)
rightBuilder.sizeHint(fab.size)
fab.foreach { case (k, (a, b)) =>
leftBuilder += k -> a
rightBuilder += k -> b
}
(leftBuilder.result(), rightBuilder.result())
}

override def map2[A, B, Z](fa: Map[K, A], fb: Map[K, B])(f: (A, B) => Z): Map[K, Z] =
if (fb.isEmpty) Map.empty // do O(1) work if fb is empty
else fa.flatMap { case (k, a) => fb.get(k).map(b => (k, f(a, b))) }
Expand Down
33 changes: 33 additions & 0 deletions core/src/main/scala/cats/instances/option.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,33 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances {
override def map[A, B](fa: Option[A])(f: A => B): Option[B] =
fa.map(f)

override def as[A, B](fa: Option[A], b: B): Option[B] =
if (fa.isDefined) Some(b) else None

override def tupleLeft[A, B](fa: Option[A], b: B): Option[(B, A)] =
fa match {
case Some(a) => Some((b, a))
case None => None
}

override def tupleRight[A, B](fa: Option[A], b: B): Option[(A, B)] =
fa match {
case Some(a) => Some((a, b))
case None => None
}

override def fproduct[A, B](fa: Option[A])(f: A => B): Option[(A, B)] =
fa match {
case Some(a) => Some((a, f(a)))
case None => None
}

override def fproductLeft[A, B](fa: Option[A])(f: A => B): Option[(B, A)] =
fa match {
case Some(a) => Some((f(a), a))
case None => None
}

def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] =
fa.flatMap(f)

Expand Down Expand Up @@ -244,6 +271,12 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances {
case (Some(a), Some(b)) => Some(f(Ior.both(a, b)))
}

override def unzip[A, B](fab: Option[(A, B)]): (Option[A], Option[B]) =
fab match {
case Some((a, b)) => (Some(a), Some(b))
case None => (None, None)
}

override def unit: Option[Unit] = someUnit
override def void[A](oa: Option[A]): Option[Unit] =
if (oa.isDefined) someUnit else None
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/queue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances {

override def toIterable[A](fa: Queue[A]): Iterable[A] = fa

override def unzip[A, B](fab: Queue[(A, B)]): (Queue[A], Queue[B]) =
fab.unzip

override def reduceLeftOption[A](fa: Queue[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/seq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances {
override def zipWithIndex[A](fa: Seq[A]): Seq[(A, Int)] =
fa.zipWithIndex

override def unzip[A, B](fab: Seq[(A, B)]): (Seq[A], Seq[B]) =
fab.unzip

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

Expand Down
13 changes: 13 additions & 0 deletions core/src/main/scala/cats/instances/sortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ trait SortedMapInstances extends SortedMapInstances2 {
fa.map { case (k, a) => (k, f(a)) }
}

override def unzip[A, B](fab: SortedMap[K, (A, B)]): (SortedMap[K, A], SortedMap[K, B]) = {
implicit val ordering: Ordering[K] = fab.ordering
val leftBuilder = SortedMap.newBuilder[K, A](ordering)
val rightBuilder = SortedMap.newBuilder[K, B](ordering)
leftBuilder.sizeHint(fab.size)
rightBuilder.sizeHint(fab.size)
fab.foreach { case (k, (a, b)) =>
leftBuilder += k -> a
rightBuilder += k -> b
}
(leftBuilder.result(), rightBuilder.result())
}

override def map2Eval[A, B, Z](
fa: SortedMap[K, A],
fb: Eval[SortedMap[K, B]]
Expand Down
36 changes: 36 additions & 0 deletions core/src/main/scala/cats/instances/try.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,36 @@ trait TryInstances extends TryInstances1 {

override def map[A, B](ta: Try[A])(f: A => B): Try[B] = ta.map(f)

override def as[A, B](ta: Try[A], b: B): Try[B] =
ta match {
case Success(_) => Success(b)
case err: Failure[?] => castFailure[B](err)
}

override def tupleLeft[A, B](ta: Try[A], b: B): Try[(B, A)] =
ta match {
case Success(a) => Success((b, a))
case err: Failure[?] => castFailure[(B, A)](err)
}

override def tupleRight[A, B](ta: Try[A], b: B): Try[(A, B)] =
ta match {
case Success(a) => Success((a, b))
case err: Failure[?] => castFailure[(A, B)](err)
}

override def fproduct[A, B](ta: Try[A])(f: A => B): Try[(A, B)] =
ta match {
case Success(a) => Success((a, f(a)))
case err: Failure[?] => castFailure[(A, B)](err)
}

override def fproductLeft[A, B](ta: Try[A])(f: A => B): Try[(B, A)] =
ta match {
case Success(a) => Success((f(a), a))
case err: Failure[?] => castFailure[(B, A)](err)
}

override def reduceLeftToOption[A, B](fa: Try[A])(f: A => B)(g: (B, A) => B): Option[B] =
fa.map(f).toOption

Expand Down Expand Up @@ -165,6 +195,12 @@ trait TryInstances extends TryInstances1 {
case Success(a) => a :: Nil
}

override def unzip[A, B](fab: Try[(A, B)]): (Try[A], Try[B]) =
fab match {
case Success((a, b)) => (Success(a), Success(b))
case err: Failure[?] => (castFailure[A](err), castFailure[B](err))
}

override def isEmpty[A](fa: Try[A]): Boolean = fa.isFailure

override def catchNonFatal[A](a: => A)(implicit ev: Throwable <:< Throwable): Try[A] = Try(a)
Expand Down
21 changes: 21 additions & 0 deletions core/src/main/scala/cats/instances/tuple.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,27 @@ private[instances] trait Tuple2InstancesBinCompat0 {

implicit val catsDataFunctorForPair: Functor[λ[P => (P, P)]] = new Functor[λ[P => (P, P)]] {
override def map[A, B](fa: (A, A))(f: A => B): (B, B) = (f(fa._1), f(fa._2))

override def as[A, B](fa: (A, A), b: B): (B, B) = (b, b)

override def tupleLeft[A, B](fa: (A, A), b: B): ((B, A), (B, A)) =
((b, fa._1), (b, fa._2))

override def tupleRight[A, B](fa: (A, A), b: B): ((A, B), (A, B)) =
((fa._1, b), (fa._2, b))

override def fproduct[A, B](fa: (A, A))(f: A => B): ((A, B), (A, B)) =
((fa._1, f(fa._1)), (fa._2, f(fa._2)))

override def fproductLeft[A, B](fa: (A, A))(f: A => B): ((B, A), (B, A)) =
((f(fa._1), fa._1), (f(fa._2), fa._2))

override def unzip[A, B](fab: ((A, B), (A, B))): ((A, A), (B, B)) =
((fab._1._1, fab._2._1), (fab._1._2, fab._2._2))

private val unit: (Unit, Unit) = ((), ())

override def void[A](fa: (A, A)): (Unit, Unit) = unit
}
}

Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances {

override def algebra[A]: Monoid[Vector[A]] = kernel.instances.VectorMonoid[A]

override def unzip[A, B](fab: Vector[(A, B)]): (Vector[A], Vector[B]) =
fab.unzip

def functor: Functor[Vector] = this

def align[A, B](fa: Vector[A], fb: Vector[B]): Vector[A Ior B] = {
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ package object cats {
}
override def distribute[F[_], A, B](fa: F[A])(f: A => B)(implicit F: Functor[F]): Id[F[B]] = F.map(fa)(f)
override def map[A, B](fa: A)(f: A => B): B = f(fa)
override def as[A, B](fa: A, b: B): B = b
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder – should we get void here for completeness? As long as as is here.

override def void[A](fa: A): Unit = ()
override def unit: Unit = ()
override def tupleLeft[A, B](fa: A, b: B): (B, A) = (b, fa)
override def tupleRight[A, B](fa: A, b: B): (A, B) = (fa, b)
override def fproduct[A, B](fa: A)(f: A => B): (A, B) = (fa, f(fa))
override def fproductLeft[A, B](fa: A)(f: A => B): (B, A) = (f(fa), fa)
override def unzip[A, B](fab: (A, B)): (A, B) = fab
override def ap[A, B](ff: A => B)(fa: A): B = ff(fa)
override def flatten[A](ffa: A): A = ffa
override def map2[A, B, Z](fa: A, fb: B)(f: (A, B) => Z): Z = f(fa, fb)
Expand Down
15 changes: 15 additions & 0 deletions tests/shared/src/test/scala/cats/tests/EitherSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,21 @@ class EitherSuite extends CatsSuite {
}
}

test("functor default methods match map-based implementations") {
val F = Functor[Either[Int, *]]
forAll { (fa: Either[Int, String], b: Long, f: String => Double) =>
assert(F.as(fa, b) === F.map(fa)(_ => b))
assert(F.tupleLeft(fa, b) === F.map(fa)(a => (b, a)))
assert(F.tupleRight(fa, b) === F.map(fa)(a => (a, b)))
assert(F.fproduct(fa)(f) === F.map(fa)(a => (a, f(a))))
assert(F.fproductLeft(fa)(f) === F.map(fa)(a => (f(a), a)))
assert(F.void(fa) === F.map(fa)(_ => ()))
}
forAll { (fab: Either[Int, (String, Long)]) =>
assert(F.unzip(fab) === (F.map(fab)(_._1), F.map(fab)(_._2)))
}
}

test("map2Eval is lazy") {
val bomb: Eval[Either[String, Int]] = Later(sys.error("boom"))
val x: Either[String, Int] = Left("l")
Expand Down
Loading