diff --git a/core/src/main/scala-3/cats/derived/DerivedApply.scala b/core/src/main/scala-3/cats/derived/DerivedApply.scala new file mode 100644 index 00000000..ae59998e --- /dev/null +++ b/core/src/main/scala-3/cats/derived/DerivedApply.scala @@ -0,0 +1,42 @@ +package cats.derived + +import shapeless3.deriving.{Const, K1} +import cats.{Apply, Semigroup} + +import scala.compiletime.* +import shapeless3.deriving.{Continue, K0, Labelling} + +import scala.annotation.implicitNotFound +import scala.deriving.Mirror + +@implicitNotFound("""Could not derive an instance of Apply[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: Apply and H: Apply + * it is a generic case class where all fields have an Apply instance + +Note: using kind-projector notation - https://github.com/typelevel/kind-projector""") +type DerivedApply[F[_]] = Derived[Apply[F]] +object DerivedApply: + type Or[F[_]] = Derived.Or[Apply[F]] + + inline def apply[F[_]]: Apply[F] = + import DerivedApply.given + summonInline[DerivedApply[F]].instance + + given [T](using T: Semigroup[T]): DerivedApply[Const[T]] = new Apply[Const[T]]: + def ap[A, B](ff: T)(fa: T) = T.combine(ff, fa) + def map[A, B](fa: T)(f: A => B) = fa + + given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedApply[[x] =>> F[G[x]]] = + F.unify.compose(G.unify) + + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedApply[F] = + given K1.ProductInstances[Apply, F] = inst.unify + new Product[Apply, F] {} + + trait Product[T[x[_]] <: Apply[x], F[_]](using inst: K1.ProductInstances[T, F]) extends Apply[F]: + override def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = + inst.map2(ff, fa)([t[_]] => (apl: T[t], tt: t[A => B], ta: t[A]) => apl.ap(tt)(ta)) + override def map[A, B](fa: F[A])(f: A => B): F[B] = + inst.map(fa: F[A])([f[_]] => (tf: T[f], fa: f[A]) => tf.map(fa)(f)) diff --git a/core/src/main/scala-3/cats/derived/package.scala b/core/src/main/scala-3/cats/derived/package.scala index bd81397e..4239b9c4 100644 --- a/core/src/main/scala-3/cats/derived/package.scala +++ b/core/src/main/scala-3/cats/derived/package.scala @@ -14,6 +14,7 @@ extension (x: Monoid.type) inline def derived[A]: Monoid[A] = DerivedMonoid[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: Show.type) inline def derived[A]: Show[A] = DerivedShow[A] +extension (x: Apply.type) inline def derived[F[_]]: Apply[F] = DerivedApply[F] extension (x: Foldable.type) inline def derived[F[_]]: Foldable[F] = DerivedFoldable[F] extension (x: Functor.type) inline def derived[F[_]]: Functor[F] = DerivedFunctor[F] extension (x: Reducible.type) inline def derived[F[_]]: Reducible[F] = DerivedReducible[F] @@ -37,6 +38,7 @@ object semiauto inline def monoid[A]: Monoid[A] = DerivedMonoid[A] inline def commutativeSemigroup[A]: CommutativeSemigroup[A] = DerivedCommutativeSemigroup[A] inline def commutativeMonoid[A]: CommutativeMonoid[A] = DerivedCommutativeMonoid[A] + inline def apply[F[_]]: Apply[F] = DerivedApply[F] inline def foldable[F[_]]: Foldable[F] = DerivedFoldable[F] inline def functor[F[_]]: Functor[F] = DerivedFunctor[F] inline def reducible[F[_]]: Reducible[F] = DerivedReducible[F] @@ -69,6 +71,9 @@ object auto: object show: inline given [A](using NotGiven[Show[A]]): Show[A] = DerivedShow[A] + object apply: + inline given [F[_]](using NotGiven[Apply[F]]): Apply[F] = DerivedApply[F] + object functor: inline given [F[_]](using NotGiven[Functor[F]]): Functor[F] = DerivedFunctor[F] diff --git a/core/src/test/scala-3/cats/derived/ApplySuite.scala b/core/src/test/scala-3/cats/derived/ApplySuite.scala new file mode 100644 index 00000000..5150e71f --- /dev/null +++ b/core/src/test/scala-3/cats/derived/ApplySuite.scala @@ -0,0 +1,72 @@ +/* + * 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.Apply +import cats.laws.discipline.{ApplyTests, SerializableTests} +import cats.laws.discipline.SemigroupalTests.Isomorphisms +import scala.compiletime.* + +class ApplySuite extends KittensSuite { + import ApplySuite._ + import TestDefns._ + + inline def applyTests[F[_]]: ApplyTests[F] = + ApplyTests[F](summonInline) + + inline def testApply(inline context: String): Unit = { + given Isomorphisms[CaseClassWOption] = Isomorphisms.invariant(summonInline[Apply[CaseClassWOption]]) + given Isomorphisms[OptList] = Isomorphisms.invariant(summonInline[Apply[OptList]]) + // given isoAndInt: Isomorphisms[AndInt] = Isomorphisms.invariant(andInt) + given Isomorphisms[Interleaved] = Isomorphisms.invariant(summonInline[Apply[Interleaved]]) + given Isomorphisms[ListBox] = Isomorphisms.invariant(summonInline[Apply[ListBox]]) + + checkAll(s"$context.Apply[CaseClassWOption]", applyTests[CaseClassWOption].apply[Int, String, Long]) + checkAll(s"$context.Apply[OptList]", applyTests[OptList].apply[Int, String, Long]) +// checkAll(s"$context.Apply[AndInt]", applyTests[AndInt].apply[Int, String, Long]) + checkAll(s"$context.Apply[Interleaved]", applyTests[Interleaved].apply[Int, String, Long]) + checkAll(s"$context.Apply[ListBox]", applyTests[ListBox].apply[Int, String, Long]) + checkAll(s"$context.Apply is Serializable", SerializableTests.serializable(summonInline[Apply[Interleaved]])) + } + + { + import auto.apply.given + testApply("auto") + } + + { + import semiInstances.given + testApply("semiauto") + } +} + +object ApplySuite { + import TestDefns._ + + type OptList[A] = Option[List[A]] +// type AndInt[A] = (A, Int) + type ListBox[A] = List[Box[A]] + + object semiInstances { + given Apply[Box] = semiauto.apply + given Apply[CaseClassWOption] = semiauto.apply + given Apply[OptList] = semiauto.apply +// given Apply[AndInt] = semiauto.apply + given Apply[Interleaved] = semiauto.apply + given Apply[ListBox] = semiauto.apply + } +}