From b548226af95f9e5b72f3561a3968e210eaa22a0d Mon Sep 17 00:00:00 2001 From: Brian Holt Date: Thu, 19 Aug 2021 23:54:18 -0500 Subject: [PATCH] make many of the Semigroups and Monoids commutative --- .../eu/timepit/refined/cats/package.scala | 137 ++++++++++++------ ...ommutativeSemigroupAndMonoidLawTests.scala | 132 +++++++++++++++++ .../cats/SemigroupAndMonoidLawTests.scala | 59 -------- 3 files changed, 224 insertions(+), 104 deletions(-) create mode 100644 modules/cats/shared/src/test/scala/eu/timepit/refined/cats/CommutativeSemigroupAndMonoidLawTests.scala delete mode 100644 modules/cats/shared/src/test/scala/eu/timepit/refined/cats/SemigroupAndMonoidLawTests.scala diff --git a/modules/cats/shared/src/main/scala/eu/timepit/refined/cats/package.scala b/modules/cats/shared/src/main/scala/eu/timepit/refined/cats/package.scala index b6c020623..6729a4072 100644 --- a/modules/cats/shared/src/main/scala/eu/timepit/refined/cats/package.scala +++ b/modules/cats/shared/src/main/scala/eu/timepit/refined/cats/package.scala @@ -1,9 +1,9 @@ package eu.timepit.refined -import _root_.cats.{Contravariant, MonadError, Show} +import _root_.cats.{Contravariant, MonadError, Semigroup, Show} import _root_.cats.implicits._ -import _root_.cats.kernel.{Eq, Monoid, Order, Semigroup} -import eu.timepit.refined.api.{Refined, RefType, Validate} +import _root_.cats.kernel.{CommutativeMonoid, CommutativeSemigroup, Eq, Monoid, Order} +import eu.timepit.refined.api.{RefType, Refined, Validate} import eu.timepit.refined.numeric.{Negative, NonNegative, NonPositive, Positive} import eu.timepit.refined.types.numeric._ @@ -30,49 +30,96 @@ package object cats { implicit def refTypeShow[F[_, _], T: Show, P](implicit rt: RefType[F]): Show[F[T, P]] = cats.derivation.refTypeViaContravariant[F, Show, T, P] - // Semigroup instances - implicit val posByteSemigroup: Semigroup[PosByte] = getPosIntegralSemigroup[Byte] - implicit val posShortSemigroup: Semigroup[PosShort] = getPosIntegralSemigroup[Short] - implicit val posIntSemigroup: Semigroup[PosInt] = getPosIntegralSemigroup[Int] - implicit val posLongSemigroup: Semigroup[PosLong] = getPosIntegralSemigroup[Long] - implicit val posFloatSemigroup: Semigroup[PosFloat] = getSemigroup[Float, Positive] - implicit val posDoubleSemigroup: Semigroup[PosDouble] = getSemigroup[Double, Positive] - - implicit val negByteSemigroup: Semigroup[NegByte] = getNegIntegralSemigroup[Byte] - implicit val negShortSemigroup: Semigroup[NegShort] = getNegIntegralSemigroup[Short] - implicit val negIntSemigroup: Semigroup[NegInt] = getNegIntegralSemigroup[Int] - implicit val negLongSemigroup: Semigroup[NegLong] = getNegIntegralSemigroup[Long] - implicit val negFloatSemigroup: Semigroup[NegFloat] = getSemigroup[Float, Negative] - implicit val negDoubleSemigroup: Semigroup[NegDouble] = getSemigroup[Double, Negative] + // CommutativeSemigroup instances + implicit val posByteCommutativeSemigroup: CommutativeSemigroup[PosByte] = + getPosIntegralCommutativeSemigroup[Byte] + implicit val posShortCommutativeSemigroup: CommutativeSemigroup[PosShort] = + getPosIntegralCommutativeSemigroup[Short] + implicit val posIntCommutativeSemigroup: CommutativeSemigroup[PosInt] = + getPosIntegralCommutativeSemigroup[Int] + implicit val posLongCommutativeSemigroup: CommutativeSemigroup[PosLong] = + getPosIntegralCommutativeSemigroup[Long] + implicit val posFloatCommutativeSemigroup: CommutativeSemigroup[PosFloat] = + getCommutativeSemigroup[Float, Positive] + implicit val posDoubleCommutativeSemigroup: CommutativeSemigroup[PosDouble] = + getCommutativeSemigroup[Double, Positive] + + implicit val negByteCommutativeSemigroup: CommutativeSemigroup[NegByte] = + getNegIntegralCommutativeSemigroup[Byte] + implicit val negShortCommutativeSemigroup: CommutativeSemigroup[NegShort] = + getNegIntegralCommutativeSemigroup[Short] + implicit val negIntCommutativeSemigroup: CommutativeSemigroup[NegInt] = + getNegIntegralCommutativeSemigroup[Int] + implicit val negLongCommutativeSemigroup: CommutativeSemigroup[NegLong] = + getNegIntegralCommutativeSemigroup[Long] + implicit val negFloatCommutativeSemigroup: CommutativeSemigroup[NegFloat] = + getCommutativeSemigroup[Float, Negative] + implicit val negDoubleCommutativeSemigroup: CommutativeSemigroup[NegDouble] = + getCommutativeSemigroup[Double, Negative] // Monoid instances - implicit val nonNegByteMonoid: Monoid[NonNegByte] = getNonNegIntegralMonoid[Byte] - implicit val nonNegShortMonoid: Monoid[NonNegShort] = getNonNegIntegralMonoid[Short] - implicit val nonNegIntMonoid: Monoid[NonNegInt] = getNonNegIntegralMonoid[Int] - implicit val nonNegLongMonoid: Monoid[NonNegLong] = getNonNegIntegralMonoid[Long] - implicit val nonNegFloatMonoid: Monoid[NonNegFloat] = getMonoid[Float, NonNegative] - implicit val nonNegDoubleMonoid: Monoid[NonNegDouble] = getMonoid[Double, NonNegative] - - implicit val nonPosFloatMonoid: Monoid[NonPosFloat] = getMonoid[Float, NonPositive] - implicit val nonPosDoubleMonoid: Monoid[NonPosDouble] = getMonoid[Double, NonPositive] - - private def getPosIntegralSemigroup[A: Semigroup: NonNegShift](implicit + implicit val nonNegByteCommutativeMonoid: CommutativeMonoid[NonNegByte] = + getNonNegIntegralCommutativeMonoid[Byte] + implicit val nonNegShortCommutativeMonoid: CommutativeMonoid[NonNegShort] = + getNonNegIntegralCommutativeMonoid[Short] + implicit val nonNegIntCommutativeMonoid: CommutativeMonoid[NonNegInt] = + getNonNegIntegralCommutativeMonoid[Int] + implicit val nonNegLongCommutativeMonoid: CommutativeMonoid[NonNegLong] = + getNonNegIntegralCommutativeMonoid[Long] + implicit val nonNegFloatCommutativeMonoid: CommutativeMonoid[NonNegFloat] = + getCommutativeMonoid[Float, NonNegative] + implicit val nonNegDoubleCommutativeMonoid: CommutativeMonoid[NonNegDouble] = + getCommutativeMonoid[Double, NonNegative] + + implicit val nonPosFloatCommutativeMonoid: CommutativeMonoid[NonPosFloat] = + getCommutativeMonoid[Float, NonPositive] + implicit val nonPosDoubleCommutativeMonoid: CommutativeMonoid[NonPosDouble] = + getCommutativeMonoid[Double, NonPositive] + + // Semigroup instances retained for binary compatibility + val posByteSemigroup: Semigroup[PosByte] = posByteCommutativeSemigroup + val posShortSemigroup: Semigroup[PosShort] = posShortCommutativeSemigroup + val posIntSemigroup: Semigroup[PosInt] = posIntCommutativeSemigroup + val posLongSemigroup: Semigroup[PosLong] = posLongCommutativeSemigroup + val posFloatSemigroup: Semigroup[PosFloat] = posFloatCommutativeSemigroup + val posDoubleSemigroup: Semigroup[PosDouble] = posDoubleCommutativeSemigroup + + val negByteSemigroup: Semigroup[NegByte] = negByteCommutativeSemigroup + val negShortSemigroup: Semigroup[NegShort] = negShortCommutativeSemigroup + val negIntSemigroup: Semigroup[NegInt] = negIntCommutativeSemigroup + val negLongSemigroup: Semigroup[NegLong] = negLongCommutativeSemigroup + val negFloatSemigroup: Semigroup[NegFloat] = negFloatCommutativeSemigroup + val negDoubleSemigroup: Semigroup[NegDouble] = negDoubleCommutativeSemigroup + + // Monoid instances retained for binary compatibility + val nonNegByteMonoid: Monoid[NonNegByte] = nonNegByteCommutativeMonoid + val nonNegShortMonoid: Monoid[NonNegShort] = nonNegShortCommutativeMonoid + val nonNegIntMonoid: Monoid[NonNegInt] = nonNegIntCommutativeMonoid + val nonNegLongMonoid: Monoid[NonNegLong] = nonNegLongCommutativeMonoid + val nonNegFloatMonoid: Monoid[NonNegFloat] = nonNegFloatCommutativeMonoid + val nonNegDoubleMonoid: Monoid[NonNegDouble] = nonNegDoubleCommutativeMonoid + + val nonPosFloatMonoid: Monoid[NonPosFloat] = nonPosFloatCommutativeMonoid + val nonPosDoubleMonoid: Monoid[NonPosDouble] = nonPosDoubleCommutativeMonoid + + private def getPosIntegralCommutativeSemigroup[A: CommutativeSemigroup: NonNegShift](implicit integral: Integral[A], v: Validate[A, Positive] - ): Semigroup[A Refined Positive] = - Semigroup.instance { (x, y) => + ): CommutativeSemigroup[A Refined Positive] = + CommutativeSemigroup.instance { (x, y) => val combined: A = x.value |+| y.value refineV[Positive](combined).getOrElse { - val result: A = Semigroup[A].combine(NonNegShift[A].shift(combined), integral.one) + val result: A = + CommutativeSemigroup[A].combine(NonNegShift[A].shift(combined), integral.one) refineV[Positive].unsafeFrom(result) } } - private def getNegIntegralSemigroup[A: Integral: Semigroup: NegShift](implicit - v: Validate[A, Negative] - ): Semigroup[A Refined Negative] = - Semigroup.instance { (x, y) => + private def getNegIntegralCommutativeSemigroup[A: Integral: CommutativeSemigroup: NegShift]( + implicit v: Validate[A, Negative] + ): CommutativeSemigroup[A Refined Negative] = + CommutativeSemigroup.instance { (x, y) => val combined: A = x.value |+| y.value refineV[Negative](combined).getOrElse { @@ -81,15 +128,15 @@ package object cats { } } - private def getSemigroup[A: Semigroup, P](implicit + private def getCommutativeSemigroup[A: CommutativeSemigroup, P](implicit v: Validate[A, P] - ): Semigroup[A Refined P] = - Semigroup.instance((x, y) => refineV[P].unsafeFrom(x.value |+| y.value)) + ): CommutativeSemigroup[A Refined P] = + CommutativeSemigroup.instance((x, y) => refineV[P].unsafeFrom(x.value |+| y.value)) - private def getNonNegIntegralMonoid[A: Integral: Monoid: NonNegShift](implicit - v: Validate[A, NonNegative] - ): Monoid[A Refined NonNegative] = - new Monoid[A Refined NonNegative] { + private def getNonNegIntegralCommutativeMonoid[A: Integral: CommutativeMonoid: NonNegShift]( + implicit v: Validate[A, NonNegative] + ): CommutativeMonoid[A Refined NonNegative] = + new CommutativeMonoid[A Refined NonNegative] { override def empty: A Refined NonNegative = refineV[NonNegative].unsafeFrom(Monoid[A].empty) override def combine( @@ -105,10 +152,10 @@ package object cats { } } - private def getMonoid[A: Monoid, P](implicit + private def getCommutativeMonoid[A: CommutativeMonoid, P](implicit v: Validate[A, P] - ): Monoid[A Refined P] = - new Monoid[A Refined P] { + ): CommutativeMonoid[A Refined P] = + new CommutativeMonoid[A Refined P] { override def empty: A Refined P = refineV[P].unsafeFrom(Monoid[A].empty) override def combine(x: A Refined P, y: A Refined P): A Refined P = diff --git a/modules/cats/shared/src/test/scala/eu/timepit/refined/cats/CommutativeSemigroupAndMonoidLawTests.scala b/modules/cats/shared/src/test/scala/eu/timepit/refined/cats/CommutativeSemigroupAndMonoidLawTests.scala new file mode 100644 index 000000000..2e3a4fac1 --- /dev/null +++ b/modules/cats/shared/src/test/scala/eu/timepit/refined/cats/CommutativeSemigroupAndMonoidLawTests.scala @@ -0,0 +1,132 @@ +package eu.timepit.refined.cats +import cats.kernel.{CommutativeMonoid, CommutativeSemigroup} +import cats.kernel.laws.discipline.{ + CommutativeMonoidTests, + CommutativeSemigroupTests, + SerializableTests +} +import eu.timepit.refined.scalacheck.numeric._ +import eu.timepit.refined.types.numeric._ +import org.scalatest.funsuite.AnyFunSuite +import org.scalatestplus.scalacheck.Checkers +import org.typelevel.discipline.scalatest.FunSuiteDiscipline + +class CommutativeSemigroupAndMonoidLawTests + extends AnyFunSuite + with FunSuiteDiscipline + with Checkers { + // Positive commutativeSemigroups + checkAll("CommutativeSemigroup[PosByte]", CommutativeSemigroupTests[PosByte].commutativeSemigroup) + checkAll( + "CommutativeSemigroup[PosByte]", + SerializableTests.serializable(CommutativeSemigroup[PosByte]) + ) + checkAll( + "CommutativeSemigroup[PosShort]", + CommutativeSemigroupTests[PosShort].commutativeSemigroup + ) + checkAll( + "CommutativeSemigroup[PosShort]", + SerializableTests.serializable(CommutativeSemigroup[PosShort]) + ) + checkAll("CommutativeSemigroup[PosInt]", CommutativeSemigroupTests[PosInt].commutativeSemigroup) + checkAll( + "CommutativeSemigroup[PosInt]", + SerializableTests.serializable(CommutativeSemigroup[PosInt]) + ) + checkAll("CommutativeSemigroup[PosLong]", CommutativeSemigroupTests[PosLong].commutativeSemigroup) + checkAll( + "CommutativeSemigroup[PosLong]", + SerializableTests.serializable(CommutativeSemigroup[PosLong]) + ) + // checkAll("CommutativeSemigroup[PosFloat]", CommutativeSemigroupTests[PosFloat].commutativeSemigroup) // approximately associative + checkAll( + "CommutativeSemigroup[PosFloat]", + SerializableTests.serializable(CommutativeSemigroup[PosFloat]) + ) + // checkAll("CommutativeSemigroup[PosDouble]", CommutativeSemigroupTests[PosDouble].commutativeSemigroup) // approximately associative + checkAll( + "CommutativeSemigroup[PosDouble]", + SerializableTests.serializable(CommutativeSemigroup[PosDouble]) + ) + + // Negative commutativeSemigroups + checkAll("CommutativeSemigroup[NegByte]", CommutativeSemigroupTests[NegByte].commutativeSemigroup) + checkAll( + "CommutativeSemigroup[NegByte]", + SerializableTests.serializable(CommutativeSemigroup[NegByte]) + ) + checkAll( + "CommutativeSemigroup[NegShort]", + CommutativeSemigroupTests[NegShort].commutativeSemigroup + ) + checkAll( + "CommutativeSemigroup[NegShort]", + SerializableTests.serializable(CommutativeSemigroup[NegShort]) + ) + checkAll("CommutativeSemigroup[NegInt]", CommutativeSemigroupTests[NegInt].commutativeSemigroup) + checkAll( + "CommutativeSemigroup[NegInt]", + SerializableTests.serializable(CommutativeSemigroup[NegInt]) + ) + checkAll("CommutativeSemigroup[NegLong]", CommutativeSemigroupTests[NegLong].commutativeSemigroup) + checkAll( + "CommutativeSemigroup[NegLong]", + SerializableTests.serializable(CommutativeSemigroup[NegLong]) + ) + // checkAll("CommutativeSemigroup[NegFloat]", CommutativeSemigroupTests[NegFloat].commutativeSemigroup) // approximately associative + checkAll( + "CommutativeSemigroup[NegFloat]", + SerializableTests.serializable(CommutativeSemigroup[NegFloat]) + ) + // checkAll("CommutativeSemigroup[NegDouble]", CommutativeSemigroupTests[NegDouble].commutativeSemigroup) // approximately associative + checkAll( + "CommutativeSemigroup[NegDouble]", + SerializableTests.serializable(CommutativeSemigroup[NegDouble]) + ) + + // NonNegative monoids + checkAll("CommutativeMonoid[NonNegByte]", CommutativeMonoidTests[NonNegByte].commutativeMonoid) + checkAll( + "CommutativeMonoid[NonNegByte]", + SerializableTests.serializable(CommutativeMonoid[NonNegByte]) + ) + checkAll("CommutativeMonoid[NonNegShort]", CommutativeMonoidTests[NonNegShort].commutativeMonoid) + checkAll( + "CommutativeMonoid[NonNegShort]", + SerializableTests.serializable(CommutativeMonoid[NonNegShort]) + ) + checkAll("CommutativeMonoid[NonNegInt]", CommutativeMonoidTests[NonNegInt].commutativeMonoid) + checkAll( + "CommutativeMonoid[NonNegInt]", + SerializableTests.serializable(CommutativeMonoid[NonNegInt]) + ) + checkAll("CommutativeMonoid[NonNegLong]", CommutativeMonoidTests[NonNegLong].commutativeMonoid) + checkAll( + "CommutativeMonoid[NonNegLong]", + SerializableTests.serializable(CommutativeMonoid[NonNegLong]) + ) + // checkAll("Monoid[NonNegFloat]", CommutativeMonoidTests[NonNegFloat].commutativeMonoid) // approximately associative + checkAll( + "CommutativeMonoid[NonNegFloat]", + SerializableTests.serializable(CommutativeMonoid[NonNegFloat]) + ) + // checkAll("Monoid[NonNegDouble]", CommutativeMonoidTests[NonNegDouble].commutativeMonoid) // approximately associative + checkAll( + "CommutativeMonoid[NonNegDouble]", + SerializableTests.serializable(CommutativeMonoid[NonNegDouble]) + ) + + // NonPositive monoids + // checkAll("Monoid[NonPosFloat]", CommutativeMonoidTests[NonPosFloat].commutativeMonoid) // approximately associative + checkAll( + "CommutativeMonoid[NonPosFloat]", + SerializableTests.serializable(CommutativeMonoid[NonPosFloat]) + ) + // checkAll("Monoid[NonPosDouble]", CommutativeMonoidTests[NonPosDouble].commutativeMonoid) // approximately associative + checkAll( + "CommutativeMonoid[NonPosDouble]", + SerializableTests.serializable(CommutativeMonoid[NonPosDouble]) + ) + +} diff --git a/modules/cats/shared/src/test/scala/eu/timepit/refined/cats/SemigroupAndMonoidLawTests.scala b/modules/cats/shared/src/test/scala/eu/timepit/refined/cats/SemigroupAndMonoidLawTests.scala deleted file mode 100644 index 78d303c50..000000000 --- a/modules/cats/shared/src/test/scala/eu/timepit/refined/cats/SemigroupAndMonoidLawTests.scala +++ /dev/null @@ -1,59 +0,0 @@ -package eu.timepit.refined.cats -import cats.kernel.{Monoid, Semigroup} -import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests, SerializableTests} -import eu.timepit.refined.scalacheck.numeric._ -import eu.timepit.refined.types.numeric._ -import org.scalatest.funsuite.AnyFunSuite -import org.scalatestplus.scalacheck.Checkers -import org.typelevel.discipline.scalatest.FunSuiteDiscipline - -class SemigroupAndMonoidLawTests extends AnyFunSuite with FunSuiteDiscipline with Checkers { - // Positive semigroups - checkAll("Semigroup[PosByte]", SemigroupTests[PosByte].semigroup) - checkAll("Semigroup[PosByte]", SerializableTests.serializable(Semigroup[PosByte])) - checkAll("Semigroup[PosShort]", SemigroupTests[PosShort].semigroup) - checkAll("Semigroup[PosShort]", SerializableTests.serializable(Semigroup[PosShort])) - checkAll("Semigroup[PosInt]", SemigroupTests[PosInt].semigroup) - checkAll("Semigroup[PosInt]", SerializableTests.serializable(Semigroup[PosInt])) - checkAll("Semigroup[PosLong]", SemigroupTests[PosLong].semigroup) - checkAll("Semigroup[PosLong]", SerializableTests.serializable(Semigroup[PosLong])) - // checkAll("Semigroup[PosFloat]", SemigroupTests[PosFloat].semigroup) // approximately associative - checkAll("Semigroup[PosFloat]", SerializableTests.serializable(Semigroup[PosFloat])) - // checkAll("Semigroup[PosDouble]", SemigroupTests[PosDouble].semigroup) // approximately associative - checkAll("Semigroup[PosDouble]", SerializableTests.serializable(Semigroup[PosDouble])) - - // Negative semigroups - checkAll("Semigroup[NegByte]", SemigroupTests[NegByte].semigroup) - checkAll("Semigroup[NegByte]", SerializableTests.serializable(Semigroup[NegByte])) - checkAll("Semigroup[NegShort]", SemigroupTests[NegShort].semigroup) - checkAll("Semigroup[NegShort]", SerializableTests.serializable(Semigroup[NegShort])) - checkAll("Semigroup[NegInt]", SemigroupTests[NegInt].semigroup) - checkAll("Semigroup[NegInt]", SerializableTests.serializable(Semigroup[NegInt])) - checkAll("Semigroup[NegLong]", SemigroupTests[NegLong].semigroup) - checkAll("Semigroup[NegLong]", SerializableTests.serializable(Semigroup[NegLong])) - // checkAll("Semigroup[NegFloat]", SemigroupTests[NegFloat].semigroup) // approximately associative - checkAll("Semigroup[NegFloat]", SerializableTests.serializable(Semigroup[NegFloat])) - // checkAll("Semigroup[NegDouble]", SemigroupTests[NegDouble].semigroup) // approximately associative - checkAll("Semigroup[NegDouble]", SerializableTests.serializable(Semigroup[NegDouble])) - - // NonNegative monoids - checkAll("Monoid[NonNegByte]", MonoidTests[NonNegByte].monoid) - checkAll("Monoid[NonNegByte]", SerializableTests.serializable(Monoid[NonNegByte])) - checkAll("Monoid[NonNegShort]", MonoidTests[NonNegShort].monoid) - checkAll("Monoid[NonNegShort]", SerializableTests.serializable(Monoid[NonNegShort])) - checkAll("Monoid[NonNegInt]", MonoidTests[NonNegInt].monoid) - checkAll("Monoid[NonNegInt]", SerializableTests.serializable(Monoid[NonNegInt])) - checkAll("Monoid[NonNegLong]", MonoidTests[NonNegLong].monoid) - checkAll("Monoid[NonNegLong]", SerializableTests.serializable(Monoid[NonNegLong])) - // checkAll("Monoid[NonNegFloat]", MonoidTests[NonNegFloat].monoid) // approximately associative - checkAll("Monoid[NonNegFloat]", SerializableTests.serializable(Monoid[NonNegFloat])) - // checkAll("Monoid[NonNegDouble]", MonoidTests[NonNegDouble].monoid) // approximately associative - checkAll("Monoid[NonNegDouble]", SerializableTests.serializable(Monoid[NonNegDouble])) - - // NonPositive monoids - // checkAll("Monoid[NonPosFloat]", MonoidTests[NonPosFloat].monoid) // approximately associative - checkAll("Monoid[NonPosFloat]", SerializableTests.serializable(Monoid[NonPosFloat])) - // checkAll("Monoid[NonPosDouble]", MonoidTests[NonPosDouble].monoid) // approximately associative - checkAll("Monoid[NonPosDouble]", SerializableTests.serializable(Monoid[NonPosDouble])) - -}