-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Port Semigroupk/Monoidk to new scheme (#472)
* Port SemigroupK and MonoidK to new derivation scheme * WIP port of SemigroupK suite to scala 3 * Nested derivations for SemigroupK * Priority for derived SemigroupK given instances * Port scala 2 MonoidK tests to scala 3 * Various improvements - ImplicitNotFound error for SemigroupK/MonoidK - Replace given priority via traits with NotGiven * Use inline in tests * SemigroupK/MonoidK test for derives syntax
- Loading branch information
1 parent
8f76c24
commit 55bad35
Showing
9 changed files
with
233 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package cats.derived | ||
|
||
import cats.* | ||
import shapeless3.deriving.{Const, K1} | ||
|
||
import scala.annotation.implicitNotFound | ||
import scala.compiletime.* | ||
import scala.util.NotGiven | ||
|
||
@implicitNotFound("""Could not derive an instance of MonoidK[F] where F = ${F}. | ||
Make sure that F[_] satisfies one of the following conditions: | ||
* it is a constant type [x] =>> T where T: Monoid | ||
* it is a nested type [x] =>> G[H[x]] where G: MonoidK | ||
* it is a nested type [x] =>> G[H[x]] where G: Applicative and H: MonoidK | ||
* it is a generic case class where all fields have a MonoidK instance""") | ||
type DerivedMonoidK[F[_]] = Derived[MonoidK[F]] | ||
object DerivedMonoidK: | ||
type Or[F[_]] = Derived.Or[MonoidK[F]] | ||
inline def apply[F[_]]: MonoidK[F] = | ||
import DerivedMonoidK.given | ||
summonInline[DerivedMonoidK[F]].instance | ||
|
||
given [T](using T: Monoid[T]): DerivedMonoidK[Const[T]] = new MonoidK[Const[T]]: | ||
final override def empty[A]: Const[T][A] = T.empty | ||
|
||
final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y) | ||
|
||
given [F[_], G[_]](using F: Or[F]): DerivedMonoidK[[x] =>> F[G[x]]] = | ||
F.unify.compose[G] | ||
|
||
given [F[_], G[_]](using | ||
N: NotGiven[Or[F]], | ||
F0: DerivedApplicative.Or[F], | ||
G0: Or[G] | ||
): DerivedMonoidK[[x] =>> F[G[x]]] = | ||
new MonoidK[[x] =>> F[G[x]]]: | ||
val F: Applicative[F] = F0.unify | ||
val G: MonoidK[G] = G0.unify | ||
|
||
final override def empty[A]: F[G[A]] = F.pure(G.empty[A]) | ||
|
||
final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.map2(x, y)(G.combineK(_, _)) | ||
|
||
given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedMonoidK[F] = | ||
given K1.ProductInstances[MonoidK, F] = inst.unify | ||
new Product[MonoidK, F] {} | ||
|
||
trait Product[T[x[_]] <: MonoidK[x], F[_]](using inst: K1.ProductInstances[T, F]) | ||
extends MonoidK[F], | ||
DerivedSemigroupK.Product[T, F]: | ||
final override def empty[A]: F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.empty[A]) |
39 changes: 39 additions & 0 deletions
39
core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cats.derived | ||
|
||
import cats.{Semigroup, SemigroupK} | ||
import shapeless3.deriving.{Const, K1} | ||
|
||
import scala.annotation.implicitNotFound | ||
import scala.compiletime.* | ||
import scala.util.NotGiven | ||
|
||
@implicitNotFound("""Could not derive an instance of SemigroupK[F] where F = ${F}. | ||
Make sure that F[_] satisfies one of the following conditions: | ||
* it is a constant type [x] =>> T where T: Semigroup | ||
* it is a nested type [x] =>> G[H[x]] where G: SemigroupK | ||
* it is a nested type [x] =>> G[H[x]] where G: Apply and H: SemigroupK | ||
* it is a generic case class where all fields have a SemigroupK instance""") | ||
type DerivedSemigroupK[F[_]] = Derived[SemigroupK[F]] | ||
object DerivedSemigroupK: | ||
type Or[F[_]] = Derived.Or[SemigroupK[F]] | ||
inline def apply[F[_]]: SemigroupK[F] = | ||
import DerivedSemigroupK.given | ||
summonInline[DerivedSemigroupK[F]].instance | ||
|
||
given [T](using T: Semigroup[T]): DerivedSemigroupK[Const[T]] = new SemigroupK[Const[T]]: | ||
final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y) | ||
|
||
given [F[_], G[_]](using F: Or[F]): DerivedSemigroupK[[x] =>> F[G[x]]] = | ||
F.unify.compose[G] | ||
|
||
given [F[_], G[_]](using N: NotGiven[Or[F]], F: DerivedApply.Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = | ||
new SemigroupK[[x] =>> F[G[x]]]: | ||
final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.unify.map2(x, y)(G.unify.combineK(_, _)) | ||
|
||
given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedSemigroupK[F] = | ||
given K1.ProductInstances[SemigroupK, F] = inst.unify | ||
new Product[SemigroupK, F] {} | ||
|
||
trait Product[T[x[_]] <: SemigroupK[x], F[_]](using inst: K1.ProductInstances[T, F]) extends SemigroupK[F]: | ||
final override def combineK[A](x: F[A], y: F[A]): F[A] = | ||
inst.map2[A, A, A](x, y)([t[_]] => (smgrpk: T[t], x: t[A], y: t[A]) => smgrpk.combineK(x, y)) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package cats.derived | ||
|
||
import alleycats.* | ||
import cats.* | ||
import cats.derived.* | ||
import cats.laws.discipline.{MonoidKTests, SerializableTests} | ||
import org.scalacheck.Arbitrary | ||
import scala.compiletime.* | ||
|
||
class MonoidKSuite extends KittensSuite { | ||
import MonoidKSuite.* | ||
import TestDefns.* | ||
|
||
inline def monoidKTests[F[_]]: MonoidKTests[F] = MonoidKTests[F](summonInline) | ||
|
||
inline def testMonoidK(context: String): Unit = { | ||
checkAll(s"$context.MonoidK[ComplexProduct]", monoidKTests[ComplexProduct].monoidK[Char]) | ||
checkAll(s"$context.MonoidK[CaseClassWOption]", monoidKTests[CaseClassWOption].monoidK[Char]) | ||
checkAll(s"$context.MonoidK[BoxMul]", monoidKTests[BoxMul].monoidK[Char]) | ||
checkAll(s"$context.MonoidK is Serializable", SerializableTests.serializable(summonInline[MonoidK[ComplexProduct]])) | ||
|
||
test(s"$context.MonoidK respects existing instances") { | ||
val M = summonInline[MonoidK[BoxMul]] | ||
assert(M.empty[Char] == Box(Mul[Char](1))) | ||
assert(M.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) | ||
} | ||
} | ||
|
||
{ | ||
import auto.monoidK.given | ||
testMonoidK("auto") | ||
} | ||
|
||
{ | ||
import monInstances.given | ||
testMonoidK("semi") | ||
} | ||
} | ||
|
||
object MonoidKSuite { | ||
import TestDefns._ | ||
|
||
type BoxMul[A] = Box[Mul[A]] | ||
|
||
object monInstances { | ||
implicit val complexProduct: MonoidK[ComplexProduct] = semiauto.monoidK | ||
implicit val caseClassWOption: MonoidK[CaseClassWOption] = semiauto.monoidK | ||
implicit val boxMul: MonoidK[BoxMul] = semiauto.monoidK | ||
} | ||
|
||
final case class Mul[T](value: Int) | ||
object Mul { | ||
|
||
implicit def eqv[T]: Eq[Mul[T]] = Eq.by(_.value) | ||
|
||
implicit def arbitrary[T]: Arbitrary[Mul[T]] = | ||
Arbitrary(Arbitrary.arbitrary[Int].map(apply)) | ||
|
||
implicit val monoidK: MonoidK[Mul] = new MonoidK[Mul] { | ||
def empty[A] = Mul(1) | ||
def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) | ||
} | ||
} | ||
|
||
case class Simple[A](value1: List[A], value2: Set[A]) derives MonoidK | ||
case class Recursive[A](first: List[A], rest: Recursive[A]) derives MonoidK | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package cats.derived | ||
|
||
import cats.* | ||
import cats.laws.discipline.{SemigroupKTests, SerializableTests} | ||
import org.scalacheck.Arbitrary | ||
import scala.compiletime.* | ||
|
||
class SemigroupKSuite extends KittensSuite { | ||
import SemigroupKSuite.* | ||
import TestDefns.* | ||
|
||
inline def semigroupKTests[F[_]]: SemigroupKTests[F] = SemigroupKTests[F](summonInline) | ||
|
||
inline def testSemigroupK(context: String): Unit = { | ||
checkAll(s"$context.SemigroupK[ComplexProduct]", semigroupKTests[ComplexProduct].semigroupK[Char]) | ||
checkAll(s"$context.SemigroupK[CaseClassWOption]", semigroupKTests[CaseClassWOption].semigroupK[Char]) | ||
checkAll(s"$context.SemigroupK[BoxMul]", semigroupKTests[BoxMul].semigroupK[Char]) | ||
checkAll( | ||
s"$context.SemigroupK is Serializable", | ||
SerializableTests.serializable(summonInline[SemigroupK[ComplexProduct]]) | ||
) | ||
|
||
test(s"$context.SemigroupK respects existing instances") { | ||
assert(summonInline[SemigroupK[BoxMul]].combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) | ||
} | ||
} | ||
|
||
locally { | ||
import auto.semigroupK.given | ||
testSemigroupK("auto") | ||
} | ||
|
||
locally { | ||
import semiInstances.given | ||
testSemigroupK("semiauto") | ||
} | ||
} | ||
|
||
object SemigroupKSuite { | ||
import TestDefns._ | ||
|
||
type BoxMul[A] = Box[Mul[A]] | ||
|
||
object semiInstances { | ||
implicit val complexProduct: SemigroupK[ComplexProduct] = semiauto.semigroupK | ||
implicit val caseClassWOption: SemigroupK[CaseClassWOption] = semiauto.semigroupK | ||
implicit val boxMul: SemigroupK[BoxMul] = semiauto.semigroupK | ||
} | ||
|
||
final case class Mul[T](value: Int) | ||
object Mul { | ||
|
||
implicit def eqv[T]: Eq[Mul[T]] = Eq.by(_.value) | ||
|
||
implicit def arbitrary[T]: Arbitrary[Mul[T]] = | ||
Arbitrary(Arbitrary.arbitrary[Int].map(apply)) | ||
|
||
implicit val semigroupK: SemigroupK[Mul] = new SemigroupK[Mul] { | ||
def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) | ||
} | ||
} | ||
|
||
case class Simple[A](value1: List[A], value2: Set[A]) derives SemigroupK | ||
case class Recursive[A](first: List[A], rest: Recursive[A]) derives SemigroupK | ||
} |
This file was deleted.
Oops, something went wrong.