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 EmptySuite and also solve Empty for Coproducts #387

Merged
merged 1 commit into from
Sep 26, 2021
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
8 changes: 3 additions & 5 deletions core/src/main/scala-3/cats/derived/empty.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cats.derived

import alleycats.Empty
import cats.derived.util.Kinds
import shapeless3.deriving.K0

import scala.compiletime.*

type DerivedEmpty[A] = Derived[Empty[A]]
Expand All @@ -15,8 +17,4 @@ object DerivedEmpty:
Empty(inst.unify.construct([A] => (A: Empty[A]) => A.empty))

inline given coproduct[A](using gen: K0.CoproductGeneric[A]): DerivedEmpty[A] =
K0.summonFirst[Or, gen.MirroredElemTypes, A].unify

trait EmptyDerivation:
extension (E: Empty.type)
inline def derived[A]: Empty[A] = DerivedEmpty[A]
Kinds.summonOne0[Or, gen.MirroredElemTypes, A].unify
12 changes: 4 additions & 8 deletions core/src/main/scala-3/cats/derived/monoid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ package cats.derived
import cats.Monoid
import shapeless3.deriving.K0

trait ProductMonoid[F[x] <: Monoid[x], A](
using inst: K0.ProductInstances[F, A]
) extends ProductSemigroup[F, A], Monoid[A]:
trait ProductMonoid[F[x] <: Monoid[x], A](using
inst: K0.ProductInstances[F, A]
) extends ProductSemigroup[F, A],
Monoid[A]:
val empty: A = inst.construct([A] => (F: F[A]) => F.empty)

trait MonoidDerivation:
extension (M: Monoid.type)
inline def derived[A](using gen: K0.ProductGeneric[A]): Monoid[A] =
new ProductMonoid[Monoid, A]{}
82 changes: 43 additions & 39 deletions core/src/main/scala-3/cats/derived/package.scala
Original file line number Diff line number Diff line change
@@ -1,55 +1,59 @@
package cats.derived

import alleycats.*
import cats.*

extension (F: Foldable.type)
inline def derived[F[_]]: Foldable[F] = DerivedFoldable[F]

extension (F: Functor.type)
inline def derived[F[_]]: Functor[F] = DerivedFunctor[F]

extension (F: Reducible.type)
inline def derived[F[_]]: Reducible[F] = DerivedReducible[F]

extension (F: Traverse.type)
inline def derived[F[_]]: Traverse[F] = DerivedTraverse[F]

object semiauto extends
CommutativeMonoidDerivation,
CommutativeSemigroupDerivation,
ContravariantDerivation,
EmptyDerivation,
EmptyKDerivation,
EqDerivation,
HashDerivation,
InvariantDerivation,
MonoidDerivation,
MonoidKDerivation,
OrderDerivation,
PartialOrderDerivation,
SemigroupDerivation,
SemigroupKDerivation,
ShowDerivation,
Instances:

import shapeless3.deriving.K0

import scala.util.NotGiven

extension (E: Empty.type) inline def derived[A]: Empty[A] = DerivedEmpty[A]

extension (S: Semigroup.type)
inline def derived[A](using gen: K0.ProductGeneric[A]): Semigroup[A] =
new ProductSemigroup[Semigroup, A] {}

extension (M: Monoid.type)
inline def derived[A](using gen: K0.ProductGeneric[A]): Monoid[A] =
new ProductMonoid[Monoid, A] {}

extension (F: Foldable.type) inline def derived[F[_]]: Foldable[F] = DerivedFoldable[F]
extension (F: Functor.type) inline def derived[F[_]]: Functor[F] = DerivedFunctor[F]
extension (F: Reducible.type) inline def derived[F[_]]: Reducible[F] = DerivedReducible[F]
extension (F: Traverse.type) inline def derived[F[_]]: Traverse[F] = DerivedTraverse[F]

object semiauto
extends CommutativeMonoidDerivation,
CommutativeSemigroupDerivation,
ContravariantDerivation,
EmptyKDerivation,
EqDerivation,
HashDerivation,
InvariantDerivation,
MonoidKDerivation,
OrderDerivation,
PartialOrderDerivation,
SemigroupKDerivation,
ShowDerivation,
Instances:

