From 3b67dabb5fb255f31660841ed729d20f5e6e1c38 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Mon, 8 Jan 2024 10:09:05 +0200 Subject: [PATCH] Derive Alternative on Scala 3 (#630) * Derive Alternative on Scala 3 * Update docs --- README.md | 107 +++++++++--------- .../cats/derived/DerivedAlternative.scala | 44 +++++++ .../cats/derived/DerivedApplicative.scala | 2 +- .../derived/DerivedNonEmptyAlternative.scala | 4 +- .../derived/DerivedNonEmptyTraverse.scala | 5 +- .../cats/derived/DerivedTraverse.scala | 4 +- .../main/scala-3/cats/derived/package.scala | 7 ++ core/src/test/scala-3/cats/derived/ADTs.scala | 1 + .../cats/derived/AlternativeSuite.scala | 79 +++++++++++++ .../derived/NonEmptyAlternativeSuite.scala | 10 +- 10 files changed, 195 insertions(+), 68 deletions(-) create mode 100644 core/src/main/scala-3/cats/derived/DerivedAlternative.scala create mode 100644 core/src/test/scala-3/cats/derived/AlternativeSuite.scala diff --git a/README.md b/README.md index 2a0b9652..7a3f8e1a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ service][sonatype] and synced to Maven Central. It is available for Scala 2.12 and 2.13, Scala.js 1.5 and Scala Native 0.4. -To get started with sbt, simply add the following to your `build.sbt` file: +To get started with sbt, add the following to your `build.sbt` file: ```Scala libraryDependencies += "org.typelevel" %% "kittens" % "latestVersion" // indicated in the badge below @@ -84,13 +84,13 @@ scala> mike.show res0: String = People(name = Mike, contactInfo = ContactInfo(phoneNumber = 202-295-3928, address = 1 Main ST, Chicago, IL)) ``` -Note that in this example, the derivation generated instances for all referenced classes but still respected the -existing instance in scope. For different ways to derive instances please see the -[three modes of derivation below](#three-modes-of-derivation). +Note that in this example, +the derivation generated instances for all referenced classes but still respected the existing instance in scope. +For different ways to derive instances, please see the [three modes of derivation below](#three-modes-of-derivation). ### Sequence examples -Note that to run these examples you need partial unification enabled. +Note that to run these examples, you need partial unification enabled. For **Scala 2.12** you should add the following to your `build.sbt`: ```scala @@ -152,8 +152,8 @@ res0: Option[String] = Some(1 - a - 3.2) ### Three modes of derivation -Kittens provides three objects for derivation `cats.derived.auto`, `cats.derived.cached` and `cats.derived.semi` -The recommended best practice is going to be a semi auto one: +Kittens provides three objects for derivation `cats.derived.auto`, `cats.derived.cached` and `cats.derived.semi`. +The recommended best practice is the semiauto one: ```scala import cats.derived @@ -167,7 +167,7 @@ This will respect all existing instances even if the field is a type constructor the native `Show` instance for `List` and derived instance for `A`. And it manually caches the result to the `val showFoo`. Downside user will need to write one for every type they directly need a `Show` instance. -There are 3 alternatives: +There are three alternatives: 1. full auto: ```scala @@ -184,7 +184,7 @@ import derived.cached.show._ ``` Use this one with caution. It caches the derived instance globally. So it's only applicable if the instance is global -in the application. This could be problematic for libraries, which has no control over the uniqueness of an instance on +in the application. This could be problematic for libraries, which have no control over the uniqueness of an instance on use site. It relies on `shapeless.Cached` which is buggy. 3. manual semi @@ -192,10 +192,11 @@ use site. It relies on `shapeless.Cached` which is buggy. implicit val showFoo: Show[Foo] = derived.semiauto.show ``` -It has the same downside as the recommenced semi-auto practice but also suffers from the type constructor field issue. -I.e. if a field type is a type constructor whose native instance relies on the instance of the parameter type, this -approach will by default derive an instance for the type constructor one. To overcome this user have to first derive -the instance for type parameter. e.g. given: +It has the same downside as the recommenced semiauto practice but also suffers from the type constructor field issue. +I.e., if a field type is a type constructor whose native instance relies on the instance of the parameter type, +this approach will by default derive an instance for the type constructor one. +To overcome this, user have to first derive the instance for type parameter. +E.g., given: ```scala case class Foo(bars: List[Bar]) @@ -214,9 +215,10 @@ This way the native instance for `Show[List]` would be used. ## Scala 3 -We also offer 3 methods of derivation for Scala 3. All of them have the same behaviour wrt to recursively defining instances: +We also offer three methods of derivation for Scala 3. +All of them have the same behavior wrt to recursively defining instances: 1. Instances will always be recursively instantiated if necessary -2. Subject to the same type constructor field limitation as the Scala 2 auto and manual semi derivations +2. Subject to the same type constructor field limitation as the Scala 2 auto and manual semiauto derivations ### `derives` syntax (recommended) @@ -314,16 +316,18 @@ List[Set[x]]]`. #### Stack safety -Our derived instances are not stack-safe. This is a departure from the behaviour for Scala 2 because we didn't want to incur the performance penalty of trampolining all instances in `cats.Eval`. If your data-type is recursive or _extremely_ large then you may want to write instances by hand instead. +Our derived instances are not stack-safe. +This is a departure from the behaviour for Scala 2 +because we didn't want to incur the performance penalty of trampolining all instances in `cats.Eval`. +If your data-type is recursive or _extremely_ large, then you may want to write instances by hand instead. #### Missing features -Kittens for Scala 3 is built on top of [Shapeless -3](https://github.com/typelevel/shapeless-3) which has a completely different -API than [Shapeless 2](https://github.com/milessabin/shapeless) so we don't -support features like `Sequence` and `Lift`. +Kittens for Scala 3 is built on top of [Shapeless 3](https://github.com/typelevel/shapeless-3) +which has a completely different API than [Shapeless 2](https://github.com/milessabin/shapeless), +so we don't support features like `Sequence` and `Lift`. -`ConsK` derivation is also not supported although we expect this to be +`ConsK` derivation is also not supported, although we expect this to be [added](https://github.com/typelevel/kittens/issues/489) in a future release. ## Type class support matrix @@ -337,39 +341,40 @@ Legend: #### For monomorphic types -| Type Class | Case Classes | Sealed Traits | -|----------------------|--------------------------------|--------------------------| -| CommutativeMonoid | ∀ fields: CommutativeMonoid | | -| CommutativeSemigroup | ∀ fields: CommutativeSemigroup | | -| Empty | ∀ fields: Empty | ∃! variant: Empty | -| Eq | ∀ fields: Eq | ∀ variants: Eq | -| Hash | ∀ fields: Hash | ∀ variants: Hash | -| Monoid | ∀ fields: Monoid | | -| Order | ∀ fields: Order | ∀ variants: Order | -| PartialOrder | ∀ fields: PartialOrder | ∀ variants: PartialOrder | -| Semigroup | ∀ fields: Semigroup | | -| Show | ∀ fields: Show | ∀ variants: Show | -| ShowPretty | ∀ fields: ShowPretty | ∀ variants: ShowPretty | +| Type Class | Case Classes | Sealed Traits | Singleton types | +|----------------------|--------------------------------|--------------------------|:---------------:| +| CommutativeMonoid | ∀ fields: CommutativeMonoid | ✗ | ✗ | +| CommutativeSemigroup | ∀ fields: CommutativeSemigroup | ✗ | ✗ | +| Empty | ∀ fields: Empty | ∃! variant: Empty | ✗ | +| Eq | ∀ fields: Eq | ∀ variants: Eq | ✓ | +| Hash | ∀ fields: Hash | ∀ variants: Hash | ✓ | +| Monoid | ∀ fields: Monoid | ✗ | ✗ | +| Order | ∀ fields: Order | ∀ variants: Order | ✓ | +| PartialOrder | ∀ fields: PartialOrder | ∀ variants: PartialOrder | ✓ | +| Semigroup | ∀ fields: Semigroup | ✗ | ✗ | +| Show | ∀ fields: Show | ∀ variants: Show | ✓ | +| ShowPretty | ∀ fields: ShowPretty | ∀ variants: ShowPretty | ✓ | #### For polymorphic types -| Type Class | Case Classes | Sealed Traits | Constant Types `λ[x => T]` | Nested Types `λ[x => F[G[x]]]` | -|-----------------------|--------------------------------------------------|------------------------------|------------------------------|---------------------------------------------------------------------------| -| Applicative | ∀ fields: Applicative | | for T: Monoid | for F: Applicative and G: Applicative | -| Apply | ∀ fields: Apply | | for T: Semigroup | for F: Apply and G: Apply | -| Contravariant | ∀ fields: Contravariant | ∀ variants: Contravariant | for any T | for F: Functor and G: Contravariant | -| EmptyK | ∀ fields: EmptyK | ∃! variant: EmptyK | for T: Empty | for F: EmptyK and any G ∨ for F: Pure and G: EmptyK | -| Foldable | ∀ fields: Foldable | ∀ variants: Foldable | for any T | for F: Foldable and G: Foldable | -| Functor | ∀ fields: Functor | ∀ variants: Functor | for any T | for F: Functor and G: Functor ∨ for F: Contravariant and G: Contravariant | -| Invariant | ∀ fields: Invariant | ∀ variants: Invariant | for any T | for F: Invariant and G: Invariant | -| MonoidK | ∀ fields: MonoidK | | for T: Monoid | for F: MonoidK and any G ∨ for F: Applicative and G: MonoidK | -| NonEmptyTraverse | ∃ field: NonEmptyTraverse ∧ ∀ fields: Traverse | ∀ variants: NonEmptyTraverse | | for F: NonEmptyTraverse and G: NonEmptyTraverse | -| Pure | ∀ fields: Pure | | for T: Empty | for F: Pure and G: Pure | -| Reducible | ∃ field: Reducible ∧ ∀ fields: Foldable | ∀ variants: Reducible | | for F: Reducible and G: Reducible | -| SemigroupK | ∀ fields: SemigroupK | | for T: Semigroup | for F: SemigroupK and any G ∨ for F: Apply and G: SemigroupK | -| Traverse | ∀ fields: Traverse | ∀ variants: Traverse | for any T | for F: Traverse and G: Traverse | -| **Scala 3 only** ↓ | -| NonEmptyAlternative | ∀ fields: NonEmptyAlternative | | | for F: NonEmptyAlternative and G: Applicative | +| Type Class | Case Classes | Sealed Traits | Constant Types `λ[x => T]` | Nested Types `λ[x => F[G[x]]]` | +|---------------------|------------------------------------------------|------------------------------|----------------------------|---------------------------------------------------------------------------| +| Applicative | ∀ fields: Applicative | ✗ | for T: Monoid | for F: Applicative and G: Applicative | +| Apply | ∀ fields: Apply | ✗ | for T: Semigroup | for F: Apply and G: Apply | +| Contravariant | ∀ fields: Contravariant | ∀ variants: Contravariant | for any T | for F: Functor and G: Contravariant | +| EmptyK | ∀ fields: EmptyK | ∃! variant: EmptyK | for T: Empty | for F: EmptyK and any G ∨ for F: Pure and G: EmptyK | +| Foldable | ∀ fields: Foldable | ∀ variants: Foldable | for any T | for F: Foldable and G: Foldable | +| Functor | ∀ fields: Functor | ∀ variants: Functor | for any T | for F: Functor and G: Functor ∨ for F: Contravariant and G: Contravariant | +| Invariant | ∀ fields: Invariant | ∀ variants: Invariant | for any T | for F: Invariant and G: Invariant | +| MonoidK | ∀ fields: MonoidK | ✗ | for T: Monoid | for F: MonoidK and any G ∨ for F: Applicative and G: MonoidK | +| NonEmptyTraverse | ∃ field: NonEmptyTraverse ∧ ∀ fields: Traverse | ∀ variants: NonEmptyTraverse | ✗ | for F: NonEmptyTraverse and G: NonEmptyTraverse | +| Pure | ∀ fields: Pure | ✗ | for T: Empty | for F: Pure and G: Pure | +| Reducible | ∃ field: Reducible ∧ ∀ fields: Foldable | ∀ variants: Reducible | ✗ | for F: Reducible and G: Reducible | +| SemigroupK | ∀ fields: SemigroupK | ✗ | for T: Semigroup | for F: SemigroupK and any G ∨ for F: Apply and G: SemigroupK | +| Traverse | ∀ fields: Traverse | ∀ variants: Traverse | for any T | for F: Traverse and G: Traverse | +| **Scala 3 only** ↓ | +| NonEmptyAlternative | ∀ fields: NonEmptyAlternative | ✗ | ✗ | for F: NonEmptyAlternative and G: Applicative | +| Alternative | ∀ fields: Alternative | ✗ | ✗ | for F: Alternative and G: Applicative | [cats]: https://github.com/typelevel/cats [shapeless]: https://github.com/milessabin/shapeless diff --git a/core/src/main/scala-3/cats/derived/DerivedAlternative.scala b/core/src/main/scala-3/cats/derived/DerivedAlternative.scala new file mode 100644 index 00000000..c35642e6 --- /dev/null +++ b/core/src/main/scala-3/cats/derived/DerivedAlternative.scala @@ -0,0 +1,44 @@ +package cats.derived + +import cats.Alternative +import shapeless3.deriving.K1 + +import scala.annotation.* +import scala.compiletime.* + +@implicitNotFound("""Could not derive an instance of Alternative[F] where F = ${F}. +Make sure that F[_] satisfies one of the following conditions: + * it is a nested type [x] =>> G[H[x]] where G: Alternative and H: Applicative + * it is a generic case class where all fields have a Alternative instance""") +type DerivedAlternative[F[_]] = Derived[Alternative[F]] +object DerivedAlternative: + type Or[F[_]] = Derived.Or[Alternative[F]] + + @nowarn("msg=unused import") + inline def apply[F[_]]: Alternative[F] = + import DerivedAlternative.given + summonInline[DerivedAlternative[F]].instance + + @nowarn("msg=unused import") + inline def strict[F[_]]: Alternative[F] = + import Strict.given + summonInline[DerivedAlternative[F]].instance + + given nested[F[_], G[_]](using + F: => Or[F], + G: => DerivedApplicative.Or[G] + ): DerivedAlternative[[x] =>> F[G[x]]] = + new Derived.Lazy(() => F.unify.compose(using G.unify)) with Alternative[[x] =>> F[G[x]]]: + export delegate.* + + given product[F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedAlternative[F] = + Strict.product(using inst.unify) + + trait Product[T[f[_]] <: Alternative[f], F[_]](using K1.ProductInstances[T, F]) + extends Alternative[F], + DerivedNonEmptyAlternative.Product[T, F], + DerivedMonoidK.Product[T, F] + + object Strict: + given product[F[_]](using K1.ProductInstances[Alternative, F]): DerivedAlternative[F] = + new Alternative[F] with Product[Alternative, F] {} diff --git a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala index c3ecd982..cd688cc1 100644 --- a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala +++ b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala @@ -48,4 +48,4 @@ object DerivedApplicative: object Strict: given product[F[_]](using K1.ProductInstances[Applicative, F]): DerivedApplicative[F] = - new Product[Applicative, F] with DerivedApply.Product[Applicative, F] {} + new Applicative[F] with Product[Applicative, F] {} diff --git a/core/src/main/scala-3/cats/derived/DerivedNonEmptyAlternative.scala b/core/src/main/scala-3/cats/derived/DerivedNonEmptyAlternative.scala index 339e4a80..a6c24022 100644 --- a/core/src/main/scala-3/cats/derived/DerivedNonEmptyAlternative.scala +++ b/core/src/main/scala-3/cats/derived/DerivedNonEmptyAlternative.scala @@ -41,6 +41,4 @@ object DerivedNonEmptyAlternative: object Strict: given product[F[_]](using K1.ProductInstances[NonEmptyAlternative, F]): DerivedNonEmptyAlternative[F] = - new Product[NonEmptyAlternative, F] - with DerivedApply.Product[NonEmptyAlternative, F] - with DerivedSemigroupK.Product[NonEmptyAlternative, F] {} + new NonEmptyAlternative[F] with Product[NonEmptyAlternative, F] {} diff --git a/core/src/main/scala-3/cats/derived/DerivedNonEmptyTraverse.scala b/core/src/main/scala-3/cats/derived/DerivedNonEmptyTraverse.scala index 9d262552..078304be 100644 --- a/core/src/main/scala-3/cats/derived/DerivedNonEmptyTraverse.scala +++ b/core/src/main/scala-3/cats/derived/DerivedNonEmptyTraverse.scala @@ -89,7 +89,4 @@ object DerivedNonEmptyTraverse: given coproduct[F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedNonEmptyTraverse[F] = given K1.CoproductInstances[NonEmptyTraverse, F] = inst.unify - new Coproduct[NonEmptyTraverse, F] - with DerivedReducible.Coproduct[NonEmptyTraverse, F] - with DerivedTraverse.Coproduct[NonEmptyTraverse, F] - with DerivedFunctor.Generic[NonEmptyTraverse, F] {} + new NonEmptyTraverse[F] with Coproduct[NonEmptyTraverse, F] {} diff --git a/core/src/main/scala-3/cats/derived/DerivedTraverse.scala b/core/src/main/scala-3/cats/derived/DerivedTraverse.scala index f3e615bd..9120dd3b 100644 --- a/core/src/main/scala-3/cats/derived/DerivedTraverse.scala +++ b/core/src/main/scala-3/cats/derived/DerivedTraverse.scala @@ -63,8 +63,8 @@ object DerivedTraverse: object Strict: given product[F[_]](using K1.ProductInstances[Traverse, F]): DerivedTraverse[F] = - new Product[Traverse, F] with DerivedFunctor.Generic[Traverse, F] {} + new Traverse[F] with Product[Traverse, F] {} given coproduct[F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedTraverse[F] = given K1.CoproductInstances[Traverse, F] = inst.unify - new Coproduct[Traverse, F] with DerivedFunctor.Generic[Traverse, F] {} + new Traverse[F] with Coproduct[Traverse, F] {} diff --git a/core/src/main/scala-3/cats/derived/package.scala b/core/src/main/scala-3/cats/derived/package.scala index ef273719..d656538d 100644 --- a/core/src/main/scala-3/cats/derived/package.scala +++ b/core/src/main/scala-3/cats/derived/package.scala @@ -18,6 +18,7 @@ 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] extension (x: NonEmptyAlternative.type) inline def derived[F[_]]: NonEmptyAlternative[F] = DerivedNonEmptyAlternative[F] +extension (x: Alternative.type) inline def derived[F[_]]: Alternative[F] = DerivedAlternative[F] extension (x: EmptyK.type) inline def derived[F[_]]: EmptyK[F] = DerivedEmptyK[F] extension (x: Pure.type) inline def derived[F[_]]: Pure[F] = DerivedPure[F] extension (x: Foldable.type) inline def derived[F[_]]: Foldable[F] = DerivedFoldable[F] @@ -44,6 +45,7 @@ object semiauto: inline def applicative[F[_]]: Applicative[F] = DerivedApplicative[F] inline def apply[F[_]]: Apply[F] = DerivedApply[F] inline def nonEmptyAlternative[F[_]]: NonEmptyAlternative[F] = DerivedNonEmptyAlternative[F] + inline def alternative[F[_]]: Alternative[F] = DerivedAlternative[F] inline def emptyK[F[_]]: EmptyK[F] = DerivedEmptyK[F] inline def pure[F[_]]: Pure[F] = DerivedPure[F] inline def foldable[F[_]]: Foldable[F] = DerivedFoldable[F] @@ -83,6 +85,7 @@ object strict: extension (x: Applicative.type) inline def derived[F[_]]: Applicative[F] = DerivedApplicative.strict[F] extension (x: NonEmptyAlternative.type) inline def derived[F[_]]: NonEmptyAlternative[F] = DerivedNonEmptyAlternative.strict[F] + extension (x: Alternative.type) inline def derived[F[_]]: Alternative[F] = DerivedAlternative.strict[F] extension (x: Foldable.type) inline def derived[F[_]]: Foldable[F] = DerivedFoldable.strict[F] extension (x: Reducible.type) inline def derived[F[_]]: Reducible[F] = DerivedReducible.strict[F] extension (x: Traverse.type) inline def derived[F[_]]: Traverse[F] = DerivedTraverse.strict[F] @@ -110,6 +113,7 @@ object strict: inline def apply[F[_]]: Apply[F] = DerivedApply.strict[F] inline def applicative[F[_]]: Applicative[F] = DerivedApplicative.strict[F] inline def nonEmptyAlternative[F[_]]: NonEmptyAlternative[F] = DerivedNonEmptyAlternative.strict[F] + inline def alternative[F[_]]: Alternative[F] = DerivedAlternative.strict[F] inline def foldable[F[_]]: Foldable[F] = DerivedFoldable.strict[F] inline def reducible[F[_]]: Reducible[F] = DerivedReducible.strict[F] inline def traverse[F[_]]: Traverse[F] = DerivedTraverse.strict[F] @@ -152,6 +156,9 @@ object auto: object nonEmptyAlternative: inline given [F[_]](using NotGiven[NonEmptyAlternative[F]]): NonEmptyAlternative[F] = DerivedNonEmptyAlternative[F] + object alternative: + inline given [F[_]](using NotGiven[Alternative[F]]): Alternative[F] = DerivedAlternative[F] + object emptyK: inline given [F[_]](using NotGiven[EmptyK[F]]): EmptyK[F] = DerivedEmptyK[F] diff --git a/core/src/test/scala-3/cats/derived/ADTs.scala b/core/src/test/scala-3/cats/derived/ADTs.scala index d157e65f..e21da5bd 100644 --- a/core/src/test/scala-3/cats/derived/ADTs.scala +++ b/core/src/test/scala-3/cats/derived/ADTs.scala @@ -221,6 +221,7 @@ object ADTs: Cogen[Option[A]].contramap { case GenericAdtCase(value) => value } final case class CaseClassWOption[A](value: Option[A]) + final case class UnCons[+A](head: Option[A], tail: List[A]) final case class First(value: String) final case class Second(value: String) diff --git a/core/src/test/scala-3/cats/derived/AlternativeSuite.scala b/core/src/test/scala-3/cats/derived/AlternativeSuite.scala new file mode 100644 index 00000000..22417b4d --- /dev/null +++ b/core/src/test/scala-3/cats/derived/AlternativeSuite.scala @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015 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.laws.discipline.* +import cats.{Alternative, Applicative} +import org.scalacheck.Arbitrary + +import scala.compiletime.* + +class AlternativeSuite extends KittensSuite: + import ADTs.* + import AlternativeSuite.* + + inline def tests[F[_]]: AlternativeTests[F] = + AlternativeTests[F](summonInline) + + inline def validate(inline instance: String): Unit = + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].alternative[Int, String, Long]) + checkAll(s"$instance[OptList]", tests[OptList].alternative[Int, String, Long]) + checkAll(s"$instance[UnCons]", tests[UnCons].alternative[Int, String, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[Alternative[UnCons]])) + + locally: + import auto.alternative.given + validate("auto.alternative") + + locally: + import semiInstances.given + validate("semiauto.alternative") + + locally: + import strictInstances.given + validate("strict.semiauto.alternative") + testNoInstance("strict.semiauto.alternative", "TopK") + + locally: + import derivedInstances.* + val instance = "derived.alternative" + checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].alternative[Int, String, Long]) + checkAll(s"$instance[UnCons]", tests[UnCons].alternative[Int, String, Long]) + checkAll(s"$instance is Serializable", SerializableTests.serializable(Alternative[CaseClassWOption])) + +end AlternativeSuite + +object AlternativeSuite: + import ADTs.* + + type OptList[A] = Option[List[A]] + + object semiInstances: + given Alternative[CaseClassWOption] = semiauto.alternative + given Alternative[OptList] = semiauto.alternative + given Alternative[UnCons] = semiauto.alternative + + object strictInstances: + given [F[_]: Alternative, G[_]: Applicative]: Alternative[[x] =>> F[G[x]]] = Alternative[F].compose[G] + given Alternative[CaseClassWOption] = strict.semiauto.alternative + given Alternative[UnCons] = strict.semiauto.alternative + + object derivedInstances: + case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives Alternative + case class UnCons[A](x: ADTs.UnCons[A]) derives Alternative + +end AlternativeSuite diff --git a/core/src/test/scala-3/cats/derived/NonEmptyAlternativeSuite.scala b/core/src/test/scala-3/cats/derived/NonEmptyAlternativeSuite.scala index 9cedcb17..0636eb02 100644 --- a/core/src/test/scala-3/cats/derived/NonEmptyAlternativeSuite.scala +++ b/core/src/test/scala-3/cats/derived/NonEmptyAlternativeSuite.scala @@ -33,10 +33,7 @@ class NonEmptyAlternativeSuite extends KittensSuite: checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].nonEmptyAlternative[Int, String, Long]) checkAll(s"$instance[OptList]", tests[OptList].nonEmptyAlternative[Int, String, Long]) checkAll(s"$instance[UnCons]", tests[UnCons].nonEmptyAlternative[Int, String, Long]) - checkAll( - s"$instance is Serializable", - SerializableTests.serializable(summonInline[NonEmptyAlternative[CaseClassWOption]]) - ) + checkAll(s"$instance is Serializable", SerializableTests.serializable(summonInline[NonEmptyAlternative[UnCons]])) locally: import auto.nonEmptyAlternative.given @@ -56,7 +53,7 @@ class NonEmptyAlternativeSuite extends KittensSuite: val instance = "derived.nonEmptyAlternative" checkAll(s"$instance[CaseClassWOption]", tests[CaseClassWOption].nonEmptyAlternative[Int, String, Long]) checkAll(s"$instance[UnCons]", tests[UnCons].nonEmptyAlternative[Int, String, Long]) - checkAll(s"$instance is Serializable", SerializableTests.serializable(Applicative[CaseClassWOption])) + checkAll(s"$instance is Serializable", SerializableTests.serializable(NonEmptyAlternative[UnCons])) end NonEmptyAlternativeSuite @@ -64,7 +61,6 @@ object NonEmptyAlternativeSuite: import ADTs.* type OptList[A] = Option[List[A]] - type UnCons[+A] = (Option[A], Vector[A]) object semiInstances: given NonEmptyAlternative[CaseClassWOption] = semiauto.nonEmptyAlternative @@ -79,6 +75,6 @@ object NonEmptyAlternativeSuite: object derivedInstances: case class CaseClassWOption[A](x: ADTs.CaseClassWOption[A]) derives NonEmptyAlternative - case class UnCons[A](x: NonEmptyAlternativeSuite.UnCons[A]) derives NonEmptyAlternative + case class UnCons[A](x: ADTs.UnCons[A]) derives NonEmptyAlternative end NonEmptyAlternativeSuite