Skip to content

Commit

Permalink
backporting typelevel#3076 Added Align instances
Browse files Browse the repository at this point in the history
  • Loading branch information
gagandeepkalra committed Mar 8, 2020
1 parent e2c468b commit 56bb7d0
Show file tree
Hide file tree
Showing 32 changed files with 353 additions and 29 deletions.
9 changes: 8 additions & 1 deletion binCompatTest/src/main/scala/catsBC/MimaExceptions.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package catsBC
import cats.InjectK
import cats.implicits._

object MimaExceptions {
Expand Down Expand Up @@ -27,6 +28,12 @@ object MimaExceptions {
true.iterateUntilM(Option(_))(identity _),
Either.catchOnly[NumberFormatException] { "foo".toInt },
(1.validNel[String], 2.validNel[String], 3.validNel[String]) mapN (_ + _ + _),
(1.asRight[String], 2.asRight[String], 3.asRight[String]) parMapN (_ + _ + _)
(1.asRight[String], 2.asRight[String], 3.asRight[String]) parMapN (_ + _ + _),
InjectK.catsReflexiveInjectKInstance[Option],
(
cats.Bimonad[cats.data.NonEmptyChain],
cats.NonEmptyTraverse[cats.data.NonEmptyChain],
cats.SemigroupK[cats.data.NonEmptyChain]
)
)
}
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cats

import simulacrum.typeclass
import simulacrum.noop
import cats.data.Ior

/**
* Weaker version of Applicative[F]; has apply but not pure.
Expand Down Expand Up @@ -225,6 +226,11 @@ object Apply {
*/
def semigroup[F[_], A](implicit f: Apply[F], sg: Semigroup[A]): Semigroup[F[A]] =
new ApplySemigroup[F, A](f, sg)

def align[F[_]: Apply]: Align[F] = new Align[F] {
def align[A, B](fa: F[A], fb: F[B]): F[Ior[A, B]] = Apply[F].map2(fa, fb)(Ior.both)
def functor: Functor[F] = Apply[F]
}
}