inline def empty[A]: Empty[A] = DerivedEmpty[A]
inline def foldable[F[_]]: Foldable[F] = DerivedFoldable[F]

inline def functor[F[_]]: Functor[F] = DerivedFunctor[F]

inline def reducible[F[_]]: Reducible[F] = DerivedReducible[F]

inline def traverse[F[_]]: Traverse[F] = DerivedTraverse[F]


object auto:
object empty:
inline given [A](using NotGiven[Empty[A]]): Empty[A] = DerivedEmpty[A]

object functor:
inline given [F[_]]: Functor[F] = DerivedFunctor[F]
inline given [F[_]](using NotGiven[Functor[F]]): Functor[F] = DerivedFunctor[F]

object foldable:
inline given [F[_]]: Foldable[F] = DerivedFoldable[F]
inline given [F[_]](using NotGiven[Foldable[F]]): Foldable[F] = DerivedFoldable[F]

object reducible:
inline given [F[_]]: Reducible[F] = DerivedReducible[F]
inline given [F[_]](using NotGiven[Reducible[F]]): Reducible[F] = DerivedReducible[F]

object traverse:
inline given [F[_]]: Traverse[F] = DerivedTraverse[F]
inline given [F[_]](using NotGiven[Traverse[F]]): Traverse[F] = DerivedTraverse[F]
9 changes: 2 additions & 7 deletions core/src/main/scala-3/cats/derived/semigroup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,8 @@ package cats.derived
import cats.Semigroup
import shapeless3.deriving.K0

