From c458c36a9eb175d74d64b12c172b85e878e7bc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Mon, 7 Feb 2022 02:30:41 +0100 Subject: [PATCH 1/5] Port Applicative to dotty --- .../cats/derived/DerivedApplicative.scala | 44 +++++++++++ .../main/scala-3/cats/derived/package.scala | 5 ++ .../cats/derived/ApplicativeSuite.scala | 75 +++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 core/src/main/scala-3/cats/derived/DerivedApplicative.scala create mode 100644 core/src/test/scala-3/cats/derived/ApplicativeSuite.scala diff --git a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala new file mode 100644 index 00000000..25233cb7 --- /dev/null +++ b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala @@ -0,0 +1,44 @@ +package cats.derived + +import shapeless3.deriving.{Const, K1} +import cats.{Applicative, Monoid} + +import scala.compiletime.* +import shapeless3.deriving.{Continue, K0, Labelling} + +import scala.annotation.implicitNotFound +import scala.deriving.Mirror + +@implicitNotFound("""Could not derive an instance of Applicative[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: Applicative and H: Applicative + * it is a generic case class where all fields have an Applicative instance + +Note: using kind-projector notation - https://github.com/typelevel/kind-projector""") +type DerivedApplicative[F[_]] = Derived[Applicative[F]] +object DerivedApplicative: + type Or[F[_]] = Derived.Or[Applicative[F]] + + inline def apply[F[_]]: Applicative[F] = + import DerivedApplicative.given + summonInline[DerivedApplicative[F]].instance + + given [T](using T: Monoid[T]): DerivedApplicative[Const[T]] = new Applicative[Const[T]]: + def pure[A](x: A) = T.empty + def ap[A, B](ff: T)(fa: T) = T.combine(ff, fa) + override def map[A, B](fa: T)(f: A => B) = fa + + given [F[_], G[_]](using F: Applicative[F], G: Applicative[G]): DerivedApplicative[[x] =>> F[G[x]]] = + F.compose(G) + + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedApplicative[F] = + given K1.ProductInstances[Applicative, F] = inst.unify + new Product[Applicative, F] {} + + trait Product[T[x[_]] <: Applicative[x], F[_]](using inst: K1.ProductInstances[T, F]) extends Applicative[F]: + + override def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = + inst.map2(ff, fa)([t[_]] => (emp: T[t], t0: t[A => B], t1: t[A]) => emp.ap(t0)(t1)) + + override def pure[A](x: A): F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.pure[A](x)) diff --git a/core/src/main/scala-3/cats/derived/package.scala b/core/src/main/scala-3/cats/derived/package.scala index bd81397e..a4dd1ced 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: Applicative.type) inline def derived[F[_]]: Applicative[F] = DerivedApplicative[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 applicative[F[_]]: Applicative[F] = DerivedApplicative[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 applicative: + inline given [F[_]](using NotGiven[Applicative[F]]): Applicative[F] = DerivedApplicative[F] + object functor: inline given [F[_]](using NotGiven[Functor[F]]): Functor[F] = DerivedFunctor[F] diff --git a/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala new file mode 100644 index 00000000..840ed8b2 --- /dev/null +++ b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala @@ -0,0 +1,75 @@ +/* + * 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.Applicative +import cats.laws.discipline.{ApplicativeTests, SerializableTests} +import cats.laws.discipline.SemigroupalTests.Isomorphisms + +class ApplicativeSuite extends KittensSuite { + import ApplicativeSuite._ + import TestDefns._ + + def testApplicative(context: String)(using + caseClassWOption: Applicative[CaseClassWOption], + optList: Applicative[OptList], + andInt: Applicative[AndInt], + interleaved: Applicative[Interleaved], + listBox: Applicative[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.Applicative[CaseClassWOption]", + ApplicativeTests[CaseClassWOption].applicative[Int, String, Long] + ) + checkAll(s"$context.Applicative[OptList]", ApplicativeTests[OptList].applicative[Int, String, Long]) + checkAll(s"$context.Applicative[AndInt]", ApplicativeTests[AndInt].applicative[Int, String, Long]) + checkAll(s"$context.Applicative[Interleaved]", ApplicativeTests[Interleaved].applicative[Int, String, Long]) + checkAll(s"$context.Applicative[ListBox]", ApplicativeTests[ListBox].applicative[Int, String, Long]) + checkAll(s"$context.Applicative is Serializable", SerializableTests.serializable(Applicative[Interleaved])) + } + + { + import auto.applicative.given + testApplicative("auto") + } + + { + import semiInstances.given + testApplicative("semiauto") + } +} + +object ApplicativeSuite { + import TestDefns._ + import cats.instances.tuple._ + + type OptList[A] = Option[List[A]] + type AndInt[A] = (A, Int) + type ListBox[A] = List[Box[A]] + + object semiInstances { + given Applicative[Box] = semiauto.applicative + given Applicative[CaseClassWOption] = semiauto.applicative + given Applicative[OptList] = semiauto.applicative + given Applicative[AndInt] = semiauto.applicative + given Applicative[Interleaved] = semiauto.applicative + given Applicative[ListBox] = semiauto.applicative + } +} From 2fd07b53a3dc9bbec89e4947c240e9fa8a254602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Mon, 7 Feb 2022 02:35:00 +0100 Subject: [PATCH 2/5] Code cleanup --- .../src/main/scala-3/cats/derived/DerivedApplicative.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala index 25233cb7..8df3f3db 100644 --- a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala +++ b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala @@ -27,7 +27,6 @@ object DerivedApplicative: given [T](using T: Monoid[T]): DerivedApplicative[Const[T]] = new Applicative[Const[T]]: def pure[A](x: A) = T.empty def ap[A, B](ff: T)(fa: T) = T.combine(ff, fa) - override def map[A, B](fa: T)(f: A => B) = fa given [F[_], G[_]](using F: Applicative[F], G: Applicative[G]): DerivedApplicative[[x] =>> F[G[x]]] = F.compose(G) @@ -37,8 +36,6 @@ object DerivedApplicative: new Product[Applicative, F] {} trait Product[T[x[_]] <: Applicative[x], F[_]](using inst: K1.ProductInstances[T, F]) extends Applicative[F]: - override def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = - inst.map2(ff, fa)([t[_]] => (emp: T[t], t0: t[A => B], t1: t[A]) => emp.ap(t0)(t1)) - - override def pure[A](x: A): F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.pure[A](x)) + inst.map2(ff, fa)([t[_]] => (apl: T[t], tt: t[A => B], ta: t[A]) => apl.ap(tt)(ta)) + override def pure[A](x: A): F[A] = inst.construct([t[_]] => (apl: T[t]) => apl.pure[A](x)) From d26c2129e912ead891f82f83aadae632d61c52e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Mon, 7 Feb 2022 19:50:34 +0100 Subject: [PATCH 3/5] Comment out Tuple --- .../test/scala-3/cats/derived/ApplicativeSuite.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala index 840ed8b2..c929f207 100644 --- a/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala +++ b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala @@ -27,19 +27,20 @@ class ApplicativeSuite extends KittensSuite { def testApplicative(context: String)(using caseClassWOption: Applicative[CaseClassWOption], optList: Applicative[OptList], - andInt: Applicative[AndInt], + // Requires Dotty 3.1.2 +// andInt: Applicative[AndInt], interleaved: Applicative[Interleaved], listBox: Applicative[ListBox] ): Unit = { given isoOptList: Isomorphisms[OptList] = Isomorphisms.invariant(optList) - given isoAndInt: Isomorphisms[AndInt] = Isomorphisms.invariant(andInt) +// given isoAndInt: Isomorphisms[AndInt] = Isomorphisms.invariant(andInt) given isoListBox: Isomorphisms[ListBox] = Isomorphisms.invariant(listBox) checkAll( s"$context.Applicative[CaseClassWOption]", ApplicativeTests[CaseClassWOption].applicative[Int, String, Long] ) checkAll(s"$context.Applicative[OptList]", ApplicativeTests[OptList].applicative[Int, String, Long]) - checkAll(s"$context.Applicative[AndInt]", ApplicativeTests[AndInt].applicative[Int, String, Long]) +// checkAll(s"$context.Applicative[AndInt]", ApplicativeTests[AndInt].applicative[Int, String, Long]) checkAll(s"$context.Applicative[Interleaved]", ApplicativeTests[Interleaved].applicative[Int, String, Long]) checkAll(s"$context.Applicative[ListBox]", ApplicativeTests[ListBox].applicative[Int, String, Long]) checkAll(s"$context.Applicative is Serializable", SerializableTests.serializable(Applicative[Interleaved])) @@ -61,14 +62,14 @@ object ApplicativeSuite { import cats.instances.tuple._ type OptList[A] = Option[List[A]] - type AndInt[A] = (A, Int) +// type AndInt[A] = (A, Int) type ListBox[A] = List[Box[A]] object semiInstances { given Applicative[Box] = semiauto.applicative given Applicative[CaseClassWOption] = semiauto.applicative given Applicative[OptList] = semiauto.applicative - given Applicative[AndInt] = semiauto.applicative +// given Applicative[AndInt] = semiauto.applicative given Applicative[Interleaved] = semiauto.applicative given Applicative[ListBox] = semiauto.applicative } From afbb63ea9bbdadd6cc6d4ac739391e1b3123a140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8BAndrzej=20Ressel?= Date: Sun, 13 Feb 2022 12:19:36 +0100 Subject: [PATCH 4/5] Use Apply for deriving Applicative --- .../scala-3/cats/derived/DerivedApplicative.scala | 12 ++++++------ .../test/scala-3/cats/derived/ApplicativeSuite.scala | 3 --- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala index 8df3f3db..a1f60511 100644 --- a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala +++ b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala @@ -25,17 +25,17 @@ object DerivedApplicative: summonInline[DerivedApplicative[F]].instance given [T](using T: Monoid[T]): DerivedApplicative[Const[T]] = new Applicative[Const[T]]: - def pure[A](x: A) = T.empty - def ap[A, B](ff: T)(fa: T) = T.combine(ff, fa) + def pure[A](x: A): Const[T][A] = T.empty + def ap[A, B](ff: T)(fa: T): Const[T][B] = T.combine(ff, fa) given [F[_], G[_]](using F: Applicative[F], G: Applicative[G]): DerivedApplicative[[x] =>> F[G[x]]] = F.compose(G) given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedApplicative[F] = given K1.ProductInstances[Applicative, F] = inst.unify - new Product[Applicative, F] {} + new Product[Applicative, F] with DerivedApply.Product[Applicative, F] {} - trait Product[T[x[_]] <: Applicative[x], F[_]](using inst: K1.ProductInstances[T, F]) extends Applicative[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)) + trait Product[T[x[_]] <: Applicative[x], F[_]](using inst: K1.ProductInstances[T, F]) + extends Applicative[F], + DerivedApply.Product[T, F]: override def pure[A](x: A): F[A] = inst.construct([t[_]] => (apl: T[t]) => apl.pure[A](x)) diff --git a/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala index c929f207..0fc5a125 100644 --- a/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala +++ b/core/src/test/scala-3/cats/derived/ApplicativeSuite.scala @@ -32,9 +32,6 @@ class ApplicativeSuite extends KittensSuite { interleaved: Applicative[Interleaved], listBox: Applicative[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.Applicative[CaseClassWOption]", ApplicativeTests[CaseClassWOption].applicative[Int, String, Long] From 6f1b76a679ccfedd12ff504ce84ac97e78b555a2 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Sun, 13 Feb 2022 15:00:16 +0100 Subject: [PATCH 5/5] Composite Applicative can also be derived --- core/src/main/scala-3/cats/derived/DerivedApplicative.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala index a1f60511..2a94e080 100644 --- a/core/src/main/scala-3/cats/derived/DerivedApplicative.scala +++ b/core/src/main/scala-3/cats/derived/DerivedApplicative.scala @@ -28,8 +28,8 @@ object DerivedApplicative: def pure[A](x: A): Const[T][A] = T.empty def ap[A, B](ff: T)(fa: T): Const[T][B] = T.combine(ff, fa) - given [F[_], G[_]](using F: Applicative[F], G: Applicative[G]): DerivedApplicative[[x] =>> F[G[x]]] = - F.compose(G) + given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedApplicative[[x] =>> F[G[x]]] = + F.unify.compose(G.unify) given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedApplicative[F] = given K1.ProductInstances[Applicative, F] = inst.unify