private[cats] class ApplySemigroup[F[_], A](f: Apply[F], sg: Semigroup[A]) extends Semigroup[F[A]] {
Expand Down
9 changes: 9 additions & 0 deletions core/src/main/scala/cats/SemigroupK.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cats

import simulacrum.typeclass
import cats.data.Ior

/**
* SemigroupK is a universal semigroup which operates on kinds.
Expand Down Expand Up @@ -68,3 +69,11 @@ import simulacrum.typeclass
val F = self
}
}

object SemigroupK {
def align[F[_]: SemigroupK: Functor]: Align[F] = new Align[F] {
def align[A, B](fa: F[A], fb: F[B]): F[Ior[A, B]] =
SemigroupK[F].combineK(Functor[F].map(fa)(Ior.left), Functor[F].map(fb)(Ior.right))
def functor: Functor[F] = Functor[F]
}
}
25 changes: 23 additions & 2 deletions core/src/main/scala/cats/data/Chain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,8 @@ sealed abstract private[data] class ChainInstances extends ChainInstances1 {
}

implicit val catsDataInstancesForChain
: Traverse[Chain] with Alternative[Chain] with Monad[Chain] with CoflatMap[Chain] =
new Traverse[Chain] with Alternative[Chain] with Monad[Chain] with CoflatMap[Chain] {
: Traverse[Chain] with Alternative[Chain] with Monad[Chain] with CoflatMap[Chain] with Align[Chain] =
new Traverse[Chain] with Alternative[Chain] with Monad[Chain] with CoflatMap[Chain] with Align[Chain] {
def foldLeft[A, B](fa: Chain[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)
def foldRight[A, B](fa: Chain[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Expand Down Expand Up @@ -743,6 +743,27 @@ sealed abstract private[data] class ChainInstances extends ChainInstances1 {
}

override def get[A](fa: Chain[A])(idx: Long): Option[A] = fa.get(idx)

def functor: Functor[Chain] = this

def align[A, B](fa: Chain[A], fb: Chain[B]): Chain[Ior[A, B]] =
alignWith(fa, fb)(identity)

override def alignWith[A, B, C](fa: Chain[A], fb: Chain[B])(f: Ior[A, B] => C): Chain[C] = {
val iterA = fa.iterator
val iterB = fb.iterator

var result: Chain[C] = Chain.empty

while (iterA.hasNext || iterB.hasNext) {
val ior =
if (iterA.hasNext && iterB.hasNext) Ior.both(iterA.next(), iterB.next())
else if (iterA.hasNext) Ior.left(iterA.next())
else Ior.right(iterB.next())
result = result :+ f(ior)
}
result
}
}

implicit def catsDataShowForChain[A](implicit A: Show[A]): Show[Chain[A]] =
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ sealed abstract private[data] class ConstInstances extends ConstInstances0 {
x.compare(y)
}

implicit def catsDataAlignForConst[A: Semigroup]: Align[Const[A, *]] = new Align[Const[A, *]] {
def align[B, C](fa: Const[A, B], fb: Const[A, C]): Const[A, Ior[B, C]] =
Const(Semigroup[A].combine(fa.getConst, fb.getConst))
def functor: Functor[Const[A, *]] = catsDataFunctorForConst
}

implicit def catsDataShowForConst[A: Show, B]: Show[Const[A, B]] = new Show[Const[A, B]] {
def show(f: Const[A, B]): String = f.show
}
Expand Down
18 changes: 15 additions & 3 deletions core/src/main/scala/cats/data/NonEmptyChain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,11 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A]) extends AnyVal {

sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChainInstances1 {

implicit val catsDataInstancesForNonEmptyChain
: SemigroupK[NonEmptyChain] with NonEmptyTraverse[NonEmptyChain] with Bimonad[NonEmptyChain] =
new AbstractNonEmptyInstances[Chain, NonEmptyChain] {
implicit val catsDataInstancesForNonEmptyChain: SemigroupK[NonEmptyChain]
with NonEmptyTraverse[NonEmptyChain]
with Bimonad[NonEmptyChain]
with Align[NonEmptyChain] =
new AbstractNonEmptyInstances[Chain, NonEmptyChain] with Align[NonEmptyChain] {
def extract[A](fa: NonEmptyChain[A]): A = fa.head

def nonEmptyTraverse[G[_]: Apply, A, B](fa: NonEmptyChain[A])(f: A => G[B]): G[NonEmptyChain[B]] =
Expand Down Expand Up @@ -451,6 +453,16 @@ sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChain

override def get[A](fa: NonEmptyChain[A])(idx: Long): Option[A] =
if (idx == 0) Some(fa.head) else fa.tail.get(idx - 1)

private val alignInstance = Align[Chain].asInstanceOf[Align[NonEmptyChain]]

def functor: Functor[NonEmptyChain] = alignInstance.functor

def align[A, B](fa: NonEmptyChain[A], fb: NonEmptyChain[B]): NonEmptyChain[Ior[A, B]] =
alignInstance.align(fa, fb)

override def alignWith[A, B, C](fa: NonEmptyChain[A], fb: NonEmptyChain[B])(f: Ior[A, B] => C): NonEmptyChain[C] =
alignInstance.alignWith(fa, fb)(f)
}

implicit def catsDataOrderForNonEmptyChain[A: Order]: Order[NonEmptyChain[A]] =
Expand Down
24 changes: 22 additions & 2 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -510,11 +510,12 @@ object NonEmptyList extends NonEmptyListInstances {
sealed abstract private[data] class NonEmptyListInstances extends NonEmptyListInstances0 {

implicit val catsDataInstancesForNonEmptyList
: SemigroupK[NonEmptyList] with Bimonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] =
: SemigroupK[NonEmptyList] with Bimonad[NonEmptyList] with NonEmptyTraverse[NonEmptyList] with Align[NonEmptyList] =
new NonEmptyReducible[NonEmptyList, List]
with SemigroupK[NonEmptyList]
with Bimonad[NonEmptyList]
with NonEmptyTraverse[NonEmptyList] {
with NonEmptyTraverse[NonEmptyList]
with Align[NonEmptyList] {

def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] =
a.concatNel(b)
Expand Down Expand Up @@ -619,6 +620,25 @@ sealed abstract private[data] class NonEmptyListInstances extends NonEmptyListIn

override def get[A](fa: NonEmptyList[A])(idx: Long): Option[A] =
if (idx == 0) Some(fa.head) else Foldable[List].get(fa.tail)(idx - 1)

def functor: Functor[NonEmptyList] = this

def align[A, B](fa: NonEmptyList[A], fb: NonEmptyList[B]): NonEmptyList[Ior[A, B]] =
alignWith(fa, fb)(identity)

override def alignWith[A, B, C](fa: NonEmptyList[A], fb: NonEmptyList[B])(f: Ior[A, B] => C): NonEmptyList[C] = {

@tailrec
def go(as: List[A], bs: List[B], acc: List[C]): List[C] = (as, bs) match {
case (Nil, Nil) => acc
case (Nil, y :: ys) => go(Nil, ys, f(Ior.right(y)) :: acc)
case (x :: xs, Nil) => go(xs, Nil, f(Ior.left(x)) :: acc)
case (x :: xs, y :: ys) => go(xs, ys, f(Ior.both(x, y)) :: acc)
}

NonEmptyList(f(Ior.both(fa.head, fb.head)), go(fa.tail, fb.tail, Nil).reverse)
}

}

implicit def catsDataShowForNonEmptyList[A](implicit A: Show[A]): Show[NonEmptyList[A]] =
Expand Down
9 changes: 7 additions & 2 deletions core/src/main/scala/cats/data/NonEmptyMapImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ sealed class NonEmptyMapOps[K, A](val value: NonEmptyMap[K, A]) {
sealed abstract private[data] class NonEmptyMapInstances extends NonEmptyMapInstances0 {

implicit def catsDataInstancesForNonEmptyMap[K: Order]
: SemigroupK[NonEmptyMap[K, *]] with NonEmptyTraverse[NonEmptyMap[K, *]] =
new SemigroupK[NonEmptyMap[K, *]] with NonEmptyTraverse[NonEmptyMap[K, *]] {
: SemigroupK[NonEmptyMap[K, *]] with NonEmptyTraverse[NonEmptyMap[K, *]] with Align[NonEmptyMap[K, *]] =
new SemigroupK[NonEmptyMap[K, *]] with NonEmptyTraverse[NonEmptyMap[K, *]] with Align[NonEmptyMap[K, *]] {

override def map[A, B](fa: NonEmptyMap[K, A])(f: A => B): NonEmptyMap[K, B] =
fa.map(f)
Expand Down Expand Up @@ -316,6 +316,11 @@ sealed abstract private[data] class NonEmptyMapInstances extends NonEmptyMapInst

override def toNonEmptyList[A](fa: NonEmptyMap[K, A]): NonEmptyList[A] =
NonEmptyList(fa.head._2, fa.tail.toList.map(_._2))

def functor: Functor[NonEmptyMap[K, *]] = this

def align[A, B](fa: NonEmptyMap[K, A], fb: NonEmptyMap[K, B]): NonEmptyMap[K, Ior[A, B]] =
NonEmptyMap.fromMapUnsafe(Align[SortedMap[K, *]].align(fa.toSortedMap, fb.toSortedMap))
}

implicit def catsDataHashForNonEmptyMap[K: Hash: Order, A: Hash]: Hash[NonEmptyMap[K, A]] =
Expand Down
18 changes: 15 additions & 3 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,15 @@ final class NonEmptyVector[+A] private (val toVector: Vector[A]) extends AnyVal
@suppressUnusedImportWarningForScalaVersionSpecific
sealed abstract private[data] class NonEmptyVectorInstances {

implicit val catsDataInstancesForNonEmptyVector
: SemigroupK[NonEmptyVector] with Bimonad[NonEmptyVector] with NonEmptyTraverse[NonEmptyVector] =
implicit val catsDataInstancesForNonEmptyVector: SemigroupK[NonEmptyVector]
with Bimonad[NonEmptyVector]
with NonEmptyTraverse[NonEmptyVector]
with Align[NonEmptyVector] =
new NonEmptyReducible[NonEmptyVector, Vector]
with SemigroupK[NonEmptyVector]
with Bimonad[NonEmptyVector]
with NonEmptyTraverse[NonEmptyVector] {
with NonEmptyTraverse[NonEmptyVector]
with Align[NonEmptyVector] {

def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] =
a.concatNev(b)
Expand Down Expand Up @@ -360,6 +363,15 @@ sealed abstract private[data] class NonEmptyVectorInstances {

override def toNonEmptyList[A](fa: NonEmptyVector[A]): NonEmptyList[A] =
NonEmptyList(fa.head, fa.tail.toList)

def functor: Functor[NonEmptyVector] = this

def align[A, B](fa: NonEmptyVector[A], fb: NonEmptyVector[B]): NonEmptyVector[Ior[A, B]] =
NonEmptyVector.fromVectorUnsafe(Align[Vector].align(fa.toVector, fb.toVector))

override def alignWith[A, B, C](fa: NonEmptyVector[A],
fb: NonEmptyVector[B])(f: Ior[A, B] => C): NonEmptyVector[C] =
NonEmptyVector.fromVectorUnsafe(Align[Vector].alignWith(fa.toVector, fb.toVector)(f))
}

implicit def catsDataEqForNonEmptyVector[A](implicit A: Eq[A]): Eq[NonEmptyVector[A]] =
Expand Down
21 changes: 21 additions & 0 deletions core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,27 @@ sealed abstract private[data] class ValidatedInstances extends ValidatedInstance
}
}

implicit def catsDataAlignForValidated[E: Semigroup]: Align[Validated[E, *]] =
new Align[Validated[E, *]] {
def functor: Functor[Validated[E, *]] = catsDataTraverseFunctorForValidated
def align[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, Ior[A, B]] =
alignWith(fa, fb)(identity)

override def alignWith[A, B, C](fa: Validated[E, A], fb: Validated[E, B])(f: Ior[A, B] => C): Validated[E, C] =
fa match {
case Invalid(e) =>
fb match {
case Invalid(e2) => Invalid(Semigroup[E].combine(e, e2))
case Valid(b) => Valid(f(Ior.right(b)))
}
case Valid(a) =>
fb match {
case Invalid(e) => Valid(f(Ior.left(a)))
case Valid(b) => Valid(f(Ior.both(a, b)))
}
}
}

implicit def catsDataMonoidForValidated[A, B](implicit A: Semigroup[A], B: Monoid[B]): Monoid[Validated[A, B]] =
new Monoid[Validated[A, B]] {
def empty: Validated[A, B] = Valid(B.empty)
Expand Down
25 changes: 23 additions & 2 deletions core/src/main/scala/cats/instances/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cats.data.Validated
import cats.syntax.EitherUtil
import cats.syntax.either._
import scala.annotation.tailrec
import cats.data.Ior

trait EitherInstances extends cats.kernel.instances.EitherInstances {
implicit val catsStdBitraverseForEither: Bitraverse[Either] =
Expand All @@ -31,8 +32,9 @@ trait EitherInstances extends cats.kernel.instances.EitherInstances {
}

// scalastyle:off method.length
implicit def catsStdInstancesForEither[A]: MonadError[Either[A, *], A] with Traverse[Either[A, *]] =
new MonadError[Either[A, *], A] with Traverse[Either[A, *]] {
implicit def catsStdInstancesForEither[A]
: MonadError[Either[A, *], A] with Traverse[Either[A, *]] with Align[Either[A, *]] =
new MonadError[Either[A, *], A] with Traverse[Either[A, *]] with Align[Either[A, *]] {
def pure[B](b: B): Either[A, B] = Right(b)

def flatMap[B, C](fa: Either[A, B])(f: B => Either[A, C]): Either[A, C] =
Expand Down Expand Up @@ -140,6 +142,25 @@ trait EitherInstances extends cats.kernel.instances.EitherInstances {

override def isEmpty[B](fab: Either[A, B]): Boolean =
fab.isLeft

def functor: Functor[Either[A, *]] = this

def align[B, C](fa: Either[A, B], fb: Either[A, C]): Either[A, Ior[B, C]] =
alignWith(fa, fb)(identity)

override def alignWith[B, C, D](fb: Either[A, B], fc: Either[A, C])(f: Ior[B, C] => D): Either[A, D] = fb match {
case left @ Left(a) =>
fc match {
case Left(_) => left.rightCast[D]
case Right(c) => Right(f(Ior.right(c)))
}
case Right(b) =>
fc match {
case Left(a) => Right(f(Ior.left(b)))
case Right(c) => Right(f(Ior.both(b, c)))
}
}

}
// scalastyle:on method.length

Expand Down
24 changes: 21 additions & 3 deletions core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import cats.syntax.show._
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer

import cats.data.Ior

trait ListInstances extends cats.kernel.instances.ListInstances {

implicit val catsStdInstancesForList: Traverse[List] with Alternative[List] with Monad[List] with CoflatMap[List] =
new Traverse[List] with Alternative[List] with Monad[List] with CoflatMap[List] {
implicit val catsStdInstancesForList
: Traverse[List] with Alternative[List] with Monad[List] with CoflatMap[List] with Align[List] =
new Traverse[List] with Alternative[List] with Monad[List] with CoflatMap[List] with Align[List] {
def empty[A]: List[A] = Nil

def combineK[A](x: List[A], y: List[A]): List[A] = x ++ y
Expand Down Expand Up @@ -75,6 +78,22 @@ trait ListInstances extends cats.kernel.instances.ListInstances {
G.map2Eval(f(a), lglb)(_ :: _)
}.value

def functor: Functor[List] = this

def align[A, B](fa: List[A], fb: List[B]): List[A Ior B] =
alignWith(fa, fb)(identity)

override def alignWith[A, B, C](fa: List[A], fb: List[B])(f: Ior[A, B] => C): List[C] = {
@tailrec def loop(buf: ListBuffer[C], as: List[A], bs: List[B]): List[C] =
(as, bs) match {
case (a :: atail, b :: btail) => loop(buf += f(Ior.Both(a, b)), atail, btail)
case (Nil, Nil) => buf.toList
case (arest, Nil) => (buf ++= arest.map(a => f(Ior.left(a)))).toList
case (Nil, brest) => (buf ++= brest.map(b => f(Ior.right(b)))).toList
}
loop(ListBuffer.empty[C], fa, fb)
}

override def mapWithIndex[A, B](fa: List[A])(f: (A, Int) => B): List[B] =
fa.iterator.zipWithIndex.map(ai => f(ai._1, ai._2)).toList

Expand Down Expand Up @@ -143,7 +162,6 @@ trait ListInstances extends cats.kernel.instances.ListInstances {

override def collectFirstSome[A, B](fa: List[A])(f: A => Option[B]): Option[B] =
fa.collectFirst(Function.unlift(f))

}

implicit def catsStdShowForList[A: Show]: Show[List[A]] =
Expand Down
Loading

0 comments on commit 56bb7d0

Please sign in to comment.