Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derive Band, Semilattice and BoundedSemilattice on Scala 3 #645

Merged
merged 3 commits into from
Feb 4, 2024
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
32 changes: 32 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedBand.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cats.derived

import cats.kernel.Band
import shapeless3.deriving.K0.*

import scala.annotation.*
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Band[A] where A = ${A}.
Make sure that A is a case class where all fields have a Band instance.""")
type DerivedBand[A] = Derived[Band[A]]
object DerivedBand:
type Or[A] = Derived.Or[Band[A]]

@nowarn("msg=unused import")
inline def apply[A]: Band[A] =
import DerivedBand.given
summonInline[DerivedBand[A]].instance

@nowarn("msg=unused import")
inline def strict[A]: Band[A] =
import Strict.given
summonInline[DerivedBand[A]].instance

given product[A](using inst: => ProductInstances[Or, A]): DerivedBand[A] =
Strict.product(using inst.unify)

trait Product[F[x] <: Band[x], A: ProductInstancesOf[F]] extends DerivedSemigroup.Product[F, A], Band[A]

object Strict:
given product[A: ProductInstancesOf[Band]]: DerivedBand[A] =
new Product[Band, A] {}
34 changes: 34 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedBoundedSemilattice.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cats.derived

import cats.kernel.BoundedSemilattice
import shapeless3.deriving.K0.*

import scala.annotation.*
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of BoundedSemilattice[A] where A = ${A}.
Make sure that A is a case class where all fields have a BoundedSemilattice instance.""")
type DerivedBoundedSemilattice[A] = Derived[BoundedSemilattice[A]]
object DerivedBoundedSemilattice:
type Or[A] = Derived.Or[BoundedSemilattice[A]]

@nowarn("msg=unused import")
inline def apply[A]: BoundedSemilattice[A] =
import DerivedBoundedSemilattice.given
summonInline[DerivedBoundedSemilattice[A]].instance

@nowarn("msg=unused import")
inline def strict[A]: BoundedSemilattice[A] =
import Strict.given
summonInline[DerivedBoundedSemilattice[A]].instance

given product[A](using inst: => ProductInstances[Or, A]): DerivedBoundedSemilattice[A] =
Strict.product(using inst.unify)

trait Product[F[x] <: BoundedSemilattice[x], A: ProductInstancesOf[F]]
extends DerivedCommutativeMonoid.Product[F, A],
BoundedSemilattice[A]

object Strict:
given product[A: ProductInstancesOf[BoundedSemilattice]]: DerivedBoundedSemilattice[A] =
new Product[BoundedSemilattice, A] {}
34 changes: 34 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedSemilattice.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cats.derived

import cats.kernel.Semilattice
import shapeless3.deriving.K0.*

