Skip to content

Commit

Permalink
Fix many cats-kernel instances.
Browse files Browse the repository at this point in the history
This commit does a bunch of things:

 1. Move relevant either/function instnaces into kernel
 2. Add missing Eq/PartialOrder instances for Either
 3. Add many missing instances for Function0/Function1
 4. Add instances for BitSet
 5. Remove code duplication between collection monoids
 6. Improve test coverage of function instances
 7. Add some missing package objects in kernel
 8. A few stylistic changes

This is a relatively large commit but I think almost all of this is
stuff we'd like to get done before we freeze cats-kernel.
  • Loading branch information
erik-stripe committed Aug 23, 2016
1 parent 06539fa commit 706effc
Show file tree
Hide file tree
Showing 19 changed files with 486 additions and 144 deletions.
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/instances/bigDecimal.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package instances

trait BigDecimalInstances {
trait BigDecimalInstances extends cats.kernel.instances.BigDecimalInstances {
implicit val catsStdShowForBigDecimal: Show[BigDecimal] =
Show.fromToString[BigDecimal]
}
44 changes: 6 additions & 38 deletions core/src/main/scala/cats/instances/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cats.syntax.EitherUtil
import cats.syntax.either._
import scala.annotation.tailrec

trait EitherInstances extends EitherInstances1 {
trait EitherInstances extends cats.kernel.instances.EitherInstances {
implicit val catsStdBitraverseForEither: Bitraverse[Either] =
new Bitraverse[Either] {
def bitraverse[G[_], A, B, C, D](fab: Either[A, B])(f: A => G[C], g: B => G[D])(implicit G: Applicative[G]): G[Either[C, D]] =
Expand Down Expand Up @@ -81,51 +81,19 @@ trait EitherInstances extends EitherInstances1 {
}
// scalastyle:on method.length

implicit def catsStdOrderForEither[A, B](implicit A: Order[A], B: Order[B]): Order[Either[A, B]] = new Order[Either[A, B]] {
def compare(x: Either[A, B], y: Either[A, B]): Int = x.fold(
a => y.fold(A.compare(a, _), _ => -1),
b => y.fold(_ => 1, B.compare(b, _))
)
}

implicit def catsStdShowForEither[A, B](implicit A: Show[A], B: Show[B]): Show[Either[A, B]] =
new Show[Either[A, B]] {
def show(f: Either[A, B]): String = f.fold(
a => s"Left(${A.show(a)})",
b => s"Right(${B.show(b)})"
)
}

implicit def catsDataMonoidForEither[A, B](implicit B: Monoid[B]): Monoid[Either[A, B]] =
new Monoid[Either[A, B]] {
def empty: Either[A, B] = Right(B.empty)
def combine(x: Either[A, B], y: Either[A, B]): Either[A, B] = x combine y
}

implicit def catsDataSemigroupKForEither[L]: SemigroupK[Either[L, ?]] =
new SemigroupK[Either[L, ?]] {
def combineK[A](x: Either[L, A], y: Either[L, A]): Either[L, A] = x match {
case Left(_) => y
case Right(_) => x
}
}
}

private[instances] sealed trait EitherInstances1 extends EitherInstances2 {
implicit def catsStdPartialOrderForEither[A, B](implicit A: PartialOrder[A], B: PartialOrder[B]): PartialOrder[Either[A, B]] =
new PartialOrder[Either[A, B]] {
def partialCompare(x: Either[A, B], y: Either[A, B]): Double = x.fold(
a => y.fold(A.partialCompare(a, _), _ => -1),
b => y.fold(_ => 1, B.partialCompare(b, _))
implicit def catsStdShowForEither[A, B](implicit A: Show[A], B: Show[B]): Show[Either[A, B]] =
new Show[Either[A, B]] {
def show(f: Either[A, B]): String = f.fold(
a => s"Left(${A.show(a)})",
b => s"Right(${B.show(b)})"
)
}
}

private[instances] sealed trait EitherInstances2 {
implicit def catsStdEqForEither[A, B](implicit A: Eq[A], B: Eq[B]): Eq[Either[A, B]] = new Eq[Either[A, B]] {
def eqv(x: Either[A, B], y: Either[A, B]): Boolean = x.fold(
a => y.fold(A.eqv(a, _), _ => false),
b => y.fold(_ => false, B.eqv(b, _))
)
}
}
55 changes: 10 additions & 45 deletions core/src/main/scala/cats/instances/function.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import cats.arrow.{Arrow, Choice}
import cats.functor.Contravariant
import annotation.tailrec


trait FunctionInstances extends cats.kernel.instances.FunctionInstances
with Function0Instances with Function1Instances

private[instances] sealed trait Function0Instances {

implicit val catsStdBimonadForFunction0: Bimonad[Function0] with RecursiveTailRecM[Function0] =
Expand All @@ -29,14 +33,9 @@ private[instances] sealed trait Function0Instances {
loop(a)
}
}

implicit def catsStdEqForFunction0[A](implicit A: Eq[A]): Eq[() => A] =
new Eq[() => A] {
def eqv(x: () => A, y: () => A): Boolean = A.eqv(x(), y())
}
}

private[instances] sealed trait Function1Instances extends Function1Instances0 {
private[instances] sealed trait Function1Instances {
implicit def catsStdContravariantForFunction1[R]: Contravariant[? => R] =
new Contravariant[? => R] {
def contramap[T1, T0](fa: T1 => R)(f: T0 => T1): T0 => R =
Expand Down Expand Up @@ -91,43 +90,9 @@ private[instances] sealed trait Function1Instances extends Function1Instances0 {
def compose[A, B, C](f: B => C, g: A => B): A => C = f.compose(g)
}

implicit def catsStdMonoidForFunction1[A, B](implicit M: Monoid[B]): Monoid[A => B] =
new Function1Monoid[A, B] { def B: Monoid[B] = M }

implicit val catsStdMonoidKForFunction1: MonoidK[λ[α => α => α]] =
new Function1MonoidK {}
}

private[instances] sealed trait Function1Instances0 {
implicit def catsStdSemigroupForFunction1[A, B](implicit S: Semigroup[B]): Semigroup[A => B] =
new Function1Semigroup[A, B] { def B: Semigroup[B] = S }

implicit val catsStdSemigroupKForFunction1: SemigroupK[λ[α => α => α]] =
new Function1SemigroupK {}
}

private[instances] sealed trait Function1Semigroup[A, B] extends Semigroup[A => B] {
implicit def B: Semigroup[B]

override def combine(x: A => B, y: A => B): A => B = { a =>
B.combine(x(a), y(a))
}
}

private[instances] sealed trait Function1Monoid[A, B] extends Monoid[A => B] with Function1Semigroup[A, B] {
implicit def B: Monoid[B]

override def empty: A => B = _ => B.empty
}

private[instances] sealed trait Function1SemigroupK extends SemigroupK[λ[α => α => α]] {
override def combineK[A](x: A => A, y: A => A): A => A = x compose y
}

private[instances] sealed trait Function1MonoidK extends MonoidK[λ[α => α => α]] with Function1SemigroupK {
override def empty[A]: A => A = identity[A]
implicit val catsStdMonoidKForFunction1: MonoidK[λ[α => Function1[α, α]]] =
new MonoidK[λ[α => Function1[α, α]]] {
def empty[A]: A => A = identity[A]
def combineK[A](x: A => A, y: A => A): A => A = x compose y
}
}

trait FunctionInstances
extends Function0Instances
with Function1Instances
3 changes: 2 additions & 1 deletion core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ trait ListInstances extends cats.kernel.instances.ListInstances {

implicit def catsStdShowForList[A:Show]: Show[List[A]] =
new Show[List[A]] {
def show(fa: List[A]): String = fa.map(_.show).mkString("List(", ", ", ")")
def show(fa: List[A]): String =
fa.iterator.map(_.show).mkString("List(", ", ", ")")
}
}
10 changes: 5 additions & 5 deletions core/src/main/scala/cats/instances/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import scala.annotation.tailrec
trait MapInstances extends cats.kernel.instances.MapInstances {

implicit def catsStdShowForMap[A, B](implicit showA: Show[A], showB: Show[B]): Show[Map[A, B]] =
Show.show[Map[A, B]] { m =>
val body = m.map { case (a, b) =>
s"${showA.show(a)} -> ${showB.show(b)})"
}.mkString(",")
s"Map($body)"
new Show[Map[A, B]] {
def show(m: Map[A, B]): String =
m.iterator
.map { case (a, b) => showA.show(a) + " -> " + showB.show(b) }
.mkString("Map(", ", ", ")")
}

// scalastyle:off method.length
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/scala/cats/instances/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances {

implicit def catsStdShowForVector[A:Show]: Show[Vector[A]] =
new Show[Vector[A]] {
def show(fa: Vector[A]): String = fa.map(_.show).mkString("Vector(", ", ", ")")
def show(fa: Vector[A]): String =
fa.iterator.map(_.show).mkString("Vector(", ", ", ")")
}
}
11 changes: 11 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/StaticMethods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,15 @@ object StaticMethods {
true
}
// scalastyle:on return

def combineNIterable[A, R](b: mutable.Builder[A, R], x: Iterable[A], n: Int): R = {
var i = n
while (i > 0) { b ++= x; i -= 1 }
b.result
}

def combineAllIterable[A, R](b: mutable.Builder[A, R], xs: TraversableOnce[Iterable[A]]): R = {
xs.foreach(b ++= _)
b.result
}
}
2 changes: 2 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ package object all extends AllInstances
trait AllInstances
extends BigDecimalInstances
with BigIntInstances
with BitSetInstances
with BooleanInstances
with ByteInstances
with CharInstances
with DoubleInstances
with FloatInstances
with FunctionInstances
with IntInstances
with ListInstances
with LongInstances
Expand Down
31 changes: 31 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/bitSet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cats.kernel
package instances

import scala.collection.immutable.BitSet

package object bitSet extends BitSetInstances

trait BitSetInstances {
implicit val catsKernelStdPartialOrderForBitSet: PartialOrder[BitSet] =
new BitSetPartialOrder

implicit val catsKernelStdSemilatticeForBitSet: BoundedSemilattice[BitSet] =
new BitSetSemilattice
}

class BitSetPartialOrder extends PartialOrder[BitSet] {
def partialCompare(x: BitSet, y: BitSet): Double =
if (x eq y) 0.0
else if (x.size < y.size) if (x.subsetOf(y)) -1.0 else Double.NaN
else if (y.size < x.size) if (y.subsetOf(x)) 1.0 else Double.NaN
else if (x == y) 0.0
else Double.NaN

override def eqv(x: BitSet, y: BitSet): Boolean =
x == y
}

class BitSetSemilattice extends BoundedSemilattice[BitSet] {
def empty: BitSet = BitSet.empty
def combine(x: BitSet, y: BitSet): BitSet = x | y
}
2 changes: 2 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/double.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package instances

import java.lang.Math

package object double extends DoubleInstances

trait DoubleInstances {
implicit val catsKernelStdOrderForDouble: Order[Double] = new DoubleOrder
implicit val catsKernelStdGroupForDouble: CommutativeGroup[Double] = new DoubleGroup
Expand Down
71 changes: 71 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/either.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cats.kernel
package instances

package object either extends EitherInstances

trait EitherInstances extends EitherInstances0 {

implicit def catsStdOrderForEither[A, B](implicit A: Order[A], B: Order[B]): Order[Either[A, B]] =
new Order[Either[A, B]] {
def compare(x: Either[A, B], y: Either[A, B]): Int =
x match {
case Left(xx) => y match {
case Left(yy) => A.compare(xx, yy)
case Right(_) => -1
}
case Right(xx) => y match {
case Left(_) => 1
case Right(yy) => B.compare(xx, yy)
}
}
}

implicit def catsDataMonoidForEither[A, B](implicit B: Monoid[B]): Monoid[Either[A, B]] =
new Monoid[Either[A, B]] {
def empty: Either[A, B] =
Right(B.empty)
def combine(x: Either[A, B], y: Either[A, B]): Either[A, B] =
x match {
case left @ Left(_) => left
case Right(xx) => y match {
case left @ Left(_) => left
case Right(yy) => Right(B.combine(xx, yy))
}
}
}
}

trait EitherInstances0 extends EitherInstances1 {

implicit def catsStdPartialOrderForEither[A, B](implicit A: PartialOrder[A], B: PartialOrder[B]): PartialOrder[Either[A, B]] =
new PartialOrder[Either[A, B]] {
def partialCompare(x: Either[A, B], y: Either[A, B]): Double =
x match {
case Left(xx) => y match {
case Left(yy) => A.partialCompare(xx, yy)
case Right(_) => -1.0
}
case Right(xx) => y match {
case Left(_) => 1.0
case Right(yy) => B.partialCompare(xx, yy)
}
}
}
}

trait EitherInstances1 {
implicit def catsStdEqForEither[A, B](implicit A: Eq[A], B: Eq[B]): Eq[Either[A, B]] =
new Eq[Either[A, B]] {
def eqv(x: Either[A, B], y: Either[A, B]): Boolean =
x match {
case Left(xx) => y match {
case Left(yy) => A.eqv(xx, yy)
case Right(_) => false
}
case Right(xx) => y match {
case Left(_) => false
case Right(yy) => B.eqv(xx, yy)
}
}
}
}
2 changes: 2 additions & 0 deletions kernel/src/main/scala/cats/kernel/instances/float.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cats.kernel
package instances

package object float extends FloatInstances

trait FloatInstances {
implicit val catsKernelStdOrderForFloat: Order[Float] = new FloatOrder
implicit val catsKernelStdGroupForFloat: CommutativeGroup[Float] = new FloatGroup
Expand Down
Loading

0 comments on commit 706effc

Please sign in to comment.