From 4bb9936fd26992c236f617de6101639d72069167 Mon Sep 17 00:00:00 2001 From: Zach Albia Date: Tue, 11 May 2021 16:35:25 +0800 Subject: [PATCH 1/5] integrate subset of pr #449 --- .../scala/zio/prelude/AssociativeBoth.scala | 17 +++++++++++++- .../main/scala/zio/prelude/Covariant.scala | 17 ++++++++++++++ .../src/main/scala/zio/prelude/Derive.scala | 22 +++++++++++++++++++ .../src/main/scala/zio/prelude/Equal.scala | 12 ++++++++++ .../scala/zio/prelude/newtypes/package.scala | 10 +++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala index e4fd60932..34b04b66c 100644 --- a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala +++ b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala @@ -18,7 +18,7 @@ package zio.prelude import zio._ import zio.prelude.coherent.AssociativeBothDeriveEqualInvariant -import zio.prelude.newtypes.{AndF, Failure, OrF} +import zio.prelude.newtypes.{AndF, Failure, Nested, OrF} import zio.stm.ZSTM import zio.stream.{ZSink, ZStream} import zio.test.TestResult @@ -1158,6 +1158,21 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar Id(Id.unwrap(fa) -> Id.unwrap(fb)) } + /** + * The `IdentityBoth` (and `AssociativeBoth`) instance for `Nested[F, G, A]`. + */ + implicit def NestedIdentityBoth[F[+_]: IdentityBoth: Covariant, G[+_]](implicit + G: IdentityBoth[G] + ): IdentityBoth[({ type lambda[+A] = Nested[F, G, A] })#lambda] = + new IdentityBoth[({ type lambda[+A] = Nested[F, G, A] })#lambda] { + override def any: Nested[F, G, Any] = Nested(G.any.succeed[F]) + + override def both[A, B](fa: => Nested[F, G, A], fb: => Nested[F, G, B]): Nested[F, G, (A, B)] = + Nested { + Nested.unwrap[F[G[A]]](fa).zipWith(Nested.unwrap[F[G[B]]](fb))(_ zip _) + } + } + /** * The `IdentityBoth` (and `AssociativeBoth`) instance for `List`. */ diff --git a/core/shared/src/main/scala/zio/prelude/Covariant.scala b/core/shared/src/main/scala/zio/prelude/Covariant.scala index 497e99616..899d333b6 100644 --- a/core/shared/src/main/scala/zio/prelude/Covariant.scala +++ b/core/shared/src/main/scala/zio/prelude/Covariant.scala @@ -17,6 +17,7 @@ package zio.prelude import zio.prelude.coherent.CovariantDeriveEqual +import zio.prelude.newtypes.Nested import zio.test.TestResult import zio.test.laws._ @@ -111,6 +112,22 @@ object Covariant extends LawfulF.Covariant[CovariantDeriveEqual, Equal] { def apply[F[+_]](implicit covariant: Covariant[F]): Covariant[F] = covariant + /** + * Constructs the instance for `Covariant[({ type lambda[+A] = Nested[F, G, A] })#lambda]` + * given a pair of pre-existing `Covariant` instances for covariant, higher-kinded type + * parameters `F[+_]` and `G[+_]`. + */ + implicit def NestedCovariant[F[+_], G[+_]](implicit + F: Covariant[F], + G: Covariant[G] + ): Covariant[({ type lambda[+A] = Nested[F, G, A] })#lambda] = + new Covariant[({ type lambda[+A] = Nested[F, G, A] })#lambda] { + private lazy val composedCovariant = F.compose(G) + + override def map[A, B](f: A => B): Nested[F, G, A] => Nested[F, G, B] = { x: Nested[F, G, A] => + Nested(composedCovariant.map(f)(Nested.unwrap[F[G[A]]](x))) + } + } } trait CovariantSyntax { diff --git a/core/shared/src/main/scala/zio/prelude/Derive.scala b/core/shared/src/main/scala/zio/prelude/Derive.scala index 8b8864dca..16057fe07 100644 --- a/core/shared/src/main/scala/zio/prelude/Derive.scala +++ b/core/shared/src/main/scala/zio/prelude/Derive.scala @@ -17,6 +17,7 @@ package zio.prelude import zio.{Cause, Chunk, Exit, NonEmptyChunk} +import zio.prelude.newtypes.Nested import scala.util.Try @@ -47,6 +48,27 @@ object Derive { def apply[F[_], Typeclass[_]](implicit derive: Derive[F, Typeclass]): Derive[F, Typeclass] = derive + + /** + * The `DeriveEqual` instance for `Id`. + */ + implicit val IdDeriveEqual: Derive[Id, Equal] = + new Derive[Id, Equal] { + override def derive[A: Equal]: Equal[Id[A]] = Id.wrapAll(Equal[A]) + } + + /** + * The `DeriveEqual` instance for `Nested`. + */ + implicit def NestedDeriveEqual[F[+_], G[+_]](implicit + F: Derive[F, Equal], + G: Derive[G, Equal] + ): Derive[({ type lambda[A] = Nested[F, G, A] })#lambda, Equal] = + new Derive[({ type lambda[A] = Nested[F, G, A] })#lambda, Equal] { + override def derive[A: Equal]: Equal[Nested[F, G, A]] = + Equal.NestedEqual(F.derive(G.derive[A])) + } + /** * The `DeriveEqual` instance for `Chunk`. */ diff --git a/core/shared/src/main/scala/zio/prelude/Equal.scala b/core/shared/src/main/scala/zio/prelude/Equal.scala index 28283bb0d..22ce8e22a 100644 --- a/core/shared/src/main/scala/zio/prelude/Equal.scala +++ b/core/shared/src/main/scala/zio/prelude/Equal.scala @@ -18,6 +18,7 @@ package zio.prelude import zio.Exit.{Failure, Success} import zio.prelude.coherent.{HashOrd, HashPartialOrd} +import zio.prelude.newtypes.Nested import zio.test.TestResult import zio.test.laws.{Lawful, Laws} import zio.{Cause, Chunk, Exit, Fiber, NonEmptyChunk, ZTrace} @@ -220,6 +221,17 @@ object Equal extends Lawful[Equal] { def default[A]: Equal[A] = DefaultEqual + implicit def IdEqual[A: Equal]: Equal[Id[A]] = new Equal[Id[A]] { + override protected def checkEqual(l: Id[A], r: Id[A]): Boolean = + Id.unwrap[A](l) === Id.unwrap[A](r) + } + + implicit def NestedEqual[F[+_], G[+_], A](implicit eqFGA: Equal[F[G[A]]]): Equal[Nested[F, G, A]] = + new Equal[Nested[F, G, A]] { + override protected def checkEqual(l: Nested[F, G, A], r: Nested[F, G, A]): Boolean = + eqFGA.checkEqual(Nested.unwrap[F[G[A]]](l), Nested.unwrap[F[G[A]]](r)) + } + /** * `Hash` and `Ord` (and thus also `Equal`) instance for `Boolean` values. */ diff --git a/core/shared/src/main/scala/zio/prelude/newtypes/package.scala b/core/shared/src/main/scala/zio/prelude/newtypes/package.scala index 8f573f59a..3bc19ca52 100644 --- a/core/shared/src/main/scala/zio/prelude/newtypes/package.scala +++ b/core/shared/src/main/scala/zio/prelude/newtypes/package.scala @@ -108,6 +108,16 @@ package object newtypes { type FailureOut[+A] = FailureOut.Type[A] + /** + * A newtype representing Right-to-left composition of functors. + * If F[_] and G[_] are both Covariant, then Nested[F, G, *] is also a Covariant + * If F[_] and G[_] are both IdentityBoth, then Nested[F, G, *] is also an IdentityBoth + * If F[_] and G[_] are both Traversable, then Nested[F, G, *] is also a Traversable + */ + object Nested extends NewtypeF + + type Nested[F[+_], G[+_], +A] = Nested.Type[F[G[A]]] + object Natural extends SubtypeSmart[Int](isGreaterThanEqualTo(0)) { val one: Natural = From c085ce640f643f077a4c24cf8f9c600b72008ccd Mon Sep 17 00:00:00 2001 From: Zach Albia Date: Sun, 1 Aug 2021 18:08:47 +0800 Subject: [PATCH 2/5] fix dotty compile error --- core/shared/src/main/scala/zio/prelude/Covariant.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/shared/src/main/scala/zio/prelude/Covariant.scala b/core/shared/src/main/scala/zio/prelude/Covariant.scala index 899d333b6..515bed4ba 100644 --- a/core/shared/src/main/scala/zio/prelude/Covariant.scala +++ b/core/shared/src/main/scala/zio/prelude/Covariant.scala @@ -124,7 +124,7 @@ object Covariant extends LawfulF.Covariant[CovariantDeriveEqual, Equal] { new Covariant[({ type lambda[+A] = Nested[F, G, A] })#lambda] { private lazy val composedCovariant = F.compose(G) - override def map[A, B](f: A => B): Nested[F, G, A] => Nested[F, G, B] = { x: Nested[F, G, A] => + override def map[A, B](f: A => B): Nested[F, G, A] => Nested[F, G, B] = { (x: Nested[F, G, A]) => Nested(composedCovariant.map(f)(Nested.unwrap[F[G[A]]](x))) } } From 4b104b9efb1fb0a3b90699b221dc6043c4250e9f Mon Sep 17 00:00:00 2001 From: Zach Albia Date: Sun, 1 Aug 2021 18:19:17 +0800 Subject: [PATCH 3/5] run fix --- core/shared/src/main/scala/zio/prelude/Derive.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/Derive.scala b/core/shared/src/main/scala/zio/prelude/Derive.scala index 2fb8d2bd2..6187d09ed 100644 --- a/core/shared/src/main/scala/zio/prelude/Derive.scala +++ b/core/shared/src/main/scala/zio/prelude/Derive.scala @@ -16,8 +16,8 @@ package zio.prelude -import zio.{Cause, Chunk, Exit, NonEmptyChunk} import zio.prelude.newtypes.Nested +import zio.{Cause, Chunk, Exit, NonEmptyChunk} import scala.util.Try @@ -48,8 +48,7 @@ object Derive { def apply[F[_], Typeclass[_]](implicit derive: Derive[F, Typeclass]): Derive[F, Typeclass] = derive - - /** + /** * The `DeriveEqual` instance for `Id`. */ implicit val IdDeriveEqual: Derive[Id, Equal] = From 0cb9e17afbc38cc04ff53d934ba0d0d4a466bf27 Mon Sep 17 00:00:00 2001 From: Zach Albia Date: Wed, 6 Oct 2021 10:56:05 +0800 Subject: [PATCH 4/5] reorder typeclass instances --- .../scala/zio/prelude/AssociativeBoth.scala | 22 ++++---- .../src/main/scala/zio/prelude/Derive.scala | 52 +++++++++---------- .../src/main/scala/zio/prelude/Equal.scala | 22 ++++---- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala index f3ed65b2d..ba467c5fb 100644 --- a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala +++ b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala @@ -1158,6 +1158,17 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar Id(Id.unwrap(fa) -> Id.unwrap(fb)) } + /** + * The `IdentityBoth` (and `AssociativeBoth`) instance for `List`. + */ + implicit val ListIdentityBoth: IdentityBoth[List] = + new IdentityBoth[List] { + val any: List[Any] = + List(()) + + def both[A, B](fa: => List[A], fb: => List[B]): List[(A, B)] = fa.flatMap(a => fb.map(b => (a, b))) + } + /** * The `IdentityBoth` (and `AssociativeBoth`) instance for `Nested[F, G, A]`. */ @@ -1173,17 +1184,6 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar } } - /** - * The `IdentityBoth` (and `AssociativeBoth`) instance for `List`. - */ - implicit val ListIdentityBoth: IdentityBoth[List] = - new IdentityBoth[List] { - val any: List[Any] = - List(()) - - def both[A, B](fa: => List[A], fb: => List[B]): List[(A, B)] = fa.flatMap(a => fb.map(b => (a, b))) - } - /** * The `AssociativeBoth` instance for `NonEmptyChunk`. */ diff --git a/core/shared/src/main/scala/zio/prelude/Derive.scala b/core/shared/src/main/scala/zio/prelude/Derive.scala index 6187d09ed..0120691c1 100644 --- a/core/shared/src/main/scala/zio/prelude/Derive.scala +++ b/core/shared/src/main/scala/zio/prelude/Derive.scala @@ -49,32 +49,29 @@ object Derive { derive /** - * The `DeriveEqual` instance for `Id`. + * The `DeriveEqual` instance for `Chunk`. */ - implicit val IdDeriveEqual: Derive[Id, Equal] = - new Derive[Id, Equal] { - override def derive[A: Equal]: Equal[Id[A]] = Id.wrapAll(Equal[A]) + implicit val ChunkDeriveEqual: Derive[Chunk, Equal] = + new Derive[Chunk, Equal] { + def derive[A: Equal]: Equal[Chunk[A]] = + Equal.ChunkEqual } /** - * The `DeriveEqual` instance for `Nested`. + * The `DeriveEqual` instance for `Either`. */ - implicit def NestedDeriveEqual[F[+_], G[+_]](implicit - F: Derive[F, Equal], - G: Derive[G, Equal] - ): Derive[({ type lambda[A] = Nested[F, G, A] })#lambda, Equal] = - new Derive[({ type lambda[A] = Nested[F, G, A] })#lambda, Equal] { - override def derive[A: Equal]: Equal[Nested[F, G, A]] = - Equal.NestedEqual(F.derive(G.derive[A])) + implicit def EitherDeriveEqual[E: Equal]: DeriveEqual[({ type lambda[+x] = Either[E, x] })#lambda] = + new DeriveEqual[({ type lambda[+x] = Either[E, x] })#lambda] { + def derive[A: Equal]: Equal[Either[E, A]] = + Equal.EitherEqual } /** - * The `DeriveEqual` instance for `Chunk`. + * The `DeriveEqual` instance for `Id`. */ - implicit val ChunkDeriveEqual: Derive[Chunk, Equal] = - new Derive[Chunk, Equal] { - def derive[A: Equal]: Equal[Chunk[A]] = - Equal.ChunkEqual + implicit val IdDeriveEqual: Derive[Id, Equal] = + new Derive[Id, Equal] { + override def derive[A: Equal]: Equal[Id[A]] = Id.wrapAll(Equal[A]) } /** @@ -86,15 +83,6 @@ object Derive { Equal.ListEqual } - /** - * The `DeriveEqual` instance for `Either`. - */ - implicit def EitherDeriveEqual[E: Equal]: DeriveEqual[({ type lambda[+x] = Either[E, x] })#lambda] = - new DeriveEqual[({ type lambda[+x] = Either[E, x] })#lambda] { - def derive[A: Equal]: Equal[Either[E, A]] = - Equal.EitherEqual - } - /** * The `DeriveEqual` instance for `Map`. */ @@ -104,6 +92,18 @@ object Derive { Equal.MapPartialOrd } + /** + * The `DeriveEqual` instance for `Nested`. + */ + implicit def NestedDeriveEqual[F[+_], G[+_]](implicit + F: Derive[F, Equal], + G: Derive[G, Equal] + ): Derive[({ type lambda[A] = Nested[F, G, A] })#lambda, Equal] = + new Derive[({ type lambda[A] = Nested[F, G, A] })#lambda, Equal] { + override def derive[A: Equal]: Equal[Nested[F, G, A]] = + Equal.NestedEqual(F.derive(G.derive[A])) + } + /** * The `DeriveEqual` instance for `NonEmptyChunk`. */ diff --git a/core/shared/src/main/scala/zio/prelude/Equal.scala b/core/shared/src/main/scala/zio/prelude/Equal.scala index 22ce8e22a..cb8f5eec8 100644 --- a/core/shared/src/main/scala/zio/prelude/Equal.scala +++ b/core/shared/src/main/scala/zio/prelude/Equal.scala @@ -221,17 +221,6 @@ object Equal extends Lawful[Equal] { def default[A]: Equal[A] = DefaultEqual - implicit def IdEqual[A: Equal]: Equal[Id[A]] = new Equal[Id[A]] { - override protected def checkEqual(l: Id[A], r: Id[A]): Boolean = - Id.unwrap[A](l) === Id.unwrap[A](r) - } - - implicit def NestedEqual[F[+_], G[+_], A](implicit eqFGA: Equal[F[G[A]]]): Equal[Nested[F, G, A]] = - new Equal[Nested[F, G, A]] { - override protected def checkEqual(l: Nested[F, G, A], r: Nested[F, G, A]): Boolean = - eqFGA.checkEqual(Nested.unwrap[F[G[A]]](l), Nested.unwrap[F[G[A]]](r)) - } - /** * `Hash` and `Ord` (and thus also `Equal`) instance for `Boolean` values. */ @@ -329,6 +318,11 @@ object Equal extends Lawful[Equal] { implicit lazy val FiberIdHashOrd: Hash[Fiber.Id] with Ord[Fiber.Id] = HashOrd.derive[(Long, Long)].contramap[Fiber.Id](fid => (fid.startTimeMillis, fid.seqNumber)) + implicit def IdEqual[A: Equal]: Equal[Id[A]] = new Equal[Id[A]] { + override protected def checkEqual(l: Id[A], r: Id[A]): Boolean = + Id.unwrap[A](l) === Id.unwrap[A](r) + } + /** * `Hash` and `Ord` (and thus also `Equal`) instance for `Int` values. */ @@ -361,6 +355,12 @@ object Equal extends Lawful[Equal] { l.forall { case (key, value) => r.get(key).fold(false)(_ === value) } } + implicit def NestedEqual[F[+_], G[+_], A](implicit eqFGA: Equal[F[G[A]]]): Equal[Nested[F, G, A]] = + new Equal[Nested[F, G, A]] { + override protected def checkEqual(l: Nested[F, G, A], r: Nested[F, G, A]): Boolean = + eqFGA.checkEqual(Nested.unwrap[F[G[A]]](l), Nested.unwrap[F[G[A]]](r)) + } + /** * Derives an `Equal[NonEmptyChunk[A]]` given an `Equal[A]`. */ From 1347d4620b74d5f8346acb80f53c67e29af5fbb5 Mon Sep 17 00:00:00 2001 From: Zach Albia Date: Fri, 29 Oct 2021 17:11:56 +0800 Subject: [PATCH 5/5] add forEachIdentityLaw --- .../src/main/scala/zio/prelude/laws/ForEachLaws.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/laws/shared/src/main/scala/zio/prelude/laws/ForEachLaws.scala b/laws/shared/src/main/scala/zio/prelude/laws/ForEachLaws.scala index f0719fcd0..ad70ff79c 100644 --- a/laws/shared/src/main/scala/zio/prelude/laws/ForEachLaws.scala +++ b/laws/shared/src/main/scala/zio/prelude/laws/ForEachLaws.scala @@ -18,10 +18,17 @@ package zio.prelude.laws import zio.prelude._ import zio.prelude.coherent.DeriveEqualForEach +import zio.test.TestResult import zio.test.laws._ object ForEachLaws extends LawfulF.Covariant[DeriveEqualForEach, Equal] { + lazy val forEachIdentityLaw: LawsF.Covariant[DeriveEqualForEach, Equal] = + new LawsF.Covariant.Law1[DeriveEqualForEach, Equal]("forEachIdentityLaw") { + def apply[F[+_]: DeriveEqualForEach, A: Equal](fa: F[A]): TestResult = + fa.forEach(Id(_)) <-> Id(fa) + } + /** * The set of all laws that instances of `ForEach` must satisfy. */