Skip to content

Commit

Permalink
Fix ambiguous Const instances and add missing instances
Browse files Browse the repository at this point in the history
 - Reorder instances in `Const` systematically
 - Fix ambiguous `InvariantMonoidal` /  instance
 - Add `InvariantSemigroupal` instance
 - Drop implicit from unnecessary `Functor` instance
 - Add `Hash` instance and `hash` method
 - Add `SemigroupK` and `MonoidK` instances
  • Loading branch information
joroKr21 committed Jun 11, 2023
1 parent 324b577 commit 9bb21c1
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 26 deletions.
66 changes: 48 additions & 18 deletions core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ package data

import cats.kernel.{CommutativeMonoid, CommutativeSemigroup, LowerBounded, UpperBounded}

import scala.annotation.nowarn

/**
* [[Const]] is a phantom type, it does not contain a value of its second type parameter `B`
* [[Const]] can be seen as a type level version of `Function.const[A, B]: A => B => A`
Expand All @@ -39,6 +41,7 @@ final case class Const[A, B](getConst: A) {
def combine(that: Const[A, B])(implicit A: Semigroup[A]): Const[A, B] =
Const(A.combine(getConst, that.getConst))

@nowarn("cat=unused")
def traverse[F[_], C](f: B => F[C])(implicit F: Applicative[F]): F[Const[A, C]] =
F.pure(retag[C])

Expand All @@ -51,6 +54,9 @@ final case class Const[A, B](getConst: A) {
def compare(that: Const[A, B])(implicit A: Order[A]): Int =
A.compare(getConst, that.getConst)

def hash(implicit A: Hash[A]): Int =
Const(A.hash(getConst)).hashCode()

def show(implicit A: Show[A]): String =
s"Const(${A.show(getConst)})"
}
Expand Down Expand Up @@ -92,6 +98,8 @@ sealed abstract private[data] class ConstInstances extends ConstInstances0 {

implicit def catsDataOrderForConst[A: Order, B]: Order[Const[A, B]] = _ compare _

implicit def catsDataPartialOrderForConst[A: PartialOrder, B]: PartialOrder[Const[A, B]] = _ partialCompare _

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]] =
Expand Down Expand Up @@ -121,22 +129,22 @@ sealed abstract private[data] class ConstInstances extends ConstInstances0 {
implicit def catsDataTraverseFilterForConst[C]: TraverseFilter[Const[C, *]] =
new TraverseFilter[Const[C, *]] {

override def mapFilter[A, B](fa: Const[C, A])(f: (A) => Option[B]): Const[C, B] = fa.retag
override def mapFilter[A, B](fa: Const[C, A])(f: A => Option[B]): Const[C, B] = fa.retag

override def collect[A, B](fa: Const[C, A])(f: PartialFunction[A, B]): Const[C, B] = fa.retag

override def flattenOption[A](fa: Const[C, Option[A]]): Const[C, A] = fa.retag

override def filter[A](fa: Const[C, A])(f: (A) => Boolean): Const[C, A] = fa.retag
override def filter[A](fa: Const[C, A])(f: A => Boolean): Const[C, A] = fa.retag

override def filterNot[A](fa: Const[C, A])(f: A => Boolean): Const[C, A] = fa.retag

def traverseFilter[G[_], A, B](
fa: Const[C, A]
)(f: (A) => G[Option[B]])(implicit G: Applicative[G]): G[Const[C, B]] =
)(f: A => G[Option[B]])(implicit G: Applicative[G]): G[Const[C, B]] =
G.pure(fa.retag[B])

override def filterA[G[_], A](fa: Const[C, A])(f: (A) => G[Boolean])(implicit G: Applicative[G]): G[Const[C, A]] =
override def filterA[G[_], A](fa: Const[C, A])(f: A => G[Boolean])(implicit G: Applicative[G]): G[Const[C, A]] =
G.pure(fa)

val traverse: Traverse[Const[C, *]] = Const.catsDataTraverseForConst[C]
Expand All @@ -151,6 +159,8 @@ sealed abstract private[data] class ConstInstances extends ConstInstances0 {
x.combine(y)
}

implicit def catsDataSemigroupForConst[A: Semigroup, B]: Semigroup[Const[A, B]] = _ combine _

implicit val catsDataBifoldableForConst: Bifoldable[Const] =
new Bifoldable[Const] {
def bifoldLeft[A, B, C](fab: Const[A, B], c: C)(f: (C, A) => C, g: (C, B) => C): C =
Expand All @@ -161,10 +171,26 @@ sealed abstract private[data] class ConstInstances extends ConstInstances0 {
): Eval[C] =
f(fab.getConst, c)
}

implicit def catsDataMonoidKForConst[C: Monoid]: MonoidK[Const[C, *]] = new MonoidK[Const[C, *]] {
override def empty[A]: Const[C, A] = Const.empty
override def combineK[A](x: Const[C, A], y: Const[C, A]): Const[C, A] = x.combine(y)
}

implicit def catsDataSemigroupKForConst[C: Semigroup]: SemigroupK[Const[C, *]] = new SemigroupK[Const[C, *]] {
override def combineK[A](x: Const[C, A], y: Const[C, A]): Const[C, A] = x.combine(y)
}
}

sealed abstract private[data] class ConstInstances0 extends ConstInstances1 {

implicit def catsDataEqForConst[A: Eq, B]: Eq[Const[A, B]] = _ === _

implicit def catsDataHashForConst[A: Hash, B]: Hash[Const[A, B]] = new Hash[Const[A, B]] {
override def hash(x: Const[A, B]): Int = x.hash
override def eqv(x: Const[A, B], y: Const[A, B]): Boolean = x === y
}

implicit def catsDataContravariantMonoidalForConst[D: Monoid]: ContravariantMonoidal[Const[D, *]] =
new ContravariantMonoidal[Const[D, *]] {
override def unit = Const.empty[D, Unit]
Expand All @@ -174,44 +200,48 @@ sealed abstract private[data] class ConstInstances0 extends ConstInstances1 {
fa.retag[(A, B)].combine(fb.retag[(A, B)])
}

implicit def catsDataCommutativeApplicativeForConst[C](implicit
C: CommutativeMonoid[C]
): CommutativeApplicative[Const[C, *]] =
new ConstApplicative[C] with CommutativeApplicative[Const[C, *]] { val C0: CommutativeMonoid[C] = C }
implicit def catsDataContravariantSemigroupalForConst[D: Semigroup]: ContravariantSemigroupal[Const[D, *]] =
new ContravariantSemigroupal[Const[D, *]] {
override def contramap[A, B](fa: Const[D, A])(f: B => A): Const[D, B] =
fa.retag[B]
override def product[A, B](fa: Const[D, A], fb: Const[D, B]): Const[D, (A, B)] =
fa.retag[(A, B)].combine(fb.retag[(A, B)])
}
}

sealed abstract private[data] class ConstInstances1 extends ConstInstances2 {

implicit def catsDataCommutativeApplicativeForConst[C](implicit
C: CommutativeMonoid[C]
): CommutativeApplicative[Const[C, *]] =
new ConstApplicative[C] with CommutativeApplicative[Const[C, *]] {
val C0: CommutativeMonoid[C] = C
}

implicit def catsDataCommutativeApplyForConst[C](implicit C: CommutativeSemigroup[C]): CommutativeApply[Const[C, *]] =
new ConstApply[C] with CommutativeApply[Const[C, *]] { val C0: CommutativeSemigroup[C] = C }
}

sealed abstract private[data] class ConstInstances2 extends ConstInstances3 {

implicit def catsDataSemigroupForConst[A: Semigroup, B]: Semigroup[Const[A, B]] = _ combine _

implicit def catsDataPartialOrderForConst[A: PartialOrder, B]: PartialOrder[Const[A, B]] = _ partialCompare _

implicit def catsDataApplicativeForConst[C](implicit C: Monoid[C]): Applicative[Const[C, *]] =
new ConstApplicative[C] { val C0: Monoid[C] = C }
}

sealed abstract private[data] class ConstInstances3 extends ConstInstances4 {
implicit def catsDataEqForConst[A: Eq, B]: Eq[Const[A, B]] = _ === _

implicit def catsDataApplyForConst[C](implicit C: Semigroup[C]): Apply[Const[C, *]] =
new ConstApply[C] { val C0: Semigroup[C] = C }
}

sealed abstract private[data] class ConstInstances4 {
sealed abstract private[data] class ConstInstances3 extends ConstInstances4 {

implicit def catsDataFunctorForConst[C]: Functor[Const[C, *]] =
def catsDataFunctorForConst[C]: Functor[Const[C, *]] =
new ConstFunctor[C] {}

implicit def catsDataContravariantForConst[C]: Contravariant[Const[C, *]] =
new ConstContravariant[C] {}
}

sealed abstract private[data] class ConstInstances4

sealed private[data] trait ConstFunctor[C] extends Functor[Const[C, *]] {
def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] =
fa.retag[B]
Expand Down
43 changes: 35 additions & 8 deletions tests/shared/src/test/scala/cats/tests/ConstSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@ import cats.data.{Const, NonEmptyList}
import cats.kernel.Semigroup
import cats.kernel.laws.discipline.{
EqTests,
HashTests,
LowerBoundedTests,
MonoidTests,
OrderTests,
PartialOrderTests,
SemigroupTests,
UpperBoundedTests
}
import cats.laws.discipline._
import cats.laws.discipline.SemigroupalTests.Isomorphisms
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
import cats.syntax.show._
import cats.tests.Helpers.{CMono, CSemi}
import cats.syntax.eq._
import cats.syntax.show._
import cats.tests.Helpers.{CMono, CSemi, Semi}
import org.scalacheck.Prop._

class ConstSuite extends CatsSuite {
Expand Down Expand Up @@ -100,9 +101,16 @@ class ConstSuite extends CatsSuite {
checkAll("Const[String, Int]", ContravariantTests[Const[String, *]].contravariant[Int, Int, Int])
checkAll("Contravariant[Const[String, *]]", SerializableTests.serializable(Contravariant[Const[String, *]]))

checkAll("Const[String, Int]", ContravariantMonoidalTests[Const[String, *]].contravariantMonoidal[Int, Int, Int])
checkAll("ContravariantMonoidal[Const[String, *]]",
SerializableTests.serializable(ContravariantMonoidal[Const[String, *]])
checkAll("ContravariantMonoidal[Const[Int, *]]",
ContravariantMonoidalTests[Const[Int, *]].contravariantMonoidal[Int, Int, Int]
)
checkAll("ContravariantMonoidal[Const[Int, *]]", SerializableTests.serializable(ContravariantMonoidal[Const[Int, *]]))

checkAll("ContravariantSemigroupal[Const[Semi, *]]",
ContravariantSemigroupalTests[Const[Semi, *]].contravariantSemigroupal[Int, Int, Int]
)
checkAll("ContravariantSemigroupal[Const[Semi, *]]",
SerializableTests.serializable(ContravariantSemigroupal[Const[Semi, *]])
)

checkAll("Const[*, *]", BifoldableTests[Const].bifoldable[Int, Int, Int])
Expand All @@ -120,8 +128,8 @@ class ConstSuite extends CatsSuite {
forAll { (const: Const[Int, String]) =>
assert(const.show.startsWith("Const(") === true)
const.show.contains(const.getConst.show)
assert(const.show === (implicitly[Show[Const[Int, String]]].show(const)))
assert(const.show === (const.retag[Boolean].show))
assert(const.show === implicitly[Show[Const[Int, String]]].show(const))
assert(const.show === const.retag[Boolean].show)
}
}

Expand All @@ -139,4 +147,23 @@ class ConstSuite extends CatsSuite {

checkAll("Const[CSemi, Int]", CommutativeApplyTests[Const[CSemi, *]].commutativeApply[Int, Int, Int])
checkAll("CommutativeApply[Const[CSemi, *]]", SerializableTests.serializable(CommutativeApply[Const[CSemi, *]]))

checkAll("Hash[Const[Int, String]]", HashTests[Const[Int, String]].hash)
checkAll("Hash[Const[Int, String]]", SerializableTests.serializable(Hash[Const[Int, String]]))

checkAll("MonoidK[Const[Int, *]]", MonoidKTests[Const[Int, *]].monoidK[Int])
checkAll("MonoidK[Const[Int, *]]", SerializableTests.serializable(MonoidK[Const[Int, *]]))

checkAll("SemigroupK[Const[Int, *]]", SemigroupKTests[Const[Semi, *]].semigroupK[Int])
checkAll("SemigroupK[Const[Int, *]]", SerializableTests.serializable(SemigroupK[Const[Semi, *]]))
}

object ConstSuite {
def summonInstances[A, B: Hash](): Unit = {
InvariantMonoidal[Const[Int, *]]
Invariant[Const[A, *]]
Functor[Const[A, *]]
Eq[Const[B, Int]]
()
}
}

0 comments on commit 9bb21c1

Please sign in to comment.