From 4d0602b470229230dc5494c34688649db2a60e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Mon, 7 Feb 2022 21:07:41 +0100 Subject: [PATCH 1/3] Port Apply to Dotty --- .../scala-3/cats/derived/DerivedApply.scala | 42 +++++++++++ .../main/scala-3/cats/derived/package.scala | 5 ++ .../scala-3/cats/derived/ApplySuite.scala | 73 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 core/src/main/scala-3/cats/derived/DerivedApply.scala create mode 100644 core/src/test/scala-3/cats/derived/ApplySuite.scala 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..593faa00 --- /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: Apply[F], G: Apply[G]): DerivedApply[[x] =>> F[G[x]]] = + F.compose(G) + + 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..4a8ebaf6 --- /dev/null +++ b/core/src/test/scala-3/cats/derived/ApplySuite.scala @@ -0,0 +1,73 @@ +/* + * 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 + +class ApplySuite extends KittensSuite { + import ApplySuite._ + import TestDefns._ + + def testApply(context: String)(using + caseClassWOption: Apply[CaseClassWOption], + optList: Apply[OptList], + // Requires scala 3.1.2 +// andInt: Apply[AndInt], + interleaved: Apply[Interleaved], + listBox: Apply[ListBox] + ): Unit = { + given isoOptList: Isomorphisms[OptList] = Isomorphisms.invariant(optList) +// given isoAndInt: Isomorphisms[AndInt] = Isomorphisms.invariant(andInt) + given isoListBox: Isomorphisms[ListBox] = Isomorphisms.invariant(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(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 + } +} From 70f12f677a53cc041e514b4f5443c4809a5f6415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Wed, 9 Feb 2022 09:50:33 +0100 Subject: [PATCH 2/3] Apply -> Or Co-authored-by: Georgi Krastev --- core/src/main/scala-3/cats/derived/DerivedApply.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedApply.scala b/core/src/main/scala-3/cats/derived/DerivedApply.scala index 593faa00..ae59998e 100644 --- a/core/src/main/scala-3/cats/derived/DerivedApply.scala +++ b/core/src/main/scala-3/cats/derived/DerivedApply.scala @@ -28,8 +28,8 @@ object DerivedApply: 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: Apply[F], G: Apply[G]): DerivedApply[[x] =>> F[G[x]]] = - F.compose(G) + 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 From 92e91c5c0ac54496ae8d15e3b8d2b9a88d1b6e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Wed, 9 Feb 2022 10:01:48 +0100 Subject: [PATCH 3/3] Make testApply inline --- .../scala-3/cats/derived/ApplySuite.scala | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/core/src/test/scala-3/cats/derived/ApplySuite.scala b/core/src/test/scala-3/cats/derived/ApplySuite.scala index 4a8ebaf6..5150e71f 100644 --- a/core/src/test/scala-3/cats/derived/ApplySuite.scala +++ b/core/src/test/scala-3/cats/derived/ApplySuite.scala @@ -19,29 +19,28 @@ 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._ - def testApply(context: String)(using - caseClassWOption: Apply[CaseClassWOption], - optList: Apply[OptList], - // Requires scala 3.1.2 -// andInt: Apply[AndInt], - interleaved: Apply[Interleaved], - listBox: Apply[ListBox] - ): Unit = { - given isoOptList: Isomorphisms[OptList] = Isomorphisms.invariant(optList) -// given isoAndInt: Isomorphisms[AndInt] = Isomorphisms.invariant(andInt) - given isoListBox: Isomorphisms[ListBox] = Isomorphisms.invariant(listBox) + inline def applyTests[F[_]]: ApplyTests[F] = + ApplyTests[F](summonInline) - 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(Apply[Interleaved])) + 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]])) } {