trait ProductSemigroup[F[x] <: Semigroup[x], A](
using inst: K0.ProductInstances[F, A]
trait ProductSemigroup[F[x] <: Semigroup[x], A](using
inst: K0.ProductInstances[F, A]
) extends Semigroup[A]:
def combine(x: A, y: A): A =
inst.map2(x, y)([A] => (F: F[A], x: A, y: A) => F.combine(x, y))

trait SemigroupDerivation:
extension (S: Semigroup.type)
inline def derived[A](using gen: K0.ProductGeneric[A]): Semigroup[A] =
new ProductSemigroup[Semigroup, A]{}
31 changes: 31 additions & 0 deletions core/src/main/scala-3/cats/derived/util/Kinds.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cats.derived.util

import shapeless3.deriving.K0.LiftP

import scala.compiletime.*
import scala.util.NotGiven

object Kinds:
inline def summonOne0[F[_], T, U]: F[U] =
summonOneFrom[LiftP[F, T]].asInstanceOf[F[U]]

inline def summonNone0[F[_], T]: Unit =
summonNoneFrom[LiftP[F, T]]

transparent inline def summonOneFrom[T <: Tuple]: Any =
inline erasedValue[T] match
case _: (a *: b) =>
summonFrom {
case instance: `a` =>
summonNoneFrom[b]
instance
case _ =>
summonOneFrom[b]
}

transparent inline def summonNoneFrom[T <: Tuple]: Unit =
inline erasedValue[T] match
case _: EmptyTuple => ()
case _: (a *: b) =>
summonInline[NotGiven[`a`]]
summonNoneFrom[b]
89 changes: 89 additions & 0 deletions core/src/test/scala-3/cats/derived/EmptySuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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 alleycats.Empty
import cats.laws.discipline.SerializableTests

import scala.compiletime.*
import scala.util.NotGiven

class EmptySuite extends KittensSuite:
import EmptySuite.given
import EmptySuite.*
import TestDefns.*

inline def empty[A]: A =
summonInline[Empty[A]].empty

inline def testEmpty(inline context: String): Unit =
test(s"$context.Empty[Foo]")(assert(empty[Foo] == Foo(0, None)))
test(s"$context.Empty[Outer]")(assert(empty[Outer] == Outer(Inner(0))))
test(s"$context.Empty[Interleaved[String]]")(assert(empty[Interleaved[String]] == Interleaved(0, "", 0, Nil, "")))
test(s"$context.Empty[Recursive]")(assert(empty[Recursive] == Recursive(0, None)))
test(s"$context.Empty[IList[Dummy]]")(assert(empty[IList[Dummy]] == INil()))
test(s"$context.Empty[Snoc[Dummy]]")(assert(empty[Snoc[Dummy]] == SNil()))
test(s"$context.Empty respects existing instances")(assert(empty[Box[Mask]] == Box(Mask(0xffffffff))))
checkAll(s"$context.Empty is Serializable", SerializableTests.serializable(summonInline[Empty[Foo]]))

inline def testNoAuto[A]: Unit =
testNoInstance("Empty[" + nameOf[A] + "]", "Could not find an instance of Empty")

inline def testNoSemi[A]: Unit =
testNoInstance("semiauto.empty[" + nameOf[A] + "]", "no implicit argument of type cats.derived.DerivedEmpty")

locally {
import auto.empty.given
testEmpty("auto")
testNoAuto[IList[Int]]
testNoAuto[Snoc[Int]]
testNoAuto[Rgb]
}

locally {
import semiInstances.given
testEmpty("semiauto")
testNoSemi[IList[Int]]
testNoSemi[Snoc[Int]]
testNoSemi[Rgb]
}

end EmptySuite

object EmptySuite:
import TestDefns.*

// `Monoid[Option[A]]` gives us `Empty[Option[A]]` but it requires a `Semigroup[A]`.
given [A]: Empty[Option[A]] = Empty(None)

object semiInstances:
given Empty[Foo] = semiauto.empty
given Empty[Outer] = semiauto.empty
given Empty[Interleaved[String]] = semiauto.empty
given Empty[Recursive] = semiauto.empty
given Empty[IList[Dummy]] = semiauto.empty
given Empty[Snoc[Dummy]] = semiauto.empty
given Empty[Box[Mask]] = semiauto.empty
given Empty[Chain] = semiauto.empty

trait Dummy
final case class Chain(head: Int, tail: Chain)
final case class Mask(bits: Int)
object Mask:
given Empty[Mask] = Empty(Mask(0xffffffff))

end EmptySuite
2 changes: 1 addition & 1 deletion core/src/test/scala-3/cats/derived/EmptyTests.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.derived.semiauto.*
import cats.derived.*

object EmptyTests:
case class Foo(i: Int, b: IntTree) derives Empty
Expand Down
20 changes: 15 additions & 5 deletions core/src/test/scala-3/cats/derived/KittensSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ package cats.derived
import cats.platform.Platform
import cats.syntax.AllSyntax
import munit.DisciplineSuite
import org.scalacheck.Test.Parameters
import org.scalacheck.Arbitrary
import org.scalacheck.Test.Parameters

import scala.quoted.*

/** An opinionated stack of traits to improve consistency and reduce
* boilerplate in Kittens tests. Note that unlike the corresponding
* CatsSuite in the Cat project, this trait does not mix in any
* instances.
/** An opinionated stack of traits to improve consistency and reduce boilerplate in Kittens tests. Note that unlike the
* corresponding CatsSuite in the Cat project, this trait does not mix in any instances.
*/
abstract class KittensSuite extends DisciplineSuite, AllSyntax, TestEqInstances:
override val scalaCheckTestParameters: Parameters = super.scalaCheckTestParameters
Expand All @@ -37,3 +37,13 @@ abstract class KittensSuite extends DisciplineSuite, AllSyntax, TestEqInstances:

given [A: Arbitrary]: Arbitrary[List[A]] =
Arbitrary.arbContainer

inline def nameOf[A]: String =
${ KittensSuite.nameOfMacro[A] }

inline def testNoInstance(inline code: String, message: String): Unit =
test(s"No $code")(assert(compileErrors(code).contains(message)))

object KittensSuite:
def nameOfMacro[A: Type](using Quotes) =
Expr(Type.show[A])
2 changes: 1 addition & 1 deletion core/src/test/scala-3/cats/derived/MonoidTests.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.derived.semiauto.*
import cats.derived.*

class MonoidTests { //
case class Foo(i: Int, b: Option[String]) derives Monoid, Empty, Semigroup
Expand Down
7 changes: 4 additions & 3 deletions core/src/test/scala-3/cats/derived/adtdefns.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import scala.annotation.tailrec
object TestDefns {

sealed trait Rgb
case object Red extends Rgb
case object Green extends Rgb
case object Blue extends Rgb
object Rgb:
case object Red extends Rgb
case object Green extends Rgb
case object Blue extends Rgb

final case class ComplexProduct[T](lbl: String, set: Set[T], fns: Vector[() => T], opt: Eval[Option[T]])
object ComplexProduct {
Expand Down