From 1fec9ff4ebdc57e47022bb2b5f113f30710d98f1 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Wed, 25 Nov 2020 13:06:55 +0800 Subject: [PATCH 01/13] define identity law for Traversable --- .../main/scala/zio/prelude/Traversable.scala | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index 0474ab985..6c4458bb0 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -1,9 +1,10 @@ package zio.prelude import zio.prelude.coherent.DeriveEqualTraversable -import zio.prelude.newtypes.{ And, First, Max, Min, Or, Prod, Sum } +import zio.prelude.newtypes.{And, First, Max, Min, Or, Prod, Sum} +import zio.test.TestResult import zio.test.laws._ -import zio.{ Chunk, ChunkBuilder, NonEmptyChunk } +import zio.{Chunk, ChunkBuilder, NonEmptyChunk} /** * `Traversable` is an abstraction that describes the ability to iterate over @@ -269,11 +270,25 @@ trait Traversable[F[+_]] extends Covariant[F] { object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { +// need to implement natural transformations for this property? +// val naturalityLaw: LawsF.Covariant[DeriveEqualTraversable, Equal] = ??? + + /** + * Traversing by `Id` is equivalent to mapping. + */ + val identityLaw: LawsF.Covariant[DeriveEqualTraversable, Equal] = { + // g: B => C is not needed here, but no appropriate law constructor exists in zio-test + new LawsF.Covariant.ComposeLaw[DeriveEqualTraversable, Equal]("identityLaw") { + def apply[F[+_]: DeriveEqualTraversable, A: Equal, B: Equal, C: Equal](fa: F[A], f: A => B, g: B => C): TestResult = + Id.unwrap(fa.foreach(a => Id[B](f(a)))) <-> fa.map(f) + } + } + /** * The set of all laws that instances of `Traversable` must satisfy. */ val laws: LawsF.Covariant[DeriveEqualTraversable, Equal] = - Covariant.laws + Covariant.laws + identityLaw /** * Summons an implicit `Traversable`. From 4a9e2f93a8b2f7910c6e94943520c6e41ec7fa67 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Sat, 28 Nov 2020 16:21:40 +0800 Subject: [PATCH 02/13] draft laws for Traversable --- .../main/scala/zio/prelude/Instances.scala | 46 ++++++++++++ .../main/scala/zio/prelude/Traversable.scala | 71 +++++++++++++++---- .../main/scala/zio/prelude/laws/ZLawsF.scala | 41 +++++++++++ zio-prelude.code-workspace | 12 ---- 4 files changed, 145 insertions(+), 25 deletions(-) create mode 100644 core/shared/src/main/scala/zio/prelude/Instances.scala create mode 100644 core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala delete mode 100644 zio-prelude.code-workspace diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala new file mode 100644 index 000000000..8dc3fe538 --- /dev/null +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -0,0 +1,46 @@ +package zio.prelude + +import zio.prelude.classic.Applicative + +// not sure about putting this here +object Instances { + + object Applicative { + def apply[F[+_]: Applicative]: Applicative[F] = new Covariant[F] with IdentityBoth[F] { self => + def map[A, B](f: A => B): F[A] => F[B] = Covariant[F].map(f) + + def any: F[Any] = IdentityBoth[F].any + + def both[A, B](fa: => F[A], fb: => F[B]): F[(A, B)] = IdentityBoth[F].both(fa, fb) + } + + final def compose[F[+_]: Applicative, G[+_]: Applicative]( + af: Applicative[F], + ag: Applicative[G] + ): Applicative[({ type lambda[+A] = F[G[A]] })#lambda] = + new Covariant[({ type lambda[+A] = F[G[A]] })#lambda] with IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] { + def map[A, B](f: A => B): F[G[A]] => F[G[B]] = af.map(ag.map(f)) + + def any: F[G[Any]] = af.map[Any, G[Any]](_ => ag.any)(af.any) + + def both[A, B](fa: => F[G[A]], fb: => F[G[B]]): F[G[(A, B)]] = + af.map[(G[A], G[B]), G[(A, B)]] { case (ga, gb) => ag.both(ga, gb) }(af.both(fa, fb)) + } + } + + trait ApplicativeDeriveEqual[F[+_]] extends Covariant[F] with IdentityBoth[F] with DeriveEqual[F] + + object ApplicativeDeriveEqual { + implicit def derive[F[+_]](implicit applicative0: Applicative[F], deriveEqual0: DeriveEqual[F]): ApplicativeDeriveEqual[F] = + new ApplicativeDeriveEqual[F] { + def any: F[Any] = + applicative0.any + def both[A, B](fa: => F[A], fb: => F[B]): F[(A, B)] = + applicative0.both(fa, fb) + def derive[A: Equal]: Equal[F[A]] = + deriveEqual0.derive + def map[A, B](f: A => B): F[A] => F[B] = + applicative0.map(f) + } + } +} diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index 6c4458bb0..177f06a58 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -1,10 +1,10 @@ package zio.prelude import zio.prelude.coherent.DeriveEqualTraversable -import zio.prelude.newtypes.{And, First, Max, Min, Or, Prod, Sum} +import zio.prelude.newtypes.{ And, First, Max, Min, Or, Prod, Sum } import zio.test.TestResult import zio.test.laws._ -import zio.{Chunk, ChunkBuilder, NonEmptyChunk} +import zio.{ Chunk, ChunkBuilder, NonEmptyChunk } /** * `Traversable` is an abstraction that describes the ability to iterate over @@ -269,20 +269,65 @@ trait Traversable[F[+_]] extends Covariant[F] { } object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { + import zio.prelude.classic.Applicative + import zio.prelude.Instances.Applicative + import zio.prelude.Instances.ApplicativeDeriveEqual + import zio.prelude.laws.ZLawsF + + /** Traversing by `Id` is equivalent to mapping. */ + val identityLaw: LawsF.Covariant[DeriveEqualTraversable, Equal] = + new ZLawsF.Traversable.MapLaw[DeriveEqualTraversable, Equal]("identityLaw") { + def apply[F[+_]: DeriveEqualTraversable, A: Equal, B: Equal](fa: F[A], f: A => B): TestResult = + Id.unwrap(fa.foreach(f.andThen(Id[B]))) <-> fa.map(f) + } -// need to implement natural transformations for this property? -// val naturalityLaw: LawsF.Covariant[DeriveEqualTraversable, Equal] = ??? + /** Two sequentially dependent effects can be fused into one, their composition */ + val sequentialFusionLaw: ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal] = + new ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal]("sequentialFusionLaw") { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( + fa: F[A], + agb: A => G[B], + bhc: B => H[C] + ): TestResult = { + type GH[+A] = G[H[A]] + // should I make infix syntax for this? + implicit val gh: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) + implicit lazy val ghEqual: ApplicativeDeriveEqual[GH] = ApplicativeDeriveEqual.derive[GH] + + val ghfc1: GH[F[C]] = fa.foreach(agb).map(_.foreach(bhc)) + // is there any way to restate this? + val ghfc2: GH[F[C]] = fa.map(agb.andThen(_.map(bhc)).andThen(ghc => gh.both(ghc, gh.any))).foreach(_.map(_._1)) + ghfc1 <-> ghfc2 + } + } - /** - * Traversing by `Id` is equivalent to mapping. - */ - val identityLaw: LawsF.Covariant[DeriveEqualTraversable, Equal] = { - // g: B => C is not needed here, but no appropriate law constructor exists in zio-test - new LawsF.Covariant.ComposeLaw[DeriveEqualTraversable, Equal]("identityLaw") { - def apply[F[+_]: DeriveEqualTraversable, A: Equal, B: Equal, C: Equal](fa: F[A], f: A => B, g: B => C): TestResult = - Id.unwrap(fa.foreach(a => Id[B](f(a)))) <-> fa.map(f) + /** Traversal with succeed is the same as applying succeed directly */ + val purityLaw: ZLawsF.Traversable.PurityLaw[DeriveEqualTraversable, Applicative, Equal] = + new ZLawsF.Traversable.PurityLaw[DeriveEqualTraversable, Applicative, Equal]("purityLaw") { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, A: Equal](fa: F[A]): TestResult = { + implicit lazy val gEqual: ApplicativeDeriveEqual[G] = ApplicativeDeriveEqual.derive[G] + // should I implement `Applicative#succeed`? + fa.foreach(Applicative[G].any.as[A](_)) <-> Applicative[G].any.as(fa) + } + } + + val naturalityLaw: ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal] = + new ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( + fa: F[A], + agb: A => G[B], + bhc: B => H[C] + ): TestResult = ??? + } + + val parallelFusionLaw = + new ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( + fa: F[A], + agb: A => G[B], + bhc: B => H[C] + ): TestResult = ??? } - } /** * The set of all laws that instances of `Traversable` must satisfy. diff --git a/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala new file mode 100644 index 000000000..664c265ad --- /dev/null +++ b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala @@ -0,0 +1,41 @@ +package zio.prelude.laws + +import zio.URIO +import zio.test.laws.GenF +import zio.test.{ Gen, TestConfig, TestResult, check } + +object ZLawsF { + + object Traversable { + abstract class MapLaw[-CapsF[_[+_]], -Caps[_]](label: String) + extends zio.test.laws.ZLawsF.Covariant[CapsF, Caps, Any] { self => + def apply[F[+_]: CapsF, A: Caps, B: Caps](fa: F[A], f: A => B): TestResult + + final def run[R <: TestConfig, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = + check(genF(gen), Gen.function(gen))(apply(_, _).map(_.label(label))) + } + + abstract class PurityLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]](label: String) { self => + def apply[F[+_]: CapsF, G[+_]: CapsG, A: Caps](fa: F[A]): TestResult + + final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = + check(genF(gen))(apply(_).map(_.label(label))) + } + + abstract class FusionLaw[-CapsF[_[+_]], -CapsGH[_[+_]], -Caps[_]](label: String) { self => + def apply[F[+_]: CapsF, G[+_]: CapsGH, H[+_]: CapsGH, A: Caps, B: Caps, C: Caps]( + fa: F[A], + agb: A => G[B], + bhc: B => H[C] + ): TestResult + + final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsGH, H[+_]: CapsGH, A: Caps, B: Caps, C: Caps]( + genF: GenF[R, F], + genG: GenF[R, G], + genH: GenF[R, H], + gen: Gen[R, A] + ): URIO[R, TestResult] = + check(genF(gen), Gen.function(genG(gen)), Gen.function(genH(gen)))(apply(_, _, _).map(_.label(label))) + } + } +} diff --git a/zio-prelude.code-workspace b/zio-prelude.code-workspace deleted file mode 100644 index f574ba1bc..000000000 --- a/zio-prelude.code-workspace +++ /dev/null @@ -1,12 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": { - "files.watcherExclude": { - "**/target": true - } - } -} \ No newline at end of file From ad2341cd8881cc238ebb4c5ed3cf86d3ac627920 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Sat, 28 Nov 2020 17:50:06 +0800 Subject: [PATCH 03/13] run scalafix, revert deletion of *.code-workspace --- .../src/main/scala/zio/prelude/Instances.scala | 11 +++++++---- .../src/main/scala/zio/prelude/Traversable.scala | 2 +- .../src/main/scala/zio/prelude/laws/ZLawsF.scala | 5 ++++- zio-prelude.code-workspace | 12 ++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 zio-prelude.code-workspace diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index 8dc3fe538..43744c3ea 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -31,15 +31,18 @@ object Instances { trait ApplicativeDeriveEqual[F[+_]] extends Covariant[F] with IdentityBoth[F] with DeriveEqual[F] object ApplicativeDeriveEqual { - implicit def derive[F[+_]](implicit applicative0: Applicative[F], deriveEqual0: DeriveEqual[F]): ApplicativeDeriveEqual[F] = + implicit def derive[F[+_]](implicit + applicative0: Applicative[F], + deriveEqual0: DeriveEqual[F] + ): ApplicativeDeriveEqual[F] = new ApplicativeDeriveEqual[F] { - def any: F[Any] = + def any: F[Any] = applicative0.any def both[A, B](fa: => F[A], fb: => F[B]): F[(A, B)] = applicative0.both(fa, fb) - def derive[A: Equal]: Equal[F[A]] = + def derive[A: Equal]: Equal[F[A]] = deriveEqual0.derive - def map[A, B](f: A => B): F[A] => F[B] = + def map[A, B](f: A => B): F[A] => F[B] = applicative0.map(f) } } diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index 177f06a58..d94d41031 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -320,7 +320,7 @@ object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { ): TestResult = ??? } - val parallelFusionLaw = + val parallelFusionLaw: ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal] = new ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( fa: F[A], diff --git a/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala index 664c265ad..383fd348d 100644 --- a/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala +++ b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala @@ -18,7 +18,10 @@ object ZLawsF { abstract class PurityLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]](label: String) { self => def apply[F[+_]: CapsF, G[+_]: CapsG, A: Caps](fa: F[A]): TestResult - final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = + final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( + genF: GenF[R, F], + gen: Gen[R, A] + ): URIO[R, TestResult] = check(genF(gen))(apply(_).map(_.label(label))) } diff --git a/zio-prelude.code-workspace b/zio-prelude.code-workspace new file mode 100644 index 000000000..f574ba1bc --- /dev/null +++ b/zio-prelude.code-workspace @@ -0,0 +1,12 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "files.watcherExclude": { + "**/target": true + } + } +} \ No newline at end of file From 3f34d8d191a9830694975e166dfc67899404fac4 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Sat, 28 Nov 2020 20:26:18 +0800 Subject: [PATCH 04/13] restate right side of sequential fusion law, draft naturality law --- .../main/scala/zio/prelude/Traversable.scala | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index d94d41031..663980ec8 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -290,13 +290,11 @@ object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { bhc: B => H[C] ): TestResult = { type GH[+A] = G[H[A]] - // should I make infix syntax for this? - implicit val gh: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) + implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) implicit lazy val ghEqual: ApplicativeDeriveEqual[GH] = ApplicativeDeriveEqual.derive[GH] val ghfc1: GH[F[C]] = fa.foreach(agb).map(_.foreach(bhc)) - // is there any way to restate this? - val ghfc2: GH[F[C]] = fa.map(agb.andThen(_.map(bhc)).andThen(ghc => gh.both(ghc, gh.any))).foreach(_.map(_._1)) + val ghfc2: GH[F[C]] = fa.foreach(a => GH.both(agb(a).map(bhc), GH.any).map(_._1)) ghfc1 <-> ghfc2 } } @@ -317,7 +315,16 @@ object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { fa: F[A], agb: A => G[B], bhc: B => H[C] - ): TestResult = ??? + ): TestResult = { + val F = Traversable[F] + type GH[+A] = G[H[A]] + implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) + implicit lazy val ghEqual: ApplicativeDeriveEqual[GH] = ApplicativeDeriveEqual.derive[GH] + + val ghfc1: GH[F[C]] = F.flip(fa.map(agb)).map(b => F.flip(b.map(bhc))) + val ghfc2: GH[F[C]] = F.flip(fa.map(a => GH.both(agb(a).map(bhc), GH.any).map(_._1))) + ghfc1 <-> ghfc2 + } } val parallelFusionLaw: ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal] = From d47df2c54d49890a183b644ca177131ed6af71e3 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Sat, 28 Nov 2020 21:00:47 +0800 Subject: [PATCH 05/13] write Applicative.product for parallelFusionLaw --- .../main/scala/zio/prelude/Instances.scala | 25 +++++++++++++++---- .../main/scala/zio/prelude/Traversable.scala | 20 +++++++++------ .../main/scala/zio/prelude/laws/ZLawsF.scala | 2 +- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index 43744c3ea..04eb8aff4 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -15,16 +15,31 @@ object Instances { } final def compose[F[+_]: Applicative, G[+_]: Applicative]( - af: Applicative[F], - ag: Applicative[G] + F: Applicative[F], + G: Applicative[G] ): Applicative[({ type lambda[+A] = F[G[A]] })#lambda] = new Covariant[({ type lambda[+A] = F[G[A]] })#lambda] with IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] { - def map[A, B](f: A => B): F[G[A]] => F[G[B]] = af.map(ag.map(f)) + def map[A, B](f: A => B): F[G[A]] => F[G[B]] = F.map(G.map(f)) - def any: F[G[Any]] = af.map[Any, G[Any]](_ => ag.any)(af.any) + def any: F[G[Any]] = F.map[Any, G[Any]](_ => G.any)(F.any) def both[A, B](fa: => F[G[A]], fb: => F[G[B]]): F[G[(A, B)]] = - af.map[(G[A], G[B]), G[(A, B)]] { case (ga, gb) => ag.both(ga, gb) }(af.both(fa, fb)) + F.map[(G[A], G[B]), G[(A, B)]] { case (ga, gb) => G.both(ga, gb) }(F.both(fa, fb)) + } + + final def product[F[+_]: Applicative, G[+_]: Applicative]( + F: Applicative[F], + G: Applicative[G] + ): Applicative[({ type lambda[+A] = (F[A], G[A]) })#lambda] = + new Covariant[({ type lambda[+A] = (F[A], G[A]) })#lambda] + with IdentityBoth[({ type lambda[+A] = (F[A], G[A]) })#lambda] { + def map[A, B](f: A => B): ((F[A], G[A])) => (F[B], G[B]) = + (faga: (F[A], G[A])) => (faga._1.map(f), faga._2.map(f)) + + def any: (F[Any], G[Any]) = (F.any, G.any) + + def both[A, B](faga: => (F[A], G[A]), fbgb: => (F[B], G[B])): (F[(A, B)], G[(A, B)]) = + (F.both(faga._1, fbgb._1), G.both(faga._2, fbgb._2)) } } diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index 663980ec8..b9575f81d 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -282,8 +282,8 @@ object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { } /** Two sequentially dependent effects can be fused into one, their composition */ - val sequentialFusionLaw: ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal] = - new ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal]("sequentialFusionLaw") { + val sequentialFusionLaw: ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal] = + new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal]("sequentialFusionLaw") { def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( fa: F[A], agb: A => G[B], @@ -309,8 +309,8 @@ object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { } } - val naturalityLaw: ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal] = - new ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { + val naturalityLaw: ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal] = + new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( fa: F[A], agb: A => G[B], @@ -327,13 +327,19 @@ object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { } } - val parallelFusionLaw: ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal] = - new ZLawsF.Traversable.FusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { + val parallelFusionLaw: ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal] = + new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( fa: F[A], agb: A => G[B], bhc: B => H[C] - ): TestResult = ??? + ): TestResult = { + type GH[+A] = (G[A], H[A]) + implicit val GH: Applicative[GH] = Applicative.product(Applicative[G], Applicative[H]) + implicit lazy val ghEqual: ApplicativeDeriveEqual[GH] = ApplicativeDeriveEqual.derive[GH] + + ??? + } } /** diff --git a/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala index 383fd348d..cecaae04c 100644 --- a/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala +++ b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala @@ -25,7 +25,7 @@ object ZLawsF { check(genF(gen))(apply(_).map(_.label(label))) } - abstract class FusionLaw[-CapsF[_[+_]], -CapsGH[_[+_]], -Caps[_]](label: String) { self => + abstract class SequentialFusionLaw[-CapsF[_[+_]], -CapsGH[_[+_]], -Caps[_]](label: String) { self => def apply[F[+_]: CapsF, G[+_]: CapsGH, H[+_]: CapsGH, A: Caps, B: Caps, C: Caps]( fa: F[A], agb: A => G[B], From c0ec4fe23b84111337491de2857d8246567674d5 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Sun, 29 Nov 2020 18:56:53 +0800 Subject: [PATCH 06/13] implement Traversable laws using LawfulF.Traversable --- .../main/scala/zio/prelude/Instances.scala | 6 +- .../zio/prelude/NonEmptyTraversable.scala | 8 +- .../main/scala/zio/prelude/Traversable.scala | 91 ++++++++-------- .../scala/zio/prelude/laws/ZLawfulF.scala | 15 +++ .../main/scala/zio/prelude/laws/ZLawsF.scala | 102 +++++++++++++++--- .../main/scala/zio/prelude/laws/package.scala | 33 ++++++ .../scala/zio/prelude/NonEmptyListSpec.scala | 5 +- .../zio/prelude/NonEmptyTraversableSpec.scala | 4 +- .../scala/zio/prelude/TraversableSpec.scala | 14 +-- 9 files changed, 208 insertions(+), 70 deletions(-) create mode 100644 core/shared/src/main/scala/zio/prelude/laws/ZLawfulF.scala create mode 100644 core/shared/src/main/scala/zio/prelude/laws/package.scala diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index 04eb8aff4..c19ed03a1 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -1,12 +1,14 @@ package zio.prelude +import zio.prelude.AssociativeBoth.OptionIdentityBoth +import zio.prelude.Invariant.OptionTraversable import zio.prelude.classic.Applicative // not sure about putting this here object Instances { object Applicative { - def apply[F[+_]: Applicative]: Applicative[F] = new Covariant[F] with IdentityBoth[F] { self => + def apply[F[+_]: Covariant: IdentityBoth]: Applicative[F] = new Covariant[F] with IdentityBoth[F] { self => def map[A, B](f: A => B): F[A] => F[B] = Covariant[F].map(f) def any: F[Any] = IdentityBoth[F].any @@ -41,6 +43,8 @@ object Instances { def both[A, B](faga: => (F[A], G[A]), fbgb: => (F[B], G[B])): (F[(A, B)], G[(A, B)]) = (F.both(faga._1, fbgb._1), G.both(faga._2, fbgb._2)) } + + implicit val applicativeOption: Applicative[Option] = Applicative[Option](OptionTraversable, OptionIdentityBoth) } trait ApplicativeDeriveEqual[F[+_]] extends Covariant[F] with IdentityBoth[F] with DeriveEqual[F] diff --git a/core/shared/src/main/scala/zio/prelude/NonEmptyTraversable.scala b/core/shared/src/main/scala/zio/prelude/NonEmptyTraversable.scala index 05c7591c0..fc4dbab8d 100644 --- a/core/shared/src/main/scala/zio/prelude/NonEmptyTraversable.scala +++ b/core/shared/src/main/scala/zio/prelude/NonEmptyTraversable.scala @@ -1,8 +1,8 @@ package zio.prelude import zio.prelude.coherent.DeriveEqualNonEmptyTraversable +import zio.prelude.laws._ import zio.prelude.newtypes.{ Max, Min } -import zio.test.laws._ import zio.{ ChunkBuilder, NonEmptyChunk } /** @@ -134,12 +134,14 @@ trait NonEmptyTraversable[F[+_]] extends Traversable[F] { def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] = reduceMapLeft(fa)(NonEmptyList.single)((as, a) => NonEmptyList.cons(a, as)).reverse } -object NonEmptyTraversable extends LawfulF.Covariant[DeriveEqualNonEmptyTraversable, Equal] { + +import zio.prelude.Instances.ApplicativeDeriveEqual +object NonEmptyTraversable extends LawfulF.Traversable[DeriveEqualNonEmptyTraversable, ApplicativeDeriveEqual, Equal] { /** * The set of all laws that instances of `NonEmptyTraversable` must satisfy. */ - val laws: LawsF.Covariant[DeriveEqualNonEmptyTraversable, Equal] = + val laws: LawsF.Traversable[DeriveEqualNonEmptyTraversable, ApplicativeDeriveEqual, Equal] = Traversable.laws /** diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index b9575f81d..ffaa16b9d 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -3,7 +3,6 @@ package zio.prelude import zio.prelude.coherent.DeriveEqualTraversable import zio.prelude.newtypes.{ And, First, Max, Min, Or, Prod, Sum } import zio.test.TestResult -import zio.test.laws._ import zio.{ Chunk, ChunkBuilder, NonEmptyChunk } /** @@ -268,85 +267,85 @@ trait Traversable[F[+_]] extends Covariant[F] { ).runResult(0) } -object Traversable extends LawfulF.Covariant[DeriveEqualTraversable, Equal] { +object Traversable + extends zio.prelude.laws.LawfulF.Traversable[DeriveEqualTraversable, Instances.ApplicativeDeriveEqual, Equal] { + import zio.prelude.Instances.ApplicativeDeriveEqual import zio.prelude.classic.Applicative import zio.prelude.Instances.Applicative - import zio.prelude.Instances.ApplicativeDeriveEqual import zio.prelude.laws.ZLawsF /** Traversing by `Id` is equivalent to mapping. */ - val identityLaw: LawsF.Covariant[DeriveEqualTraversable, Equal] = - new ZLawsF.Traversable.MapLaw[DeriveEqualTraversable, Equal]("identityLaw") { + val identityLaw: zio.test.laws.LawsF.Covariant[DeriveEqualTraversable, Equal] = + new ZLawsF.Covariant.MapLaw[DeriveEqualTraversable, Equal]("identityLaw") { def apply[F[+_]: DeriveEqualTraversable, A: Equal, B: Equal](fa: F[A], f: A => B): TestResult = Id.unwrap(fa.foreach(f.andThen(Id[B]))) <-> fa.map(f) } - /** Two sequentially dependent effects can be fused into one, their composition */ - val sequentialFusionLaw: ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal] = - new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal]("sequentialFusionLaw") { - def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( - fa: F[A], - agb: A => G[B], - bhc: B => H[C] - ): TestResult = { + /** Two sequentially dependent effects can be fused into their composition */ + val sequentialFusionLaw: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = + new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]( + "sequentialFusionLaw" + ) { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, H[ + +_ + ]: ApplicativeDeriveEqual, A, B, C: Equal](fa: F[A], agb: A => G[B], bhc: B => H[C]): TestResult = { type GH[+A] = G[H[A]] - implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) - implicit lazy val ghEqual: ApplicativeDeriveEqual[GH] = ApplicativeDeriveEqual.derive[GH] - - val ghfc1: GH[F[C]] = fa.foreach(agb).map(_.foreach(bhc)) - val ghfc2: GH[F[C]] = fa.foreach(a => GH.both(agb(a).map(bhc), GH.any).map(_._1)) - ghfc1 <-> ghfc2 + implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) + val left: G[H[F[C]]] = fa.foreach(agb).map(_.foreach(bhc)) + val right: G[H[F[C]]] = fa.foreach(a => GH.both(agb(a).map(bhc), GH.any).map(_._1)) + left <-> right } } - /** Traversal with succeed is the same as applying succeed directly */ - val purityLaw: ZLawsF.Traversable.PurityLaw[DeriveEqualTraversable, Applicative, Equal] = - new ZLawsF.Traversable.PurityLaw[DeriveEqualTraversable, Applicative, Equal]("purityLaw") { - def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, A: Equal](fa: F[A]): TestResult = { - implicit lazy val gEqual: ApplicativeDeriveEqual[G] = ApplicativeDeriveEqual.derive[G] - // should I implement `Applicative#succeed`? + /** Traversal with `succeed` is the same as applying `succeed` directly */ + val purityLaw: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = + new ZLawsF.Traversable.PurityLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]("purityLaw") { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, A: Equal](fa: F[A]): TestResult = fa.foreach(Applicative[G].any.as[A](_)) <-> Applicative[G].any.as(fa) - } } - val naturalityLaw: ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal] = - new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { - def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( + val naturalityLaw: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = + new ZLawsF.Traversable.NaturalityFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]("naturalityLaw") { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, H[+_]: ApplicativeDeriveEqual, A: Equal]( fa: F[A], - agb: A => G[B], - bhc: B => H[C] + aga: A => G[A], + aha: A => H[A] ): TestResult = { - val F = Traversable[F] type GH[+A] = G[H[A]] - implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) - implicit lazy val ghEqual: ApplicativeDeriveEqual[GH] = ApplicativeDeriveEqual.derive[GH] + implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) - val ghfc1: GH[F[C]] = F.flip(fa.map(agb)).map(b => F.flip(b.map(bhc))) - val ghfc2: GH[F[C]] = F.flip(fa.map(a => GH.both(agb(a).map(bhc), GH.any).map(_._1))) - ghfc1 <-> ghfc2 + val left: G[H[F[A]]] = fa.map(aga).flip.map(a => a.map(aha).flip) + val right: G[H[F[A]]] = fa.map(a => GH.both(aga(a).map(aha), GH.any).map(_._1)).flip + left <-> right } } - val parallelFusionLaw: ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal] = - new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, Applicative, Equal]("parallelFusion") { - def apply[F[+_]: DeriveEqualTraversable, G[+_]: Applicative, H[+_]: Applicative, A: Equal, B: Equal, C: Equal]( + /** Two independent effects can be fused into their product */ + val parallelFusionLaw: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = + new ZLawsF.Traversable.ParallelFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]( + "parallelFusionLaw" + ) { + def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, H[ + +_ + ]: ApplicativeDeriveEqual, A, B: Equal]( fa: F[A], agb: A => G[B], - bhc: B => H[C] + ahb: A => H[B] ): TestResult = { type GH[+A] = (G[A], H[A]) - implicit val GH: Applicative[GH] = Applicative.product(Applicative[G], Applicative[H]) - implicit lazy val ghEqual: ApplicativeDeriveEqual[GH] = ApplicativeDeriveEqual.derive[GH] + implicit val GH: Applicative[GH] = Applicative.product(Applicative[G], Applicative[H]) - ??? + val left: (G[F[B]], H[F[B]]) = (fa.foreach(agb), fa.foreach(ahb)) + val right: (G[F[B]], H[F[B]]) = fa.foreach(a => GH.both((agb(a), ahb(a)), GH.any).map(_._1)) + left <-> right } } /** * The set of all laws that instances of `Traversable` must satisfy. */ - val laws: LawsF.Covariant[DeriveEqualTraversable, Equal] = - Covariant.laws + identityLaw + val laws: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = + purityLaw + identityLaw + sequentialFusionLaw + naturalityLaw + parallelFusionLaw + Covariant.laws /** * Summons an implicit `Traversable`. diff --git a/core/shared/src/main/scala/zio/prelude/laws/ZLawfulF.scala b/core/shared/src/main/scala/zio/prelude/laws/ZLawfulF.scala new file mode 100644 index 000000000..efe2b15b8 --- /dev/null +++ b/core/shared/src/main/scala/zio/prelude/laws/ZLawfulF.scala @@ -0,0 +1,15 @@ +package zio.prelude.laws + +object ZLawfulF { + + /** `ZLawful` for traversable type constructors. */ + trait Traversable[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_], -R] { self => + def laws: ZLawsF.Traversable[CapsF, CapsG, Caps, R] + def +[CapsF1[x[+_]] <: CapsF[x], CapsG1[x[+_]] <: CapsG[x], Caps1[x] <: Caps[x], R1 <: R]( + that: Traversable[CapsF1, CapsG1, Caps1, R1] + ): Traversable[CapsF1, CapsG1, Caps1, R1] = + new Traversable[CapsF1, CapsG1, Caps1, R1] { + val laws = self.laws + that.laws + } + } +} diff --git a/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala index cecaae04c..07117fe56 100644 --- a/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala +++ b/core/shared/src/main/scala/zio/prelude/laws/ZLawsF.scala @@ -1,44 +1,122 @@ package zio.prelude.laws -import zio.URIO import zio.test.laws.GenF +import zio.test.laws.ZLawsF.Covariant import zio.test.{ Gen, TestConfig, TestResult, check } +import zio.{ URIO, ZIO } object ZLawsF { - object Traversable { - abstract class MapLaw[-CapsF[_[+_]], -Caps[_]](label: String) - extends zio.test.laws.ZLawsF.Covariant[CapsF, Caps, Any] { self => - def apply[F[+_]: CapsF, A: Caps, B: Caps](fa: F[A], f: A => B): TestResult + abstract class Traversable[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_], -R] { self => + + def run[R1 <: R with TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( + genF: GenF[R1, F], + genG: GenF[R1, G], + gen: Gen[R1, A] + ): ZIO[R1, Nothing, TestResult] + + def +[CapsF1[x[+_]] <: CapsF[x], CapsG1[x[+_]] <: CapsG[x], Caps1[x] <: Caps[x], R1 <: R]( + that: Traversable[CapsF1, CapsG1, Caps1, R1] + ): Traversable[CapsF1, CapsG1, Caps1, R1] = + Traversable.Both(self, that) + def +[CapsF1[x[+_]] <: CapsF[x], Caps1[x] <: Caps[x], R1 <: R]( + that: Covariant[CapsF1, Caps1, R1] + ): Traversable[CapsF1, CapsG, Caps1, R1] = + Traversable.WithCovariant(self, that) + } + + object Covariant { + abstract class MapLaw[-CapsF[_[+_]], -Caps[_]](label: String) extends Covariant[CapsF, Caps, Any] { self => + def apply[F[+_]: CapsF, A: Caps, B: Caps](fa: F[A], f: A => B): TestResult final def run[R <: TestConfig, F[+_]: CapsF, A: Caps](genF: GenF[R, F], gen: Gen[R, A]): URIO[R, TestResult] = check(genF(gen), Gen.function(gen))(apply(_, _).map(_.label(label))) } + } - abstract class PurityLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]](label: String) { self => + object Traversable { + private final case class Both[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_], -R]( + left: Traversable[CapsF, CapsG, Caps, R], + right: Traversable[CapsF, CapsG, Caps, R] + ) extends Traversable[CapsF, CapsG, Caps, R] { + override def run[R1 <: R with TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( + genF: GenF[R1, F], + genG: GenF[R1, G], + gen: Gen[R1, A] + ): ZIO[R1, Nothing, TestResult] = + left.run(genF, genG, gen).zipWith(right.run(genF, genG, gen))(_ && _) + } + + private final case class WithCovariant[-CapsF1[_[+_]], -CapsG[_[+_]], -Caps[_], -R]( + left: Traversable[CapsF1, CapsG, Caps, R], + right: Covariant[CapsF1, Caps, R] + ) extends Traversable[CapsF1, CapsG, Caps, R] { + override def run[R1 <: R with TestConfig, F[+_]: CapsF1, G[+_]: CapsG, A: Caps]( + genF: GenF[R1, F], + genG: GenF[R1, G], + gen: Gen[R1, A] + ): ZIO[R1, Nothing, TestResult] = + left.run(genF, genG, gen).zipWith(right.run(genF, gen))(_ && _) + } + + abstract class PurityLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]](label: String) + extends Traversable[CapsF, CapsG, Caps, Any] { self => def apply[F[+_]: CapsF, G[+_]: CapsG, A: Caps](fa: F[A]): TestResult + final def run[R1 <: TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( + genF: GenF[R1, F], + genG: GenF[R1, G], + gen: Gen[R1, A] + ): ZIO[R1, Nothing, TestResult] = + check(genF(gen))(apply[F, G, A](_).map(_.label(label))) + } + + abstract class NaturalityFusionLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]](label: String) + extends Traversable[CapsF, CapsG, Caps, Any] { + def apply[F[+_]: CapsF, G[+_]: CapsG, H[+_]: CapsG, A: Caps]( + fa: F[A], + aga: A => G[A], + aha: A => H[A] + ): TestResult + final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( genF: GenF[R, F], + genG: GenF[R, G], gen: Gen[R, A] ): URIO[R, TestResult] = - check(genF(gen))(apply(_).map(_.label(label))) + check(genF(gen), Gen.function(genG(gen)), Gen.function(genG(gen)))(apply(_, _, _).map(_.label(label))) } - abstract class SequentialFusionLaw[-CapsF[_[+_]], -CapsGH[_[+_]], -Caps[_]](label: String) { self => - def apply[F[+_]: CapsF, G[+_]: CapsGH, H[+_]: CapsGH, A: Caps, B: Caps, C: Caps]( + abstract class SequentialFusionLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]](label: String) + extends Traversable[CapsF, CapsG, Caps, Any] { self => + def apply[F[+_]: CapsF, G[+_]: CapsG, H[+_]: CapsG, A, B, C: Caps]( fa: F[A], agb: A => G[B], bhc: B => H[C] ): TestResult - final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsGH, H[+_]: CapsGH, A: Caps, B: Caps, C: Caps]( + final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( + genF: GenF[R, F], + genG: GenF[R, G], + gen: Gen[R, A] + ): URIO[R, TestResult] = + check(genF(gen), Gen.function(genG(gen)), Gen.function(genG(gen)))(apply(_, _, _).map(_.label(label))) + } + + abstract class ParallelFusionLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]](label: String) + extends Traversable[CapsF, CapsG, Caps, Any] { + def apply[F[+_]: CapsF, G[+_]: CapsG, H[+_]: CapsG, A, B: Caps]( + fa: F[A], + aga: A => G[B], + aha: A => H[B] + ): TestResult + + final def run[R <: TestConfig, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( genF: GenF[R, F], genG: GenF[R, G], - genH: GenF[R, H], gen: Gen[R, A] ): URIO[R, TestResult] = - check(genF(gen), Gen.function(genG(gen)), Gen.function(genH(gen)))(apply(_, _, _).map(_.label(label))) + check(genF(gen), Gen.function(genG(gen)), Gen.function(genG(gen)))(apply(_, _, _).map(_.label(label))) } } } diff --git a/core/shared/src/main/scala/zio/prelude/laws/package.scala b/core/shared/src/main/scala/zio/prelude/laws/package.scala new file mode 100644 index 000000000..2acd3a9f2 --- /dev/null +++ b/core/shared/src/main/scala/zio/prelude/laws/package.scala @@ -0,0 +1,33 @@ +package zio.prelude + +import zio.URIO +import zio.test.laws.GenF +import zio.test.{ Gen, TestConfig, TestResult } + +package object laws { + object LawfulF { + type Traversable[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]] = ZLawfulF.Traversable[CapsF, CapsG, Caps, Any] + } + + object LawsF { + type Traversable[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]] = ZLawsF.Traversable[CapsF, CapsG, Caps, Any] + + object Covariant { + type MapLaw[-CapsF[_[+_]], -Caps[_]] = ZLawsF.Covariant.MapLaw[CapsF, Caps] + } + + object Traversable { + type PurityLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]] = + ZLawsF.Traversable.PurityLaw[CapsF, CapsG, Caps] + type SequentialFusionLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]] = + ZLawsF.Traversable.SequentialFusionLaw[CapsF, CapsG, Caps] + type NaturalityFusionLaw[-CapsF[_[+_]], -CapsG[_[+_]], -Caps[_]] = + ZLawsF.Traversable.NaturalityFusionLaw[CapsF, CapsG, Caps] + } + } + + def checkAllLaws[CapsF[_[+_]], CapsG[_[+_]], Caps[_], R <: TestConfig, R1 <: R, F[+_]: CapsF, G[+_]: CapsG, A: Caps]( + lawful: ZLawfulF.Traversable[CapsF, CapsG, Caps, R] + )(genF: GenF[R1, F], genG: GenF[R1, G], gen: Gen[R1, A]): URIO[R1, TestResult] = + lawful.laws.run(genF, genG, gen) +} diff --git a/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala b/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala index eca687ec1..49503634a 100644 --- a/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala +++ b/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala @@ -1,5 +1,6 @@ package zio.prelude +import zio.prelude.Instances.Applicative._ import zio.random.Random import zio.test._ import zio.test.laws._ @@ -50,7 +51,9 @@ object NonEmptyListSpec extends DefaultRunnableSpec { testM("hash")(checkAllLaws(Hash)(genNonEmptyList)), testM("identityBoth")(checkAllLaws(IdentityBoth)(GenFs.nonEmptyList, Gen.anyInt)), testM("identityFlatten")(checkAllLaws(IdentityFlatten)(GenFs.nonEmptyList, Gen.anyInt)), - testM("nonEmptyTraversable")(checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyList, Gen.anyInt)), + testM("nonEmptyTraversable")( + zio.prelude.laws.checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyList, GenF.option, Gen.anyInt) + ), testM("ord")(checkAllLaws(Ord)(genNonEmptyList)) ), suite("methods")( diff --git a/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala b/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala index bdb9002dd..fbd0705d0 100644 --- a/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala +++ b/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala @@ -1,5 +1,7 @@ package zio.prelude +import zio.prelude.Instances.Applicative._ +import zio.prelude.laws.checkAllLaws import zio.random.Random import zio.test._ import zio.test.laws._ @@ -21,7 +23,7 @@ object NonEmptyTraversableSpec extends DefaultRunnableSpec { def spec: ZSpec[Environment, Failure] = suite("NonEmptyTraversableSpec")( suite("instances")( - testM("nonEmptyChunk")(checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyChunk, Gen.anyInt)) + testM("nonEmptyChunk")(checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyChunk, GenF.option, Gen.anyInt)) ), suite("combinators")( testM("max") { diff --git a/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala b/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala index 46e27f2a1..ace1992ff 100644 --- a/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala +++ b/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala @@ -1,5 +1,7 @@ package zio.prelude +import zio.prelude.Instances.Applicative._ +import zio.prelude.laws.checkAllLaws import zio.random.Random import zio.test._ import zio.test.laws._ @@ -34,12 +36,12 @@ object TraversableSpec extends DefaultRunnableSpec { def spec: ZSpec[Environment, Failure] = suite("TraversableSpec")( suite("instances")( - testM("chunk")(checkAllLaws(Traversable)(GenF.chunk, Gen.anyInt)), - testM("either")(checkAllLaws(Traversable)(GenFs.either(Gen.anyInt), Gen.anyInt)), - testM("list")(checkAllLaws(Traversable)(GenF.list, Gen.anyInt)), - testM("map")(checkAllLaws(Traversable)(GenFs.map(Gen.anyInt), Gen.anyInt)), - testM("option")(checkAllLaws(Traversable)(GenF.option, Gen.anyInt)), - testM("vector")(checkAllLaws(Traversable)(GenF.vector, Gen.anyInt)) + testM("chunk")(checkAllLaws(Traversable)(GenF.chunk, GenF.option, Gen.anyInt)), + testM("either")(checkAllLaws(Traversable)(GenFs.either(Gen.anyInt), GenF.option, Gen.anyInt)), + testM("list")(checkAllLaws(Traversable)(GenF.list, GenF.option, Gen.anyInt)), + testM("map")(checkAllLaws(Traversable)(GenFs.map(Gen.anyInt), GenF.option, Gen.anyInt)), + testM("option")(checkAllLaws(Traversable)(GenF.option, GenF.option, Gen.anyInt)), + testM("vector")(checkAllLaws(Traversable)(GenF.vector, GenF.option, Gen.anyInt)) ), suite("combinators")( testM("contains") { From dbf6e1b7ac32cad51829dbabcf9f8a9cfaa5944e Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Mon, 30 Nov 2020 21:49:48 +0800 Subject: [PATCH 07/13] make PR run on Dotty - make newtypes for `Nested` functors and functor `Product`s - make instances needed for everything to work - add type ascriptions to make compiler happy in Traversable.scala --- .../src/main/scala/zio/prelude/Derive.scala | 21 ++++++++ .../src/main/scala/zio/prelude/Equal.scala | 13 +++++ .../src/main/scala/zio/prelude/GenFs.scala | 15 +++++- .../main/scala/zio/prelude/Instances.scala | 35 +++++++++++++ .../main/scala/zio/prelude/Traversable.scala | 51 ++++++++++++------- .../scala/zio/prelude/newtypes/package.scala | 26 ++++++++++ 6 files changed, 141 insertions(+), 20 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/Derive.scala b/core/shared/src/main/scala/zio/prelude/Derive.scala index 33292901f..f94effd84 100644 --- a/core/shared/src/main/scala/zio/prelude/Derive.scala +++ b/core/shared/src/main/scala/zio/prelude/Derive.scala @@ -1,5 +1,6 @@ package zio.prelude +import zio.prelude.newtypes.Nested import zio.{ Cause, Chunk, Exit, NonEmptyChunk } import scala.util.Try @@ -31,6 +32,26 @@ 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 cbafa3187..2cb5d9cc5 100644 --- a/core/shared/src/main/scala/zio/prelude/Equal.scala +++ b/core/shared/src/main/scala/zio/prelude/Equal.scala @@ -2,6 +2,7 @@ package zio.prelude import zio.Exit.{ Failure, Success } import zio.prelude.coherent.HashOrd +import zio.prelude.newtypes.{ Nested, Product } import zio.test.TestResult import zio.test.laws.{ Lawful, Laws } import zio.{ Cause, Chunk, Exit, Fiber, NonEmptyChunk, ZTrace } @@ -204,6 +205,18 @@ object Equal extends Lawful[Equal] { def default[A]: Equal[A] = make(_ == _) + /** `Equal` instance for `Id` */ + implicit def IdEqual[A: Equal]: Equal[Id[A]] = (l: Id[A], r: Id[A]) => Id.unwrap[A](l) === Id.unwrap[A](r) + + /** Constructs an `Equal` instance for two nested type constructors. */ + implicit def NestedEqual[F[+_], G[+_], A](implicit eq0: Equal[F[G[A]]]): Equal[Nested[F, G, A]] = + (l: Nested[F, G, A], r: Nested[F, G, A]) => eq0.checkEqual(Nested.unwrap[F[G[A]]](l), Nested.unwrap[F[G[A]]](r)) + + /** Constructs an `Equal` instance for the product of two type constructors. */ + implicit def ProductEqual[F[+_], G[+_], A](implicit eq0: Equal[(F[A], G[A])]): Equal[Product[F, G, A]] = + (l: Product[F, G, A], r: Product[F, G, A]) => + eq0.checkEqual(Product.unwrap[(F[A], G[A])](l), Product.unwrap[(F[A], G[A])](r)) + /** * `Hash` and `Ord` (and thus also `Equal`) instance for `Boolean` values. */ diff --git a/core/shared/src/main/scala/zio/prelude/GenFs.scala b/core/shared/src/main/scala/zio/prelude/GenFs.scala index db0d34677..c474a0790 100644 --- a/core/shared/src/main/scala/zio/prelude/GenFs.scala +++ b/core/shared/src/main/scala/zio/prelude/GenFs.scala @@ -1,6 +1,6 @@ package zio.prelude -import zio.prelude.newtypes.Failure +import zio.prelude.newtypes.{ Failure, Nested } import zio.random.Random import zio.test.Gen.oneOf import zio.test._ @@ -57,6 +57,19 @@ object GenFs { Gen.mapOf(k, v) } + def nested[F[+_], G[+_], RF, RG]( + genF: GenF[RF, F], + genG: GenF[RG, G] + ): GenF[RF with RG, ({ type lambda[+A] = Nested[F, G, A] })#lambda] = + new GenF[RF with RG, ({ type lambda[+A] = Nested[F, G, A] })#lambda] { + override def apply[R1 <: RF with RG, A](gen: Gen[R1, A]): Gen[R1, Nested[F, G, A]] = { + val value: Gen[R1 with RG with RF, newtypes.Nested.newtypeF.Type[F[G[A]]]] = + genF(genG(gen)).map(Nested(_): Nested[F, G, A]) + value + } + + } + /** * A generator of `NonEmptyChunk` values. */ diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index c19ed03a1..164427373 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -3,6 +3,7 @@ package zio.prelude import zio.prelude.AssociativeBoth.OptionIdentityBoth import zio.prelude.Invariant.OptionTraversable import zio.prelude.classic.Applicative +import zio.prelude.newtypes.{ Nested, Product } // not sure about putting this here object Instances { @@ -29,6 +30,22 @@ object Instances { F.map[(G[A], G[B]), G[(A, B)]] { case (ga, gb) => G.both(ga, gb) }(F.both(fa, fb)) } + implicit def nested0[F[+_], G[+_]](implicit + F: Applicative[F], + G: Applicative[G] + ): Applicative[({ type lambda[+A] = Nested[F, G, A] })#lambda] = + new Covariant[({ type lambda[+A] = Nested[F, G, A] })#lambda] + with IdentityBoth[({ type lambda[+A] = Nested[F, G, A] })#lambda] { + private lazy val FG = F.compose(G) + + override def map[A, B](f: A => B): Nested[F, G, A] => Nested[F, G, B] = (fga: Nested[F, G, A]) => + Nested(FG.map(f)(Nested.unwrap[F[G[A]]](fga))) + 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 _)) + } + final def product[F[+_]: Applicative, G[+_]: Applicative]( F: Applicative[F], G: Applicative[G] @@ -45,6 +62,24 @@ object Instances { } implicit val applicativeOption: Applicative[Option] = Applicative[Option](OptionTraversable, OptionIdentityBoth) + + implicit def product0[F[+_], G[+_]](implicit + F: Applicative[F], + G: Applicative[G] + ): Applicative[({ type lambda[+A] = Product[F, G, A] })#lambda] = + new Covariant[({ type lambda[+A] = Product[F, G, A] })#lambda] + with IdentityBoth[({ type lambda[+A] = Product[F, G, A] })#lambda] { + private lazy val FG = Applicative.product(F, G) + + override def map[A, B](f: A => B): Product[F, G, A] => Product[F, G, B] = (fga: Product[F, G, A]) => + Product(FG.map(f)(Product.unwrap(fga))) + + override def any: Product[F, G, Any] = Product((F.any, G.any)) + + override def both[A, B](fga: => Product[F, G, A], fgb: => Product[F, G, B]): Product[F, G, (A, B)] = + Product(FG.both(Product.unwrap(fga), Product.unwrap(fgb))) + } + } trait ApplicativeDeriveEqual[F[+_]] extends Covariant[F] with IdentityBoth[F] with DeriveEqual[F] diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index ffaa16b9d..4d4941fce 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -1,7 +1,7 @@ package zio.prelude import zio.prelude.coherent.DeriveEqualTraversable -import zio.prelude.newtypes.{ And, First, Max, Min, Or, Prod, Sum } +import zio.prelude.newtypes.{ And, First, Max, Min, Nested, Or, Prod, Sum } import zio.test.TestResult import zio.{ Chunk, ChunkBuilder, NonEmptyChunk } @@ -286,13 +286,20 @@ object Traversable new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]( "sequentialFusionLaw" ) { - def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, H[ - +_ - ]: ApplicativeDeriveEqual, A, B, C: Equal](fa: F[A], agb: A => G[B], bhc: B => H[C]): TestResult = { - type GH[+A] = G[H[A]] - implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) - val left: G[H[F[C]]] = fa.foreach(agb).map(_.foreach(bhc)) - val right: G[H[F[C]]] = fa.foreach(a => GH.both(agb(a).map(bhc), GH.any).map(_._1)) + def apply[ + F[+_]: DeriveEqualTraversable, + G[+_]: ApplicativeDeriveEqual, + H[+_]: ApplicativeDeriveEqual, + A, + B, + C: Equal + ](fa: F[A], agb: A => G[B], bhc: B => H[C]): TestResult = { + implicit val GH: Applicative[({ type lambda[+A] = Nested[G, H, A] })#lambda] = Applicative.nested0[G, H] + + val left: Nested[G, H, F[C]] = + Nested(fa.foreach(agb).map(_.foreach(bhc))) + val right: Nested[G, H, F[C]] = + fa.foreach(a => (GH.both(Nested(agb(a).map(bhc)): Nested[G, H, C], GH.any): Nested[G, H, (C, Any)]).map(_._1)) left <-> right } } @@ -311,11 +318,12 @@ object Traversable aga: A => G[A], aha: A => H[A] ): TestResult = { - type GH[+A] = G[H[A]] - implicit val GH: Applicative[GH] = Applicative.compose(Applicative[G], Applicative[H]) + implicit val GH: Applicative[({ type lambda[+A] = Nested[G, H, A] })#lambda] = Applicative.nested0[G, H] - val left: G[H[F[A]]] = fa.map(aga).flip.map(a => a.map(aha).flip) - val right: G[H[F[A]]] = fa.map(a => GH.both(aga(a).map(aha), GH.any).map(_._1)).flip + val left: Nested[G, H, F[A]] = + Nested(fa.map(aga).flip.map(a => a.map(aha).flip)) + val right: Nested[G, H, F[A]] = + fa.map(a => (GH.both(Nested(aga(a).map(aha)), GH.any): Nested[G, H, (A, Any)]).map(_._1)).flip left <-> right } } @@ -325,18 +333,23 @@ object Traversable new ZLawsF.Traversable.ParallelFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]( "parallelFusionLaw" ) { - def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, H[ - +_ - ]: ApplicativeDeriveEqual, A, B: Equal]( + def apply[ + F[+_]: DeriveEqualTraversable, + G[+_]: ApplicativeDeriveEqual, + H[+_]: ApplicativeDeriveEqual, + A, + B: Equal + ]( fa: F[A], agb: A => G[B], ahb: A => H[B] ): TestResult = { - type GH[+A] = (G[A], H[A]) - implicit val GH: Applicative[GH] = Applicative.product(Applicative[G], Applicative[H]) + import newtypes.Product + implicit val GH: Applicative[({ type lambda[+A] = Product[G, H, A] })#lambda] = Applicative.product0[G, H] - val left: (G[F[B]], H[F[B]]) = (fa.foreach(agb), fa.foreach(ahb)) - val right: (G[F[B]], H[F[B]]) = fa.foreach(a => GH.both((agb(a), ahb(a)), GH.any).map(_._1)) + val left: Product[G, H, F[B]] = Product((fa.foreach(agb), fa.foreach(ahb))) + val right: Product[G, H, F[B]] = + fa.foreach(a => (GH.both(Product((agb(a), ahb(a))), GH.any): Product[G, H, (B, Any)]).map(_._1)) left <-> right } } 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 e78ad61ee..8ce50db8a 100644 --- a/core/shared/src/main/scala/zio/prelude/newtypes/package.scala +++ b/core/shared/src/main/scala/zio/prelude/newtypes/package.scala @@ -91,4 +91,30 @@ package object newtypes { object FailureOut extends NewtypeF 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]]] + + /** + * A newtype representing the product of functors. + * + * If F[_] and G[_] are both Covariant, then Product[F, G, *] is also a Covariant. + * + * If F[_] and G[_] are both IdentityBoth, then Product[F, G, *] is also an IdentityBoth. + * + * If F[_] and G[_] are both Traversable, then Product[F, G, *] is also a Traversable. + */ + object Product extends NewtypeF + + type Product[F[+_], G[+_], +A] = Product.Type[(F[A], G[A])] } From 258734a02985ff585f07370bcd80b7ec5ae9de39 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Tue, 1 Dec 2020 01:05:34 +0800 Subject: [PATCH 08/13] change test applicative for Traversable laws from `Option` to `Id` --- core/shared/src/main/scala/zio/prelude/GenFs.scala | 5 +++++ .../src/main/scala/zio/prelude/Instances.scala | 6 +++--- .../test/scala/zio/prelude/NonEmptyListSpec.scala | 2 +- .../scala/zio/prelude/NonEmptyTraversableSpec.scala | 3 +-- .../src/test/scala/zio/prelude/TraversableSpec.scala | 12 ++++++------ 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/GenFs.scala b/core/shared/src/main/scala/zio/prelude/GenFs.scala index c474a0790..175c70dab 100644 --- a/core/shared/src/main/scala/zio/prelude/GenFs.scala +++ b/core/shared/src/main/scala/zio/prelude/GenFs.scala @@ -51,6 +51,11 @@ object GenFs { oneOf(Gen.throwable.map(Future.failed), gen.map(Future.successful)) } + def id: GenF[Random with Sized, Id] = new GenF[Random with Sized, Id] { + def apply[R1 <: Random with Sized, A](gen: Gen[R1, A]): Gen[R1, Id[A]] = + gen.map(Id[A]) + } + def map[R <: Random with Sized, K](k: Gen[R, K]): GenF[R, ({ type lambda[+v] = Map[K, v] })#lambda] = new GenF[R, ({ type lambda[+v] = Map[K, v] })#lambda] { def apply[R1 <: R, V](v: Gen[R1, V]): Gen[R1, Map[K, V]] = diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index 164427373..55d1ecbb6 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -1,7 +1,7 @@ package zio.prelude -import zio.prelude.AssociativeBoth.OptionIdentityBoth -import zio.prelude.Invariant.OptionTraversable +import zio.prelude.AssociativeBoth.IdIdentityBoth +import zio.prelude.Invariant.IdCovariant import zio.prelude.classic.Applicative import zio.prelude.newtypes.{ Nested, Product } @@ -61,7 +61,7 @@ object Instances { (F.both(faga._1, fbgb._1), G.both(faga._2, fbgb._2)) } - implicit val applicativeOption: Applicative[Option] = Applicative[Option](OptionTraversable, OptionIdentityBoth) + implicit val applicativeId: Applicative[Id] = Applicative[Id](IdCovariant, IdIdentityBoth) implicit def product0[F[+_], G[+_]](implicit F: Applicative[F], diff --git a/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala b/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala index 49503634a..4be205ad0 100644 --- a/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala +++ b/core/shared/src/test/scala/zio/prelude/NonEmptyListSpec.scala @@ -52,7 +52,7 @@ object NonEmptyListSpec extends DefaultRunnableSpec { testM("identityBoth")(checkAllLaws(IdentityBoth)(GenFs.nonEmptyList, Gen.anyInt)), testM("identityFlatten")(checkAllLaws(IdentityFlatten)(GenFs.nonEmptyList, Gen.anyInt)), testM("nonEmptyTraversable")( - zio.prelude.laws.checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyList, GenF.option, Gen.anyInt) + zio.prelude.laws.checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyList, GenFs.id, Gen.anyInt) ), testM("ord")(checkAllLaws(Ord)(genNonEmptyList)) ), diff --git a/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala b/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala index fbd0705d0..30cfd3c0d 100644 --- a/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala +++ b/core/shared/src/test/scala/zio/prelude/NonEmptyTraversableSpec.scala @@ -4,7 +4,6 @@ import zio.prelude.Instances.Applicative._ import zio.prelude.laws.checkAllLaws import zio.random.Random import zio.test._ -import zio.test.laws._ object NonEmptyTraversableSpec extends DefaultRunnableSpec { @@ -23,7 +22,7 @@ object NonEmptyTraversableSpec extends DefaultRunnableSpec { def spec: ZSpec[Environment, Failure] = suite("NonEmptyTraversableSpec")( suite("instances")( - testM("nonEmptyChunk")(checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyChunk, GenF.option, Gen.anyInt)) + testM("nonEmptyChunk")(checkAllLaws(NonEmptyTraversable)(GenFs.nonEmptyChunk, GenFs.id, Gen.anyInt)) ), suite("combinators")( testM("max") { diff --git a/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala b/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala index ace1992ff..6e099c7b3 100644 --- a/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala +++ b/core/shared/src/test/scala/zio/prelude/TraversableSpec.scala @@ -36,12 +36,12 @@ object TraversableSpec extends DefaultRunnableSpec { def spec: ZSpec[Environment, Failure] = suite("TraversableSpec")( suite("instances")( - testM("chunk")(checkAllLaws(Traversable)(GenF.chunk, GenF.option, Gen.anyInt)), - testM("either")(checkAllLaws(Traversable)(GenFs.either(Gen.anyInt), GenF.option, Gen.anyInt)), - testM("list")(checkAllLaws(Traversable)(GenF.list, GenF.option, Gen.anyInt)), - testM("map")(checkAllLaws(Traversable)(GenFs.map(Gen.anyInt), GenF.option, Gen.anyInt)), - testM("option")(checkAllLaws(Traversable)(GenF.option, GenF.option, Gen.anyInt)), - testM("vector")(checkAllLaws(Traversable)(GenF.vector, GenF.option, Gen.anyInt)) + testM("chunk")(checkAllLaws(Traversable)(GenF.chunk, GenFs.id, Gen.anyInt)), + testM("either")(checkAllLaws(Traversable)(GenFs.either(Gen.anyInt), GenFs.id, Gen.anyInt)), + testM("list")(checkAllLaws(Traversable)(GenF.list, GenFs.id, Gen.anyInt)), + testM("map")(checkAllLaws(Traversable)(GenFs.map(Gen.anyInt), GenFs.id, Gen.anyInt)), + testM("option")(checkAllLaws(Traversable)(GenF.option, GenFs.id, Gen.anyInt)), + testM("vector")(checkAllLaws(Traversable)(GenF.vector, GenFs.id, Gen.anyInt)) ), suite("combinators")( testM("contains") { From 08cf88dbcf499b92434d364c24cc16ccf567c1a8 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Tue, 1 Dec 2020 18:05:08 +0800 Subject: [PATCH 09/13] cleanup & refactoring --- .../scala/zio/prelude/AssociativeBoth.scala | 35 ++++++- .../main/scala/zio/prelude/Covariant.scala | 11 +++ .../src/main/scala/zio/prelude/Derive.scala | 10 +- .../src/main/scala/zio/prelude/Equal.scala | 12 +-- .../src/main/scala/zio/prelude/GenFs.scala | 12 +-- .../main/scala/zio/prelude/Instances.scala | 96 ++++++++++--------- .../main/scala/zio/prelude/Traversable.scala | 35 +++---- .../scala/zio/prelude/newtypes/package.scala | 28 ++---- 8 files changed, 134 insertions(+), 105 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala index 093f7c060..e0c8cab8f 100644 --- a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala +++ b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala @@ -2,7 +2,7 @@ package zio.prelude import zio._ import zio.prelude.coherent.AssociativeBothDeriveEqualInvariant -import zio.prelude.newtypes.{ AndF, Failure, OrF } +import zio.prelude.newtypes.{ AndF, Failure, NestedF, OrF } import zio.stm.ZSTM import zio.stream.{ ZSink, ZStream } import zio.test.TestResult @@ -17,12 +17,19 @@ import scala.util.{ Success, Try } * and `F[B]` to produce an `F[(A, B)]`. */ @implicitNotFound("No implicit AssociativeBoth defined for ${F}.") -trait AssociativeBoth[F[_]] { +trait AssociativeBoth[F[_]] { self => /** * Combines two values of types `F[A]` and `F[B]` to produce an `F[(A, B)]`. */ def both[A, B](fa: => F[A], fb: => F[B]): F[(A, B)] + + /** Combine with another `AssociativeBoth` to produce an `AssociativeBoth[(F[A], G[A])]`. */ + final def bothF[G[_]](g: AssociativeBoth[G]): AssociativeBoth[({ type lambda[A] = (F[A], G[A]) })#lambda] = + new AssociativeBoth[({ type lambda[A] = (F[A], G[A]) })#lambda] { + def both[A, B](faga: => (F[A], G[A]), fbgb: => (F[B], G[B])): (F[(A, B)], G[(A, B)]) = + (self.both(faga._1, fbgb._1), g.both(faga._2, fbgb._2)) + } } object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvariant, Equal] { @@ -1135,6 +1142,30 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar def both[A, B](fa: => List[A], fb: => List[B]): List[(A, B)] = fa.flatMap(a => fb.map(b => (a, b))) } + final def composeF[F[+_]: IdentityBoth: Covariant, G[+_]](implicit + G: IdentityBoth[G] + ): IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] = + new IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] { + def any: F[G[Any]] = + G.any.succeed[F] + + def both[A, B](fa: => F[G[A]], fb: => F[G[B]]): F[G[(A, B)]] = + fa.zipWith(fb)(_ zip _) + } + + implicit def NestedIdentityBoth[F[+_]: IdentityBoth: Covariant, G[+_]](implicit + G: IdentityBoth[G] + ): IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = + new IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] { + private val FG: IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] = composeF[F, G] + + def any: NestedF[F, G, Any] = + NestedF(FG.any) + + def both[A, B](fa: => NestedF[F, G, A], fb: => NestedF[F, G, B]): NestedF[F, G, (A, B)] = + NestedF(FG.both(NestedF.unwrap[F[G[A]]](fa), NestedF.unwrap[F[G[B]]](fb))) + } + /** * The `AssociativeBoth` instance for `NonEmptyChunk`. */ diff --git a/core/shared/src/main/scala/zio/prelude/Covariant.scala b/core/shared/src/main/scala/zio/prelude/Covariant.scala index 064b6a8e8..c59007be5 100644 --- a/core/shared/src/main/scala/zio/prelude/Covariant.scala +++ b/core/shared/src/main/scala/zio/prelude/Covariant.scala @@ -1,6 +1,7 @@ package zio.prelude import zio.prelude.coherent.CovariantDeriveEqual +import zio.prelude.newtypes.NestedF import zio.test.TestResult import zio.test.laws._ @@ -95,6 +96,16 @@ object Covariant extends LawfulF.Covariant[CovariantDeriveEqual, Equal] { def apply[F[+_]](implicit covariant: Covariant[F]): Covariant[F] = covariant + implicit def NestedCovariant[F[+_], G[+_]](implicit + F: Covariant[F], + G: Covariant[G] + ): Covariant[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = + new Covariant[({ type lambda[+A] = NestedF[F, G, A] })#lambda] { + private lazy val FG = F.compose(G) + + def map[A, B](f: A => B): NestedF[F, G, A] => NestedF[F, G, B] = (fga: NestedF[F, G, A]) => + NestedF(FG.map(f)(NestedF.unwrap[F[G[A]]](fga))) + } } 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 f94effd84..26e04c2f4 100644 --- a/core/shared/src/main/scala/zio/prelude/Derive.scala +++ b/core/shared/src/main/scala/zio/prelude/Derive.scala @@ -1,6 +1,6 @@ package zio.prelude -import zio.prelude.newtypes.Nested +import zio.prelude.newtypes.NestedF import zio.{ Cause, Chunk, Exit, NonEmptyChunk } import scala.util.Try @@ -46,10 +46,10 @@ object Derive { 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])) + ): Derive[({ type lambda[A] = NestedF[F, G, A] })#lambda, Equal] = + new Derive[({ type lambda[A] = NestedF[F, G, A] })#lambda, Equal] { + override def derive[A: Equal]: Equal[NestedF[F, G, A]] = + Equal.NestedFEqual(F.derive(G.derive[A])) } /** diff --git a/core/shared/src/main/scala/zio/prelude/Equal.scala b/core/shared/src/main/scala/zio/prelude/Equal.scala index 2cb5d9cc5..9dcc8af27 100644 --- a/core/shared/src/main/scala/zio/prelude/Equal.scala +++ b/core/shared/src/main/scala/zio/prelude/Equal.scala @@ -2,7 +2,7 @@ package zio.prelude import zio.Exit.{ Failure, Success } import zio.prelude.coherent.HashOrd -import zio.prelude.newtypes.{ Nested, Product } +import zio.prelude.newtypes.{ BothF, NestedF } import zio.test.TestResult import zio.test.laws.{ Lawful, Laws } import zio.{ Cause, Chunk, Exit, Fiber, NonEmptyChunk, ZTrace } @@ -209,13 +209,13 @@ object Equal extends Lawful[Equal] { implicit def IdEqual[A: Equal]: Equal[Id[A]] = (l: Id[A], r: Id[A]) => Id.unwrap[A](l) === Id.unwrap[A](r) /** Constructs an `Equal` instance for two nested type constructors. */ - implicit def NestedEqual[F[+_], G[+_], A](implicit eq0: Equal[F[G[A]]]): Equal[Nested[F, G, A]] = - (l: Nested[F, G, A], r: Nested[F, G, A]) => eq0.checkEqual(Nested.unwrap[F[G[A]]](l), Nested.unwrap[F[G[A]]](r)) + implicit def NestedFEqual[F[+_], G[+_], A](implicit eq0: Equal[F[G[A]]]): Equal[NestedF[F, G, A]] = + (l: NestedF[F, G, A], r: NestedF[F, G, A]) => eq0.checkEqual(NestedF.unwrap[F[G[A]]](l), NestedF.unwrap[F[G[A]]](r)) /** Constructs an `Equal` instance for the product of two type constructors. */ - implicit def ProductEqual[F[+_], G[+_], A](implicit eq0: Equal[(F[A], G[A])]): Equal[Product[F, G, A]] = - (l: Product[F, G, A], r: Product[F, G, A]) => - eq0.checkEqual(Product.unwrap[(F[A], G[A])](l), Product.unwrap[(F[A], G[A])](r)) + implicit def BothEqual[F[+_], G[+_], A](implicit eq0: Equal[(F[A], G[A])]): Equal[BothF[F, G, A]] = + (l: BothF[F, G, A], r: BothF[F, G, A]) => + eq0.checkEqual(BothF.unwrap[(F[A], G[A])](l), BothF.unwrap[(F[A], G[A])](r)) /** * `Hash` and `Ord` (and thus also `Equal`) instance for `Boolean` values. diff --git a/core/shared/src/main/scala/zio/prelude/GenFs.scala b/core/shared/src/main/scala/zio/prelude/GenFs.scala index 175c70dab..26260d5df 100644 --- a/core/shared/src/main/scala/zio/prelude/GenFs.scala +++ b/core/shared/src/main/scala/zio/prelude/GenFs.scala @@ -1,6 +1,6 @@ package zio.prelude -import zio.prelude.newtypes.{ Failure, Nested } +import zio.prelude.newtypes.{ Failure, NestedF } import zio.random.Random import zio.test.Gen.oneOf import zio.test._ @@ -65,11 +65,11 @@ object GenFs { def nested[F[+_], G[+_], RF, RG]( genF: GenF[RF, F], genG: GenF[RG, G] - ): GenF[RF with RG, ({ type lambda[+A] = Nested[F, G, A] })#lambda] = - new GenF[RF with RG, ({ type lambda[+A] = Nested[F, G, A] })#lambda] { - override def apply[R1 <: RF with RG, A](gen: Gen[R1, A]): Gen[R1, Nested[F, G, A]] = { - val value: Gen[R1 with RG with RF, newtypes.Nested.newtypeF.Type[F[G[A]]]] = - genF(genG(gen)).map(Nested(_): Nested[F, G, A]) + ): GenF[RF with RG, ({ type lambda[+A] = NestedF[F, G, A] })#lambda] = + new GenF[RF with RG, ({ type lambda[+A] = NestedF[F, G, A] })#lambda] { + override def apply[R1 <: RF with RG, A](gen: Gen[R1, A]): Gen[R1, NestedF[F, G, A]] = { + val value: Gen[R1 with RG with RF, newtypes.NestedF.newtypeF.Type[F[G[A]]]] = + genF(genG(gen)).map(NestedF(_): NestedF[F, G, A]) value } diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index 55d1ecbb6..f55007fcd 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -1,85 +1,87 @@ package zio.prelude -import zio.prelude.AssociativeBoth.IdIdentityBoth -import zio.prelude.Invariant.IdCovariant import zio.prelude.classic.Applicative -import zio.prelude.newtypes.{ Nested, Product } +import zio.prelude.newtypes.{ BothF, NestedF } // not sure about putting this here object Instances { object Applicative { - def apply[F[+_]: Covariant: IdentityBoth]: Applicative[F] = new Covariant[F] with IdentityBoth[F] { self => - def map[A, B](f: A => B): F[A] => F[B] = Covariant[F].map(f) - - def any: F[Any] = IdentityBoth[F].any - - def both[A, B](fa: => F[A], fb: => F[B]): F[(A, B)] = IdentityBoth[F].both(fa, fb) + def apply[F[+_]](implicit covariant: Covariant[F], identityBoth: IdentityBoth[F]): Applicative[F] = new Covariant[F] + with IdentityBoth[F] { + def map[A, B](f: A => B): F[A] => F[B] = + covariant.map(f) + def any: F[Any] = + identityBoth.any + def both[A, B](fa: => F[A], fb: => F[B]): F[(A, B)] = + identityBoth.both(fa, fb) } - final def compose[F[+_]: Applicative, G[+_]: Applicative]( + implicit def compose[F[+_], G[+_]](implicit F: Applicative[F], G: Applicative[G] ): Applicative[({ type lambda[+A] = F[G[A]] })#lambda] = new Covariant[({ type lambda[+A] = F[G[A]] })#lambda] with IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] { - def map[A, B](f: A => B): F[G[A]] => F[G[B]] = F.map(G.map(f)) - - def any: F[G[Any]] = F.map[Any, G[Any]](_ => G.any)(F.any) + private val C = F.compose(G) + private val IB = AssociativeBoth.composeF(F, F, G) + def map[A, B](f: A => B): F[G[A]] => F[G[B]] = + C.map(f) + def any: F[G[Any]] = + IB.any def both[A, B](fa: => F[G[A]], fb: => F[G[B]]): F[G[(A, B)]] = - F.map[(G[A], G[B]), G[(A, B)]] { case (ga, gb) => G.both(ga, gb) }(F.both(fa, fb)) + IB.both(fa, fb) } - implicit def nested0[F[+_], G[+_]](implicit + implicit def nestedF[F[+_], G[+_]](implicit F: Applicative[F], G: Applicative[G] - ): Applicative[({ type lambda[+A] = Nested[F, G, A] })#lambda] = - new Covariant[({ type lambda[+A] = Nested[F, G, A] })#lambda] - with IdentityBoth[({ type lambda[+A] = Nested[F, G, A] })#lambda] { - private lazy val FG = F.compose(G) - - override def map[A, B](f: A => B): Nested[F, G, A] => Nested[F, G, B] = (fga: Nested[F, G, A]) => - Nested(FG.map(f)(Nested.unwrap[F[G[A]]](fga))) - 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 _)) + ): Applicative[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = + new Covariant[({ type lambda[+A] = NestedF[F, G, A] })#lambda] + with IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] { + private val C = Covariant.NestedCovariant[F, G](F, G) + private val IB = AssociativeBoth.NestedIdentityBoth[F, G](F, F, G) + + def map[A, B](f: A => B): NestedF[F, G, A] => NestedF[F, G, B] = + C.map(f) + def any: NestedF[F, G, Any] = + IB.any + def both[A, B](fa: => NestedF[F, G, A], fb: => NestedF[F, G, B]): NestedF[F, G, (A, B)] = + IB.both(fa, fb) } - final def product[F[+_]: Applicative, G[+_]: Applicative]( + implicit def both[F[+_], G[+_]](implicit F: Applicative[F], G: Applicative[G] ): Applicative[({ type lambda[+A] = (F[A], G[A]) })#lambda] = new Covariant[({ type lambda[+A] = (F[A], G[A]) })#lambda] with IdentityBoth[({ type lambda[+A] = (F[A], G[A]) })#lambda] { - def map[A, B](f: A => B): ((F[A], G[A])) => (F[B], G[B]) = - (faga: (F[A], G[A])) => (faga._1.map(f), faga._2.map(f)) - - def any: (F[Any], G[Any]) = (F.any, G.any) - + def map[A, B](f: A => B): ((F[A], G[A])) => (F[B], G[B]) = + (faga: (F[A], G[A])) => (F.map(f)(faga._1), G.map(f)(faga._2)) + def any: (F[Any], G[Any]) = + (F.any, G.any) def both[A, B](faga: => (F[A], G[A]), fbgb: => (F[B], G[B])): (F[(A, B)], G[(A, B)]) = (F.both(faga._1, fbgb._1), G.both(faga._2, fbgb._2)) } - implicit val applicativeId: Applicative[Id] = Applicative[Id](IdCovariant, IdIdentityBoth) + implicit def applicative[F[+_]](implicit c0: Covariant[F], i0: IdentityBoth[F]): Applicative[F] = + Applicative[F](c0, i0) - implicit def product0[F[+_], G[+_]](implicit + implicit def bothF[F[+_], G[+_]](implicit F: Applicative[F], G: Applicative[G] - ): Applicative[({ type lambda[+A] = Product[F, G, A] })#lambda] = - new Covariant[({ type lambda[+A] = Product[F, G, A] })#lambda] - with IdentityBoth[({ type lambda[+A] = Product[F, G, A] })#lambda] { - private lazy val FG = Applicative.product(F, G) - - override def map[A, B](f: A => B): Product[F, G, A] => Product[F, G, B] = (fga: Product[F, G, A]) => - Product(FG.map(f)(Product.unwrap(fga))) - - override def any: Product[F, G, Any] = Product((F.any, G.any)) - - override def both[A, B](fga: => Product[F, G, A], fgb: => Product[F, G, B]): Product[F, G, (A, B)] = - Product(FG.both(Product.unwrap(fga), Product.unwrap(fgb))) + ): Applicative[({ type lambda[+A] = BothF[F, G, A] })#lambda] = + new Covariant[({ type lambda[+A] = BothF[F, G, A] })#lambda] + with IdentityBoth[({ type lambda[+A] = BothF[F, G, A] })#lambda] { + private lazy val FG = Applicative.both(F, G) + + def map[A, B](f: A => B): BothF[F, G, A] => BothF[F, G, B] = (fga: BothF[F, G, A]) => + BothF(FG.map(f)(BothF.unwrap(fga))) + def any: BothF[F, G, Any] = + BothF(FG.any) + def both[A, B](fga: => BothF[F, G, A], fgb: => BothF[F, G, B]): BothF[F, G, (A, B)] = + BothF(FG.both(BothF.unwrap(fga), BothF.unwrap(fgb))) } - } trait ApplicativeDeriveEqual[F[+_]] extends Covariant[F] with IdentityBoth[F] with DeriveEqual[F] diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index 4d4941fce..cfe734676 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -1,7 +1,7 @@ package zio.prelude import zio.prelude.coherent.DeriveEqualTraversable -import zio.prelude.newtypes.{ And, First, Max, Min, Nested, Or, Prod, Sum } +import zio.prelude.newtypes.{ And, BothF, First, Max, Min, NestedF, Or, Prod, Sum } import zio.test.TestResult import zio.{ Chunk, ChunkBuilder, NonEmptyChunk } @@ -294,12 +294,12 @@ object Traversable B, C: Equal ](fa: F[A], agb: A => G[B], bhc: B => H[C]): TestResult = { - implicit val GH: Applicative[({ type lambda[+A] = Nested[G, H, A] })#lambda] = Applicative.nested0[G, H] + implicit val GH: Applicative[({ type lambda[+A] = NestedF[G, H, A] })#lambda] = Applicative.nestedF - val left: Nested[G, H, F[C]] = - Nested(fa.foreach(agb).map(_.foreach(bhc))) - val right: Nested[G, H, F[C]] = - fa.foreach(a => (GH.both(Nested(agb(a).map(bhc)): Nested[G, H, C], GH.any): Nested[G, H, (C, Any)]).map(_._1)) + val left: NestedF[G, H, F[C]] = + NestedF(fa.foreach(agb).map(_.foreach(bhc))) + val right: NestedF[G, H, F[C]] = + fa.foreach(a => (GH.both(NestedF(agb(a).map(bhc)), GH.any): NestedF[G, H, (C, Any)]).map(_._1)) left <-> right } } @@ -308,9 +308,10 @@ object Traversable val purityLaw: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = new ZLawsF.Traversable.PurityLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]("purityLaw") { def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, A: Equal](fa: F[A]): TestResult = - fa.foreach(Applicative[G].any.as[A](_)) <-> Applicative[G].any.as(fa) + fa.foreach(Applicative[G].any.as[A](_)) <-> fa.succeed[G] } + /** Flip */ val naturalityLaw: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = new ZLawsF.Traversable.NaturalityFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]("naturalityLaw") { def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, H[+_]: ApplicativeDeriveEqual, A: Equal]( @@ -318,12 +319,12 @@ object Traversable aga: A => G[A], aha: A => H[A] ): TestResult = { - implicit val GH: Applicative[({ type lambda[+A] = Nested[G, H, A] })#lambda] = Applicative.nested0[G, H] + implicit val GH: Applicative[({ type lambda[+A] = NestedF[G, H, A] })#lambda] = Applicative.nestedF[G, H] - val left: Nested[G, H, F[A]] = - Nested(fa.map(aga).flip.map(a => a.map(aha).flip)) - val right: Nested[G, H, F[A]] = - fa.map(a => (GH.both(Nested(aga(a).map(aha)), GH.any): Nested[G, H, (A, Any)]).map(_._1)).flip + val left: NestedF[G, H, F[A]] = + NestedF(fa.map(aga).flip.map(a => a.map(aha).flip)) + val right: NestedF[G, H, F[A]] = + fa.map(a => (GH.both(NestedF(aga(a).map(aha)), GH.any): NestedF[G, H, (A, Any)]).map(_._1)).flip left <-> right } } @@ -344,12 +345,12 @@ object Traversable agb: A => G[B], ahb: A => H[B] ): TestResult = { - import newtypes.Product - implicit val GH: Applicative[({ type lambda[+A] = Product[G, H, A] })#lambda] = Applicative.product0[G, H] + implicit val GH: Applicative[({ type lambda[+A] = BothF[G, H, A] })#lambda] = Applicative.bothF - val left: Product[G, H, F[B]] = Product((fa.foreach(agb), fa.foreach(ahb))) - val right: Product[G, H, F[B]] = - fa.foreach(a => (GH.both(Product((agb(a), ahb(a))), GH.any): Product[G, H, (B, Any)]).map(_._1)) + val left: BothF[G, H, F[B]] = + BothF((fa.foreach(agb), fa.foreach(ahb))) + val right: BothF[G, H, F[B]] = + fa.foreach(a => (GH.both(BothF((agb(a), ahb(a))), GH.any): BothF[G, H, (B, Any)]).map(_._1)) left <-> right } } 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 8ce50db8a..eb04eb9e1 100644 --- a/core/shared/src/main/scala/zio/prelude/newtypes/package.scala +++ b/core/shared/src/main/scala/zio/prelude/newtypes/package.scala @@ -92,29 +92,13 @@ 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 + /** A newtype representing right-to-left composition of type constructors. */ + object NestedF extends NewtypeF - type Nested[F[+_], G[+_], +A] = Nested.Type[F[G[A]]] + type NestedF[F[+_], G[+_], +A] = NestedF.Type[F[G[A]]] - /** - * A newtype representing the product of functors. - * - * If F[_] and G[_] are both Covariant, then Product[F, G, *] is also a Covariant. - * - * If F[_] and G[_] are both IdentityBoth, then Product[F, G, *] is also an IdentityBoth. - * - * If F[_] and G[_] are both Traversable, then Product[F, G, *] is also a Traversable. - */ - object Product extends NewtypeF + /** A newtype representing the product of two type constructors. */ + object BothF extends NewtypeF - type Product[F[+_], G[+_], +A] = Product.Type[(F[A], G[A])] + type BothF[F[+_], G[+_], +A] = BothF.Type[(F[A], G[A])] } From 70956b3b5964328acdb3cc9eb78a97d06ef9d470 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Wed, 2 Dec 2020 15:43:53 +0800 Subject: [PATCH 10/13] rewrite Applicative.bothF in terms of BothFCovariant & BothFIdentityBoth --- .../scala/zio/prelude/AssociativeBoth.scala | 36 +++++++++++++++---- .../main/scala/zio/prelude/Covariant.scala | 22 ++++++++++-- .../main/scala/zio/prelude/Instances.scala | 17 ++++----- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala index e0c8cab8f..8df46db9d 100644 --- a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala +++ b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala @@ -2,15 +2,15 @@ package zio.prelude import zio._ import zio.prelude.coherent.AssociativeBothDeriveEqualInvariant -import zio.prelude.newtypes.{ AndF, Failure, NestedF, OrF } +import zio.prelude.newtypes.{AndF, BothF, Failure, NestedF, OrF} import zio.stm.ZSTM -import zio.stream.{ ZSink, ZStream } +import zio.stream.{ZSink, ZStream} import zio.test.TestResult import zio.test.laws._ import scala.annotation.implicitNotFound import scala.concurrent.Future -import scala.util.{ Success, Try } +import scala.util.{Success, Try} /** * An associative binary operator that combines two values of types `F[A]` @@ -1153,11 +1153,24 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar fa.zipWith(fb)(_ zip _) } - implicit def NestedIdentityBoth[F[+_]: IdentityBoth: Covariant, G[+_]](implicit + final def bothF[F[+_], G[+_]](implicit + F: IdentityBoth[F], G: IdentityBoth[G] - ): IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = + ): IdentityBoth[({ type lambda[+A] = (F[A], G[A]) })#lambda] = + new IdentityBoth[({ type lambda[+A] = (F[A], G[A]) })#lambda] { + private val bothFG = F.bothF[G](G) + + def any: (F[Any], G[Any]) = + (F.any, G.any) + + def both[A, B](fa: => (F[A], G[A]), fb: => (F[B], G[B])): (F[(A, B)], G[(A, B)]) = + bothFG.both(fa, fb) + } + + implicit def NestedFIdentityBoth[F[+_]: IdentityBoth: Covariant, G[+_]: IdentityBoth] + : IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = new IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] { - private val FG: IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] = composeF[F, G] + private val FG = composeF[F, G] def any: NestedF[F, G, Any] = NestedF(FG.any) @@ -1166,6 +1179,17 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar NestedF(FG.both(NestedF.unwrap[F[G[A]]](fa), NestedF.unwrap[F[G[B]]](fb))) } + implicit def BothFIdentityBoth[F[+_], G[+_]](implicit F: IdentityBoth[F], G: IdentityBoth[G]): IdentityBoth[({ type lambda[+A] = BothF[F, G, A]})#lambda] = + new IdentityBoth[({ type lambda[+A] = BothF[F, G, A]})#lambda] { + private val FG = AssociativeBoth.bothF[F, G] + + def any: BothF[F, G, Any] = + BothF(FG.any) + + def both[A, B](fga: => BothF[F, G, A], fgb: => BothF[F, G, B]): BothF[F, G, (A, B)] = + BothF(FG.both(BothF.unwrap(fga), BothF.unwrap(fgb))) + } + /** * The `AssociativeBoth` instance for `NonEmptyChunk`. */ diff --git a/core/shared/src/main/scala/zio/prelude/Covariant.scala b/core/shared/src/main/scala/zio/prelude/Covariant.scala index c59007be5..476880c79 100644 --- a/core/shared/src/main/scala/zio/prelude/Covariant.scala +++ b/core/shared/src/main/scala/zio/prelude/Covariant.scala @@ -1,7 +1,7 @@ package zio.prelude import zio.prelude.coherent.CovariantDeriveEqual -import zio.prelude.newtypes.NestedF +import zio.prelude.newtypes.{BothF, NestedF} import zio.test.TestResult import zio.test.laws._ @@ -61,6 +61,13 @@ trait Covariant[F[+_]] extends CovariantSubset[F, AnyType] with Invariant[F] { s new Contravariant[({ type lambda[-A] = F[G[A]] })#lambda] { def contramap[A, B](f: B => A): F[G[A]] => F[G[B]] = self.map(g.contramap(f)) } + + /** Construct the product of two covariant functors. */ + final def both[G[+_]](g: Covariant[G]): Covariant[({ type lambda[+A] = (F[A], G[A]) })#lambda] = + new Covariant[({ type lambda[+A] = (F[A], G[A]) })#lambda] { + def map[A, B](f: A => B): ((F[A], G[A])) => (F[B], G[B]) = (faga: (F[A], G[A])) => + (self.map(f)(faga._1), g.map(f)(faga._2)) + } } object Covariant extends LawfulF.Covariant[CovariantDeriveEqual, Equal] { @@ -96,7 +103,7 @@ object Covariant extends LawfulF.Covariant[CovariantDeriveEqual, Equal] { def apply[F[+_]](implicit covariant: Covariant[F]): Covariant[F] = covariant - implicit def NestedCovariant[F[+_], G[+_]](implicit + implicit def NestedFCovariant[F[+_], G[+_]](implicit F: Covariant[F], G: Covariant[G] ): Covariant[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = @@ -106,6 +113,17 @@ object Covariant extends LawfulF.Covariant[CovariantDeriveEqual, Equal] { def map[A, B](f: A => B): NestedF[F, G, A] => NestedF[F, G, B] = (fga: NestedF[F, G, A]) => NestedF(FG.map(f)(NestedF.unwrap[F[G[A]]](fga))) } + + implicit def BothFCovariant[F[+_], G[+_]](implicit + F: Covariant[F], + G: Covariant[G] + ): Covariant[({ type lambda[+A] = BothF[F, G, A] })#lambda] = + new Covariant[({ type lambda[+A] = BothF[F, G, A] })#lambda] { + private lazy val FG = F.both(G) + + def map[A, B](f: A => B): BothF[F, G, A] => BothF[F, G, B] = (fga: BothF[F, G, A]) => + BothF(FG.map(f)(BothF.unwrap[(F[A], G[A])](fga))) + } } trait CovariantSyntax { diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index f55007fcd..038bd9a66 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -39,8 +39,8 @@ object Instances { ): Applicative[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = new Covariant[({ type lambda[+A] = NestedF[F, G, A] })#lambda] with IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] { - private val C = Covariant.NestedCovariant[F, G](F, G) - private val IB = AssociativeBoth.NestedIdentityBoth[F, G](F, F, G) + private val C = Covariant.NestedFCovariant[F, G](F, G) + private val IB = AssociativeBoth.NestedFIdentityBoth[F, G](F, F, G) def map[A, B](f: A => B): NestedF[F, G, A] => NestedF[F, G, B] = C.map(f) @@ -73,14 +73,15 @@ object Instances { ): Applicative[({ type lambda[+A] = BothF[F, G, A] })#lambda] = new Covariant[({ type lambda[+A] = BothF[F, G, A] })#lambda] with IdentityBoth[({ type lambda[+A] = BothF[F, G, A] })#lambda] { - private lazy val FG = Applicative.both(F, G) + private val C = Covariant.BothFCovariant[F, G](F, G) + private val IB = AssociativeBoth.BothFIdentityBoth[F, G](F, G) - def map[A, B](f: A => B): BothF[F, G, A] => BothF[F, G, B] = (fga: BothF[F, G, A]) => - BothF(FG.map(f)(BothF.unwrap(fga))) - def any: BothF[F, G, Any] = - BothF(FG.any) + def map[A, B](f: A => B): BothF[F, G, A] => BothF[F, G, B] = + C.map(f) + def any: BothF[F, G, Any] = + IB.any def both[A, B](fga: => BothF[F, G, A], fgb: => BothF[F, G, B]): BothF[F, G, (A, B)] = - BothF(FG.both(BothF.unwrap(fga), BothF.unwrap(fgb))) + IB.both(fga, fgb) } } From f0e724f23e86ac55f59edf4334aeaa7bbb026436 Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Wed, 2 Dec 2020 16:00:04 +0800 Subject: [PATCH 11/13] fix --- .../main/scala/zio/prelude/AssociativeBoth.scala | 13 ++++++++----- .../src/main/scala/zio/prelude/Covariant.scala | 2 +- .../src/main/scala/zio/prelude/Instances.scala | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala index 8df46db9d..ad6d92844 100644 --- a/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala +++ b/core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala @@ -2,15 +2,15 @@ package zio.prelude import zio._ import zio.prelude.coherent.AssociativeBothDeriveEqualInvariant -import zio.prelude.newtypes.{AndF, BothF, Failure, NestedF, OrF} +import zio.prelude.newtypes.{ AndF, BothF, Failure, NestedF, OrF } import zio.stm.ZSTM -import zio.stream.{ZSink, ZStream} +import zio.stream.{ ZSink, ZStream } import zio.test.TestResult import zio.test.laws._ import scala.annotation.implicitNotFound import scala.concurrent.Future -import scala.util.{Success, Try} +import scala.util.{ Success, Try } /** * An associative binary operator that combines two values of types `F[A]` @@ -1179,8 +1179,11 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar NestedF(FG.both(NestedF.unwrap[F[G[A]]](fa), NestedF.unwrap[F[G[B]]](fb))) } - implicit def BothFIdentityBoth[F[+_], G[+_]](implicit F: IdentityBoth[F], G: IdentityBoth[G]): IdentityBoth[({ type lambda[+A] = BothF[F, G, A]})#lambda] = - new IdentityBoth[({ type lambda[+A] = BothF[F, G, A]})#lambda] { + implicit def BothFIdentityBoth[F[+_], G[+_]](implicit + F: IdentityBoth[F], + G: IdentityBoth[G] + ): IdentityBoth[({ type lambda[+A] = BothF[F, G, A] })#lambda] = + new IdentityBoth[({ type lambda[+A] = BothF[F, G, A] })#lambda] { private val FG = AssociativeBoth.bothF[F, G] def any: BothF[F, G, Any] = diff --git a/core/shared/src/main/scala/zio/prelude/Covariant.scala b/core/shared/src/main/scala/zio/prelude/Covariant.scala index 476880c79..18cc5ce77 100644 --- a/core/shared/src/main/scala/zio/prelude/Covariant.scala +++ b/core/shared/src/main/scala/zio/prelude/Covariant.scala @@ -1,7 +1,7 @@ package zio.prelude import zio.prelude.coherent.CovariantDeriveEqual -import zio.prelude.newtypes.{BothF, NestedF} +import zio.prelude.newtypes.{ BothF, NestedF } import zio.test.TestResult import zio.test.laws._ diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index 038bd9a66..e9fec2024 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -76,9 +76,9 @@ object Instances { private val C = Covariant.BothFCovariant[F, G](F, G) private val IB = AssociativeBoth.BothFIdentityBoth[F, G](F, G) - def map[A, B](f: A => B): BothF[F, G, A] => BothF[F, G, B] = + def map[A, B](f: A => B): BothF[F, G, A] => BothF[F, G, B] = C.map(f) - def any: BothF[F, G, Any] = + def any: BothF[F, G, Any] = IB.any def both[A, B](fga: => BothF[F, G, A], fgb: => BothF[F, G, B]): BothF[F, G, (A, B)] = IB.both(fga, fgb) From 9150898a3643a0266c4ff0b6c5c7572306ba1a0e Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Wed, 2 Dec 2020 17:33:48 +0800 Subject: [PATCH 12/13] clean up traversable, looking to remove most implementation in Instances.scala --- .../main/scala/zio/prelude/Traversable.scala | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/Traversable.scala b/core/shared/src/main/scala/zio/prelude/Traversable.scala index cfe734676..c474bf01d 100644 --- a/core/shared/src/main/scala/zio/prelude/Traversable.scala +++ b/core/shared/src/main/scala/zio/prelude/Traversable.scala @@ -269,9 +269,9 @@ trait Traversable[F[+_]] extends Covariant[F] { object Traversable extends zio.prelude.laws.LawfulF.Traversable[DeriveEqualTraversable, Instances.ApplicativeDeriveEqual, Equal] { - import zio.prelude.Instances.ApplicativeDeriveEqual - import zio.prelude.classic.Applicative import zio.prelude.Instances.Applicative + import zio.prelude.Instances.Applicative.nestedF + import zio.prelude.Instances.ApplicativeDeriveEqual import zio.prelude.laws.ZLawsF /** Traversing by `Id` is equivalent to mapping. */ @@ -286,20 +286,15 @@ object Traversable new ZLawsF.Traversable.SequentialFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]( "sequentialFusionLaw" ) { - def apply[ - F[+_]: DeriveEqualTraversable, - G[+_]: ApplicativeDeriveEqual, - H[+_]: ApplicativeDeriveEqual, - A, - B, - C: Equal - ](fa: F[A], agb: A => G[B], bhc: B => H[C]): TestResult = { - implicit val GH: Applicative[({ type lambda[+A] = NestedF[G, H, A] })#lambda] = Applicative.nestedF - - val left: NestedF[G, H, F[C]] = - NestedF(fa.foreach(agb).map(_.foreach(bhc))) - val right: NestedF[G, H, F[C]] = - fa.foreach(a => (GH.both(NestedF(agb(a).map(bhc)), GH.any): NestedF[G, H, (C, Any)]).map(_._1)) + def apply[F[+_], G[+_], H[+_], A, B, C](fa: F[A], agb: A => G[B], bhc: B => H[C])(implicit + F: DeriveEqualTraversable[F], + G: ApplicativeDeriveEqual[G], + H: ApplicativeDeriveEqual[H], + C: Equal[C] + ): TestResult = { + val GH = Applicative.nestedF[G, H] + val left = NestedF(fa.foreach(agb).map(_.foreach(bhc))) + val right = fa.foreach(a => (GH.both(NestedF(agb(a).map(bhc)), GH.any): NestedF[G, H, (C, Any)]).map(_._1)) left <-> right } } @@ -311,7 +306,7 @@ object Traversable fa.foreach(Applicative[G].any.as[A](_)) <-> fa.succeed[G] } - /** Flip */ + /** Flipping each of two effects sequentially is the same as flipping once on a composed effect. */ val naturalityLaw: zio.prelude.laws.LawsF.Traversable[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal] = new ZLawsF.Traversable.NaturalityFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]("naturalityLaw") { def apply[F[+_]: DeriveEqualTraversable, G[+_]: ApplicativeDeriveEqual, H[+_]: ApplicativeDeriveEqual, A: Equal]( @@ -319,12 +314,9 @@ object Traversable aga: A => G[A], aha: A => H[A] ): TestResult = { - implicit val GH: Applicative[({ type lambda[+A] = NestedF[G, H, A] })#lambda] = Applicative.nestedF[G, H] - - val left: NestedF[G, H, F[A]] = - NestedF(fa.map(aga).flip.map(a => a.map(aha).flip)) - val right: NestedF[G, H, F[A]] = - fa.map(a => (GH.both(NestedF(aga(a).map(aha)), GH.any): NestedF[G, H, (A, Any)]).map(_._1)).flip + val GH = Applicative.nestedF[G, H] + val left = NestedF(fa.map(aga).flip.map(a => a.map(aha).flip)) + val right = fa.map(a => (GH.both(NestedF(aga(a).map(aha)), GH.any): NestedF[G, H, (A, Any)]).map(_._1)).flip left <-> right } } @@ -334,23 +326,19 @@ object Traversable new ZLawsF.Traversable.ParallelFusionLaw[DeriveEqualTraversable, ApplicativeDeriveEqual, Equal]( "parallelFusionLaw" ) { - def apply[ - F[+_]: DeriveEqualTraversable, - G[+_]: ApplicativeDeriveEqual, - H[+_]: ApplicativeDeriveEqual, - A, - B: Equal - ]( + def apply[F[+_], G[+_], H[+_], A, B]( fa: F[A], agb: A => G[B], ahb: A => H[B] + )(implicit + F: DeriveEqualTraversable[F], + G: ApplicativeDeriveEqual[G], + H: ApplicativeDeriveEqual[H], + B: Equal[B] ): TestResult = { - implicit val GH: Applicative[({ type lambda[+A] = BothF[G, H, A] })#lambda] = Applicative.bothF - - val left: BothF[G, H, F[B]] = - BothF((fa.foreach(agb), fa.foreach(ahb))) - val right: BothF[G, H, F[B]] = - fa.foreach(a => (GH.both(BothF((agb(a), ahb(a))), GH.any): BothF[G, H, (B, Any)]).map(_._1)) + val GH = Applicative.bothF[G, H] + val left = BothF((fa.foreach(agb), fa.foreach(ahb))) + val right = fa.foreach(a => (GH.both(BothF((agb(a), ahb(a))), GH.any): BothF[G, H, (B, Any)]).map(_._1)) left <-> right } } From c6600705fddc1aca236606d4f4ee48c1b882de6a Mon Sep 17 00:00:00 2001 From: "zach.albia" Date: Thu, 3 Dec 2020 13:17:10 +0800 Subject: [PATCH 13/13] remove unneeded code --- .../main/scala/zio/prelude/Instances.scala | 42 ++----------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/core/shared/src/main/scala/zio/prelude/Instances.scala b/core/shared/src/main/scala/zio/prelude/Instances.scala index e9fec2024..b82f3405c 100644 --- a/core/shared/src/main/scala/zio/prelude/Instances.scala +++ b/core/shared/src/main/scala/zio/prelude/Instances.scala @@ -17,30 +17,12 @@ object Instances { identityBoth.both(fa, fb) } - implicit def compose[F[+_], G[+_]](implicit - F: Applicative[F], - G: Applicative[G] - ): Applicative[({ type lambda[+A] = F[G[A]] })#lambda] = - new Covariant[({ type lambda[+A] = F[G[A]] })#lambda] with IdentityBoth[({ type lambda[+A] = F[G[A]] })#lambda] { - private val C = F.compose(G) - private val IB = AssociativeBoth.composeF(F, F, G) - - def map[A, B](f: A => B): F[G[A]] => F[G[B]] = - C.map(f) - def any: F[G[Any]] = - IB.any - def both[A, B](fa: => F[G[A]], fb: => F[G[B]]): F[G[(A, B)]] = - IB.both(fa, fb) - } - implicit def nestedF[F[+_], G[+_]](implicit - F: Applicative[F], - G: Applicative[G] + C: Covariant[({ type lambda[+A] = NestedF[F, G, A] })#lambda], + IB: IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] ): Applicative[({ type lambda[+A] = NestedF[F, G, A] })#lambda] = new Covariant[({ type lambda[+A] = NestedF[F, G, A] })#lambda] with IdentityBoth[({ type lambda[+A] = NestedF[F, G, A] })#lambda] { - private val C = Covariant.NestedFCovariant[F, G](F, G) - private val IB = AssociativeBoth.NestedFIdentityBoth[F, G](F, F, G) def map[A, B](f: A => B): NestedF[F, G, A] => NestedF[F, G, B] = C.map(f) @@ -50,31 +32,15 @@ object Instances { IB.both(fa, fb) } - implicit def both[F[+_], G[+_]](implicit - F: Applicative[F], - G: Applicative[G] - ): Applicative[({ type lambda[+A] = (F[A], G[A]) })#lambda] = - new Covariant[({ type lambda[+A] = (F[A], G[A]) })#lambda] - with IdentityBoth[({ type lambda[+A] = (F[A], G[A]) })#lambda] { - def map[A, B](f: A => B): ((F[A], G[A])) => (F[B], G[B]) = - (faga: (F[A], G[A])) => (F.map(f)(faga._1), G.map(f)(faga._2)) - def any: (F[Any], G[Any]) = - (F.any, G.any) - def both[A, B](faga: => (F[A], G[A]), fbgb: => (F[B], G[B])): (F[(A, B)], G[(A, B)]) = - (F.both(faga._1, fbgb._1), G.both(faga._2, fbgb._2)) - } - implicit def applicative[F[+_]](implicit c0: Covariant[F], i0: IdentityBoth[F]): Applicative[F] = Applicative[F](c0, i0) implicit def bothF[F[+_], G[+_]](implicit - F: Applicative[F], - G: Applicative[G] + C: Covariant[({ type lambda[+A] = BothF[F, G, A] })#lambda], + IB: IdentityBoth[({ type lambda[+A] = BothF[F, G, A] })#lambda] ): Applicative[({ type lambda[+A] = BothF[F, G, A] })#lambda] = new Covariant[({ type lambda[+A] = BothF[F, G, A] })#lambda] with IdentityBoth[({ type lambda[+A] = BothF[F, G, A] })#lambda] { - private val C = Covariant.BothFCovariant[F, G](F, G) - private val IB = AssociativeBoth.BothFIdentityBoth[F, G](F, G) def map[A, B](f: A => B): BothF[F, G, A] => BothF[F, G, B] = C.map(f)