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

Port Semigroupk/Monoidk to new scheme #472

Merged
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
51 changes: 51 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedMonoidK.scala
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]]
joroKr21 marked this conversation as resolved.
Show resolved Hide resolved
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]
joroKr21 marked this conversation as resolved.
Show resolved Hide resolved

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 core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala
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]]
joroKr21 marked this conversation as resolved.
Show resolved Hide resolved
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))
12 changes: 0 additions & 12 deletions core/src/main/scala-3/cats/derived/monoidk.scala

This file was deleted.

18 changes: 11 additions & 7 deletions core/src/main/scala-3/cats/derived/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,10 @@ extension (x: Functor.type) inline def derived[F[_]]: Functor[F] = DerivedFuncto
extension (x: Reducible.type) inline def derived[F[_]]: Reducible[F] = DerivedReducible[F]
extension (x: Traverse.type) inline def derived[F[_]]: Traverse[F] = DerivedTraverse[F]
extension (x: NonEmptyTraverse.type) inline def derived[F[_]]: NonEmptyTraverse[F] = DerivedNonEmptyTraverse[F]
extension (x: SemigroupK.type) inline def derived[F[_]]: SemigroupK[F] = DerivedSemigroupK[F]
extension (x: MonoidK.type) inline def derived[F[_]]: MonoidK[F] = DerivedMonoidK[F]

object semiauto
extends ContravariantDerivation,
InvariantDerivation,
MonoidKDerivation,
PartialOrderDerivation,
SemigroupKDerivation,
Instances:
object semiauto extends ContravariantDerivation, InvariantDerivation, PartialOrderDerivation, Instances:

inline def eq[A]: Eq[A] = DerivedEq[A]
inline def hash[A]: Hash[A] = DerivedHash[A]
Expand All @@ -51,6 +47,8 @@ object semiauto
inline def traverse[F[_]]: Traverse[F] = DerivedTraverse[F]
inline def nonEmptyTraverse[F[_]]: NonEmptyTraverse[F] = DerivedNonEmptyTraverse[F]
inline def show[A]: Show[A] = DerivedShow[A]
inline def semigroupK[F[_]]: SemigroupK[F] = DerivedSemigroupK[F]
inline def monoidK[F[_]]: MonoidK[F] = DerivedMonoidK[F]

object auto:
object eq:
Expand Down Expand Up @@ -106,3 +104,9 @@ object auto:

object nonEmptyTraverse:
inline given [F[_]](using NotGiven[NonEmptyTraverse[F]]): NonEmptyTraverse[F] = DerivedNonEmptyTraverse[F]

object semigroupK:
inline given [F[_]](using NotGiven[SemigroupK[F]]): SemigroupK[F] = DerivedSemigroupK[F]

object monoidK:
inline given [F[_]](using NotGiven[MonoidK[F]]): MonoidK[F] = DerivedMonoidK[F]
14 changes: 0 additions & 14 deletions core/src/main/scala-3/cats/derived/semigroupk.scala

This file was deleted.

67 changes: 67 additions & 0 deletions core/src/test/scala-3/cats/derived/MonoidKSuite.scala
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
}
9 changes: 0 additions & 9 deletions core/src/test/scala-3/cats/derived/MonoidKTests.scala

This file was deleted.

65 changes: 65 additions & 0 deletions core/src/test/scala-3/cats/derived/SemigroupKSuite.scala
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
}
10 changes: 0 additions & 10 deletions core/src/test/scala-3/cats/derived/SemigroupKTests.scala

This file was deleted.