Skip to content

Commit

Permalink
Derive Band, Semilattice and BoundedSemilattice on Scala 3 (#645)
Browse files Browse the repository at this point in the history
* Derive Band on Scala 3

* Derive Semilattice on Scala 3

* Derive BoundedSemilattice on Scala 3
  • Loading branch information
joroKr21 authored Feb 4, 2024
1 parent f518ce9 commit 1f11a09
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 1 deletion.
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

0 comments on commit 1f11a09

Please sign in to comment.