Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer dependent parameter in NonEmpty/ParallelTests/Laws #3046

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions laws/src/main/scala/cats/laws/NonEmptyParallelLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ package laws
/**
* Laws that must be obeyed by any `cats.NonEmptyParallel`.
*/
trait NonEmptyParallelLaws[M[_], F[_]] {
def P: NonEmptyParallel.Aux[M, F]
trait NonEmptyParallelLaws[M[_]] {
val P: NonEmptyParallel[M]
type F[A] = P.F[A]

def parallelRoundTrip[A](ma: M[A]): IsEq[M[A]] =
P.sequential(P.parallel(ma)) <-> ma
Expand All @@ -18,6 +19,11 @@ trait NonEmptyParallelLaws[M[_], F[_]] {
}

object NonEmptyParallelLaws {
def apply[M[_], F[_]](implicit ev: NonEmptyParallel.Aux[M, F]): NonEmptyParallelLaws[M, F] =
new NonEmptyParallelLaws[M, F] { def P: NonEmptyParallel.Aux[M, F] = ev }
type Aux[M[_], F0[_]] = NonEmptyParallelLaws[M] { type F[A] = F0[A]; val P: NonEmptyParallel.Aux[M, F0] }

def apply[M[_]](implicit ev: NonEmptyParallel[M]): NonEmptyParallelLaws.Aux[M, ev.F] =
apply[M, ev.F](ev, implicitly)

def apply[M[_], F[_]](implicit ev: NonEmptyParallel.Aux[M, F], D: DummyImplicit): NonEmptyParallelLaws.Aux[M, F] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't really matter but I did this in the other direction in #3031, with the DummyImplicit on the new one.

new NonEmptyParallelLaws[M] { val P: ev.type = ev }
}
13 changes: 9 additions & 4 deletions laws/src/main/scala/cats/laws/ParallelLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ package laws
/**
* Laws that must be obeyed by any `cats.Parallel`.
*/
trait ParallelLaws[M[_], F[_]] extends NonEmptyParallelLaws[M, F] {
def P: Parallel.Aux[M, F]
trait ParallelLaws[M[_]] extends NonEmptyParallelLaws[M] {
val P: Parallel[M]

def isomorphicPure[A](a: A): IsEq[F[A]] =
P.applicative.pure(a) <-> P.parallel(P.monad.pure(a))
}

object ParallelLaws {
def apply[M[_], F[_]](implicit ev: Parallel.Aux[M, F]): ParallelLaws[M, F] =
new ParallelLaws[M, F] { def P: Parallel.Aux[M, F] = ev }
type Aux[M[_], F0[_]] = ParallelLaws[M] { type F[A] = F0[A]; val P: Parallel.Aux[M, F0] }

def apply[M[_]](implicit ev: Parallel[M]): ParallelLaws.Aux[M, ev.F] =
apply[M, ev.F](ev, implicitly)

def apply[M[_], F[_]](implicit ev: Parallel.Aux[M, F], D: DummyImplicit): ParallelLaws.Aux[M, F] =
new ParallelLaws[M] { val P: ev.type = ev }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import org.scalacheck.Arbitrary
import org.scalacheck.Prop.forAll
import org.typelevel.discipline.Laws

trait NonEmptyParallelTests[M[_], F[_]] extends Laws {
def laws: NonEmptyParallelLaws[M, F]
trait NonEmptyParallelTests[M[_]] extends Laws {
val laws: NonEmptyParallelLaws[M]
type F[A] = laws.F[A]

def nonEmptyParallel[A, B](implicit ArbA: Arbitrary[A],
ArbM: Arbitrary[M[A]],
Expand All @@ -27,6 +28,11 @@ trait NonEmptyParallelTests[M[_], F[_]] extends Laws {
}

object NonEmptyParallelTests {
def apply[M[_], F[_]](implicit ev: NonEmptyParallel.Aux[M, F]): NonEmptyParallelTests[M, F] =
new NonEmptyParallelTests[M, F] { val laws: NonEmptyParallelLaws[M, F] = NonEmptyParallelLaws[M, F] }
type Aux[M[_], F0[_]] = NonEmptyParallelTests[M] { type F[A] = F0[A]; val laws: NonEmptyParallelLaws.Aux[M, F0] }

def apply[M[_]](implicit ev: NonEmptyParallel[M]): NonEmptyParallelTests.Aux[M, ev.F] =
apply[M, ev.F](ev, implicitly)

def apply[M[_], F[_]](implicit ev: NonEmptyParallel.Aux[M, F], D: DummyImplicit): NonEmptyParallelTests.Aux[M, F] =
new NonEmptyParallelTests[M] { val laws = NonEmptyParallelLaws[M] }
}
13 changes: 9 additions & 4 deletions laws/src/main/scala/cats/laws/discipline/ParallelTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ package discipline
import org.scalacheck.Arbitrary
import org.scalacheck.Prop.forAll

trait ParallelTests[M[_], F[_]] extends NonEmptyParallelTests[M, F] {
def laws: ParallelLaws[M, F]
trait ParallelTests[M[_]] extends NonEmptyParallelTests[M] {
val laws: ParallelLaws[M]

def parallel[A, B](implicit ArbA: Arbitrary[A],
ArbM: Arbitrary[M[A]],
Expand All @@ -24,6 +24,11 @@ trait ParallelTests[M[_], F[_]] extends NonEmptyParallelTests[M, F] {
}

object ParallelTests {
def apply[M[_], F[_]](implicit ev: Parallel.Aux[M, F]): ParallelTests[M, F] =
new ParallelTests[M, F] { val laws: ParallelLaws[M, F] = ParallelLaws[M, F] }
type Aux[M[_], F0[_]] = ParallelTests[M] { type F[A] = F0[A]; val laws: ParallelLaws.Aux[M, F0] }

def apply[M[_]](implicit ev: Parallel[M]): ParallelTests.Aux[M, ev.F] =
apply[M, ev.F](ev, implicitly)

def apply[M[_], F[_]](implicit ev: Parallel.Aux[M, F], D: DummyImplicit): ParallelTests.Aux[M, F] =
new ParallelTests[M] { val laws = ParallelLaws[M] }
}
10 changes: 5 additions & 5 deletions tests/src/test/scala-2.13+/cats/tests/ScalaVersionSpecific.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package tests

import cats.data.{NonEmptyLazyList, OneAnd, ZipLazyList}
import cats.data.NonEmptyLazyList
import cats.instances.lazyList._
import cats.laws.discipline.{NonEmptyParallelTests, ParallelTests}
import cats.laws.discipline.arbitrary._
Expand Down Expand Up @@ -123,11 +123,11 @@ trait ScalaVersionSpecificParallelSuite { self: ParallelSuite =>
}

// Can't test Parallel here, as Applicative[ZipLazyList].pure doesn't terminate
checkAll("Parallel[LazyList, ZipLazyList]",
NonEmptyParallelTests[LazyList, ZipLazyList].nonEmptyParallel[Int, String])
checkAll("Parallel[LazyList]",
NonEmptyParallelTests[LazyList].nonEmptyParallel[Int, String])

checkAll("Parallel[NonEmptyLazyList, OneAnd[ZipLazyList, *]]",
ParallelTests[NonEmptyLazyList, OneAnd[ZipLazyList, *]].parallel[Int, String])
checkAll("Parallel[NonEmptyLazyList]",
ParallelTests[NonEmptyLazyList].parallel[Int, String])
}

trait ScalaVersionSpecificRegressionSuite { self: RegressionSuite =>
Expand Down
59 changes: 25 additions & 34 deletions tests/src/test/scala/cats/tests/ParallelSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package tests

import cats._
import cats.data.NonEmptyList.ZipNonEmptyList
import cats.data.NonEmptyVector.ZipNonEmptyVector
import cats.data._
import org.scalatest.funsuite.AnyFunSuiteLike
import cats.laws.discipline.{ApplicativeErrorTests, MiniInt, NonEmptyParallelTests, ParallelTests, SerializableTests}
Expand Down Expand Up @@ -416,63 +415,55 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest with Sc
resultWithInstance should ===("parallel".some)
}

checkAll("Parallel[Either[String, *], Validated[String, *]]",
ParallelTests[Either[String, *], Validated[String, *]].parallel[Int, String])
checkAll("Parallel[Ior[String, *], Ior[String, *]]",
ParallelTests[Ior[String, *], Ior[String, *]].parallel[Int, String])
checkAll("Parallel[Either[String, *]", ParallelTests[Either[String, *]].parallel[Int, String])
checkAll("Parallel[Ior[String, *]]", ParallelTests[Ior[String, *]].parallel[Int, String])
checkAll(
"Parallel[IorT[F, String, *], IorT[F, String, *]] with parallel effect",
ParallelTests[IorT[Either[String, *], String, *], IorT[Validated[String, *], String, *]].parallel[Int, String]
"Parallel[IorT[F, String, *]] with parallel effect",
ParallelTests[IorT[Either[String, *], String, *]].parallel[Int, String]
)
checkAll(
"Parallel[IorT[F, String, *], IorT[F, String, *]] with sequential effect",
ParallelTests[IorT[Option, String, *], IorT[Option, String, *]].parallel[Int, String]
"Parallel[IorT[F, String, *]] with sequential effect",
ParallelTests[IorT[Option, String, *]].parallel[Int, String]
)
checkAll("Parallel[OptionT[M, *], Nested[F, Option, *]]",
ParallelTests[OptionT[Either[String, *], *], Nested[Validated[String, *], Option, *]].parallel[Int, String])
checkAll("Parallel[OptionT[M, *]]", ParallelTests[OptionT[Either[String, *], *]].parallel[Int, String])
checkAll(
"Parallel[EitherT[M, String, *], Nested[F, Validated[String, *], *]]",
ParallelTests[EitherT[Either[String, *], String, *], Nested[Validated[String, *], Validated[String, *], *]]
"Parallel[EitherT[M, String, *]]",
ParallelTests[EitherT[Either[String, *], String, *]]
.parallel[Int, String]
)
checkAll(
"Parallel[EitherT[Option, String, *], Nested[Option, Validated[String, *], *]]",
ParallelTests[EitherT[Option, String, *], Nested[Option, Validated[String, *], *]].parallel[Int, String]
"Parallel[EitherT[Option, String, *]]",
ParallelTests[EitherT[Option, String, *]].parallel[Int, String]
)
checkAll(
"Parallel[WriterT[M, Int, *], WriterT[F, Int, *]]",
ParallelTests[WriterT[Either[String, *], Int, *], WriterT[Validated[String, *], Int, *]].parallel[Int, String]
"Parallel[WriterT[M, Int, *]]",
ParallelTests[WriterT[Either[String, *], Int, *]].parallel[Int, String]
)
checkAll("NonEmptyParallel[Vector, ZipVector]",
NonEmptyParallelTests[Vector, ZipVector].nonEmptyParallel[Int, String])
checkAll("NonEmptyParallel[List, ZipList]", NonEmptyParallelTests[List, ZipList].nonEmptyParallel[Int, String])
checkAll("NonEmptyParallel[Vector]", NonEmptyParallelTests[Vector].nonEmptyParallel[Int, String])
checkAll("NonEmptyParallel[List]", NonEmptyParallelTests[List].nonEmptyParallel[Int, String])
// Can't test Parallel here, as Applicative[ZipStream].pure doesn't terminate
checkAll("Parallel[Stream, ZipStream]", NonEmptyParallelTests[Stream, ZipStream].nonEmptyParallel[Int, String])
checkAll("Parallel[Stream]", NonEmptyParallelTests[Stream].nonEmptyParallel[Int, String])

checkAll("NonEmptyParallel[NonEmptyVector, ZipNonEmptyVector]",
NonEmptyParallelTests[NonEmptyVector, ZipNonEmptyVector].nonEmptyParallel[Int, String])
checkAll("NonEmptyParallel[NonEmptyVector]", NonEmptyParallelTests[NonEmptyVector].nonEmptyParallel[Int, String])

checkAll("NonEmptyParallel[NonEmptyList, ZipNonEmptyList]",
NonEmptyParallelTests[NonEmptyList, ZipNonEmptyList].nonEmptyParallel[Int, String])
checkAll("NonEmptyParallel[NonEmptyList]", NonEmptyParallelTests[NonEmptyList].nonEmptyParallel[Int, String])

checkAll("Parallel[NonEmptyStream, OneAnd[ZipStream, *]",
ParallelTests[NonEmptyStream, OneAnd[ZipStream, *]].parallel[Int, String])
// TODO this doesn't infer?
checkAll("Parallel[NonEmptyStream]", ParallelTests[NonEmptyStream, OneAnd[ZipStream, *]].parallel[Int, String])

checkAll("Parallel[Id, Id]", ParallelTests[Id, Id].parallel[Int, String])
checkAll("Parallel[Id]", ParallelTests[Id].parallel[Int, String])

checkAll("NonEmptyParallel[NonEmptyList, ZipNonEmptyList]",
SerializableTests.serializable(NonEmptyParallel[NonEmptyList, ZipNonEmptyList]))
checkAll("NonEmptyParallel[NonEmptyList]", SerializableTests.serializable(NonEmptyParallel[NonEmptyList]))

checkAll("Parallel[Either[String, *], Validated[String, *]]",
SerializableTests.serializable(Parallel[Either[String, *], Validated[String, *]]))
checkAll("Parallel[Either[String, *]]", SerializableTests.serializable(Parallel[Either[String, *]]))

{
implicit def kleisliEq[F[_], A, B](implicit ev: Eq[A => F[B]]): Eq[Kleisli[F, A, B]] =
Eq.by[Kleisli[F, A, B], A => F[B]](_.run)

checkAll(
"Parallel[KleisliT[M, A, *], Kleisli[F, A, *]]",
ParallelTests[Kleisli[Either[String, *], MiniInt, *], Kleisli[Validated[String, *], MiniInt, *]]
"Parallel[KleisliT[M, A, *]]",
ParallelTests[Kleisli[Either[String, *], MiniInt, *]]
.parallel[Int, String]
)
}
Expand Down