import scala.annotation.*
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Semilattice[A] where A = ${A}.
Make sure that A is a case class where all fields have a Semilattice instance.""")
type DerivedSemilattice[A] = Derived[Semilattice[A]]
object DerivedSemilattice:
type Or[A] = Derived.Or[Semilattice[A]]

@nowarn("msg=unused import")
inline def apply[A]: Semilattice[A] =
import DerivedSemilattice.given
summonInline[DerivedSemilattice[A]].instance

@nowarn("msg=unused import")
inline def strict[A]: Semilattice[A] =
import Strict.given
summonInline[DerivedSemilattice[A]].instance

given product[A](using inst: => ProductInstances[Or, A]): DerivedSemilattice[A] =
Strict.product(using inst.unify)

trait Product[F[x] <: Semilattice[x], A: ProductInstancesOf[F]]
extends DerivedCommutativeSemigroup.Product[F, A],
Semilattice[A]

object Strict:
given product[A: ProductInstancesOf[Semilattice]]: DerivedSemilattice[A] =
new Product[Semilattice, A] {}
24 changes: 23 additions & 1 deletion core/src/main/scala-3/cats/derived/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cats.derived

import alleycats.*
import cats.*
import cats.kernel.{CommutativeGroup, CommutativeMonoid, CommutativeSemigroup}
import cats.kernel.{Band, BoundedSemilattice, CommutativeGroup, CommutativeMonoid, CommutativeSemigroup, Semilattice}

import scala.util.NotGiven

Expand All @@ -12,10 +12,13 @@ extension (x: Empty.type) inline def derived[A]: Empty[A] = DerivedEmpty[A]
extension (x: Semigroup.type) inline def derived[A]: Semigroup[A] = DerivedSemigroup[A]
extension (x: Monoid.type) inline def derived[A]: Monoid[A] = DerivedMonoid[A]
extension (x: Group.type) inline def derived[A]: Group[A] = DerivedGroup[A]
extension (x: Band.type) inline def derived[A]: Band[A] = DerivedBand[A]
extension (x: Order.type) inline def derived[A]: Order[A] = DerivedOrder[A]
extension (x: CommutativeSemigroup.type) inline def derived[A]: CommutativeSemigroup[A] = DerivedCommutativeSemigroup[A]
extension (x: CommutativeMonoid.type) inline def derived[A]: CommutativeMonoid[A] = DerivedCommutativeMonoid[A]
extension (x: CommutativeGroup.type) inline def derived[A]: CommutativeGroup[A] = DerivedCommutativeGroup[A]
extension (x: Semilattice.type) inline def derived[A]: Semilattice[A] = DerivedSemilattice[A]
extension (x: BoundedSemilattice.type) inline def derived[A]: BoundedSemilattice[A] = DerivedBoundedSemilattice[A]
extension (x: Show.type) inline def derived[A]: Show[A] = DerivedShow[A]
extension (x: Applicative.type) inline def derived[F[_]]: Applicative[F] = DerivedApplicative[F]
extension (x: Apply.type) inline def derived[F[_]]: Apply[F] = DerivedApply[F]
Expand All @@ -42,10 +45,13 @@ object semiauto:
inline def semigroup[A]: Semigroup[A] = DerivedSemigroup[A]
inline def monoid[A]: Monoid[A] = DerivedMonoid[A]
inline def group[A]: Group[A] = DerivedGroup[A]
inline def band[A]: Band[A] = DerivedBand[A]
inline def order[A]: Order[A] = DerivedOrder[A]
inline def commutativeSemigroup[A]: CommutativeSemigroup[A] = DerivedCommutativeSemigroup[A]
inline def commutativeMonoid[A]: CommutativeMonoid[A] = DerivedCommutativeMonoid[A]
inline def commutativeGroup[A]: CommutativeGroup[A] = DerivedCommutativeGroup[A]
inline def semilattice[A]: Semilattice[A] = DerivedSemilattice[A]
inline def boundedSemilattice[A]: BoundedSemilattice[A] = DerivedBoundedSemilattice[A]
inline def applicative[F[_]]: Applicative[F] = DerivedApplicative[F]
inline def apply[F[_]]: Apply[F] = DerivedApply[F]
inline def nonEmptyAlternative[F[_]]: NonEmptyAlternative[F] = DerivedNonEmptyAlternative[F]
Expand Down Expand Up @@ -76,10 +82,14 @@ object strict:
extension (x: Semigroup.type) inline def derived[A]: Semigroup[A] = DerivedSemigroup.strict[A]
extension (x: Monoid.type) inline def derived[A]: Monoid[A] = DerivedMonoid.strict[A]
extension (x: Group.type) inline def derived[A]: Group[A] = DerivedGroup.strict[A]
extension (x: Band.type) inline def derived[A]: Band[A] = DerivedBand.strict[A]
extension (x: CommutativeSemigroup.type)
inline def derived[A]: CommutativeSemigroup[A] = DerivedCommutativeSemigroup.strict[A]
extension (x: CommutativeMonoid.type) inline def derived[A]: CommutativeMonoid[A] = DerivedCommutativeMonoid.strict[A]
extension (x: CommutativeGroup.type) inline def derived[A]: CommutativeGroup[A] = DerivedCommutativeGroup.strict[A]
extension (x: Semilattice.type) inline def derived[A]: Semilattice[A] = DerivedSemilattice.strict[A]
extension (x: BoundedSemilattice.type)
inline def derived[A]: BoundedSemilattice[A] = DerivedBoundedSemilattice.strict[A]
extension (x: EmptyK.type) inline def derived[F[_]]: EmptyK[F] = DerivedEmptyK.strict[F]
extension (x: SemigroupK.type) inline def derived[F[_]]: SemigroupK[F] = DerivedSemigroupK.strict[F]
extension (x: MonoidK.type) inline def derived[F[_]]: MonoidK[F] = DerivedMonoidK.strict[F]
Expand Down Expand Up @@ -108,9 +118,12 @@ object strict:
inline def semigroup[A]: Semigroup[A] = DerivedSemigroup.strict[A]
inline def monoid[A]: Monoid[A] = DerivedMonoid.strict[A]
inline def group[A]: Group[A] = DerivedGroup.strict[A]
inline def band[A]: Band[A] = DerivedBand.strict[A]
inline def commutativeSemigroup[A]: CommutativeSemigroup[A] = DerivedCommutativeSemigroup.strict[A]
inline def commutativeMonoid[A]: CommutativeMonoid[A] = DerivedCommutativeMonoid.strict[A]
inline def commutativeGroup[A]: CommutativeGroup[A] = DerivedCommutativeGroup.strict[A]
inline def semilattice[A]: Semilattice[A] = DerivedSemilattice.strict[A]
inline def boundedSemilattice[A]: BoundedSemilattice[A] = DerivedBoundedSemilattice.strict[A]
inline def emptyK[F[_]]: EmptyK[F] = DerivedEmptyK.strict[F]
inline def semigroupK[F[_]]: SemigroupK[F] = DerivedSemigroupK.strict[F]
inline def monoidK[F[_]]: MonoidK[F] = DerivedMonoidK.strict[F]
Expand Down Expand Up @@ -149,6 +162,9 @@ object auto:
object group:
inline given [A: NotGivenA[Group]]: Group[A] = DerivedGroup[A]

object band:
inline given [A: NotGivenA[Band]]: Band[A] = DerivedBand[A]

object order:
inline given [A: NotGivenA[Order]]: Order[A] = DerivedOrder[A]

Expand All @@ -161,6 +177,12 @@ object auto:
object commutativeGroup:
inline given [A: NotGivenA[CommutativeGroup]]: CommutativeGroup[A] = DerivedCommutativeGroup[A]

object semilattice:
inline given [A: NotGivenA[Semilattice]]: Semilattice[A] = DerivedSemilattice[A]

object boundedSemilattice:
inline given [A: NotGivenA[BoundedSemilattice]]: BoundedSemilattice[A] = DerivedBoundedSemilattice[A]

object show:
inline given [A: NotGivenA[Show]]: Show[A] = DerivedShow[A]

Expand Down
8 changes: 8 additions & 0 deletions core/src/test/scala-3/cats/derived/ADTs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.scalacheck.rng.Seed
import org.scalacheck.{Arbitrary, Cogen, Gen}

import scala.annotation.tailrec
import scala.collection.immutable.BitSet
import scala.concurrent.duration.Duration

object ADTs:
Expand Down Expand Up @@ -288,6 +289,13 @@ object ADTs:
case class Slice(count: Long, percentile: Double, duration: Duration)
case class Compared(x: Slice, y: Slice)

case class Masked[A](mask: BitSet, values: Set[A])
object Masked:
given [A: Arbitrary]: Arbitrary[Masked[A]] = Arbitrary(for
mask <- Gen.buildableOf[BitSet, Int](Gen.oneOf(Gen.const(0), Gen.posNum[Int]))
values <- Arbitrary.arbitrary[Set[A]]
yield Masked(mask, values))

trait EqInstances:
import ADTs.*

Expand Down
68 changes: 68 additions & 0 deletions core/src/test/scala-3/cats/derived/BandSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2016 Miles Sabin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cats.derived

import cats.kernel.Band
import cats.kernel.laws.discipline.{BandTests, SerializableTests}

import scala.compiletime.*

class BandSuite extends KittensSuite:
import ADTs.*
import BandSuite.*

inline def tests[A]: BandTests[A] =
BandTests[A](using summonInline)

inline def validate(inline instance: String): Unit =
checkAll(s"$instance[Masked[String]]", tests[Masked[String]].band)
checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Band[Masked[String]]]))

locally:
import auto.band.given
validate("auto.band")

locally:
import semiInstances.given
validate("semiauto.band")

locally:
import strictInstances.given
validate("strict.semiauto.band")
testNoInstance("strict.semiauto.band", "Top")

locally:
import derivedInstances.*
val instance = "derived.band"
checkAll(s"$instance[Masked]]", tests[Masked].band)
checkAll(s"$instance is Serializable", SerializableTests.serializable(Band[Masked]))

end BandSuite

object BandSuite:
import ADTs.*

object semiInstances:
given Band[Masked[String]] = semiauto.band

object strictInstances:
given Band[Masked[String]] = strict.semiauto.band

object derivedInstances:
case class Masked(x: ADTs.Masked[String]) derives Band

end BandSuite
71 changes: 71 additions & 0 deletions core/src/test/scala-3/cats/derived/BoundedSemilatticeSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2016 Miles Sabin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cats.derived

import cats.kernel.BoundedSemilattice
import cats.kernel.laws.discipline.{BoundedSemilatticeTests, SerializableTests}

import scala.compiletime.*

class BoundedSemilatticeSuite extends KittensSuite:
import ADTs.*
import BoundedSemilatticeSuite.*

inline def tests[A]: BoundedSemilatticeTests[A] =
BoundedSemilatticeTests[A](using summonInline)

inline def validate(inline instance: String): Unit =
checkAll(s"$instance[Masked[String]]", tests[Masked[String]].boundedSemilattice)
checkAll(
s"$instance is Serializable",
SerializableTests.serializable(summonInline[BoundedSemilattice[Masked[String]]])
)

locally:
import auto.boundedSemilattice.given
validate("auto.boundedSemilattice")

locally:
import semiInstances.given
validate("semiauto.boundedSemilattice")

locally:
import strictInstances.given
validate("strict.semiauto.boundedSemilattice")
testNoInstance("strict.semiauto.boundedSemilattice", "Top")

locally:
import derivedInstances.*
val instance = "derived.boundedSemilattice"
checkAll(s"$instance[Masked]]", tests[Masked].boundedSemilattice)
checkAll(s"$instance is Serializable", SerializableTests.serializable(BoundedSemilattice[Masked]))

end BoundedSemilatticeSuite

object BoundedSemilatticeSuite:
import ADTs.*

object semiInstances:
given BoundedSemilattice[Masked[String]] = semiauto.boundedSemilattice

object strictInstances:
given BoundedSemilattice[Masked[String]] = strict.semiauto.boundedSemilattice

object derivedInstances:
case class Masked(x: ADTs.Masked[String]) derives BoundedSemilattice

end BoundedSemilatticeSuite
Loading