diff --git a/cats-effect/src/test/scala-2/effectie/cats/FxSpec.scala b/cats-effect/src/test/scala-2/effectie/cats/FxSpec.scala index c55a57b1..654b190a 100644 --- a/cats-effect/src/test/scala-2/effectie/cats/FxSpec.scala +++ b/cats-effect/src/test/scala-2/effectie/cats/FxSpec.scala @@ -15,17 +15,21 @@ object FxSpec extends Properties { override def tests: List[Test] = List( property("test Fx[IO].effectOf", IoSpec.testEffectOf), property("test Fx[IO].pureOf", IoSpec.testPureOf), - example("test Fx[IO].unitOf", IoSpec.testUnitOf), - property("test Fx[IO] Monad laws", IoSpec.testMonadLaws), - property("test Fx[Future].effectOf", FutureSpec.testEffectOf), - property("test Fx[Future].pureOf", FutureSpec.testPureOf), - example("test Fx[Future].unitOf", FutureSpec.testUnitOf), - property("test Fx[Future] Monad laws", FutureSpec.testMonadLaws), - property("test Fx[Id].effectOf", IdSpec.testEffectOf), - property("test Fx[Id].pureOf", IdSpec.testPureOf), - example("test Fx[Id].unitOf", IdSpec.testUnitOf), - property("test Fx[Id] Monad laws", IdSpec.testMonadLaws), - ) + example("test Fx[IO].unitOf", IoSpec.testUnitOf) + ) ++ + IoSpec.testMonadLaws ++ + List( + property("test Fx[Future].effectOf", FutureSpec.testEffectOf), + property("test Fx[Future].pureOf", FutureSpec.testPureOf), + example("test Fx[Future].unitOf", FutureSpec.testUnitOf), + ) ++ + FutureSpec.testMonadLaws ++ + List( + property("test Fx[Id].effectOf", IdSpec.testEffectOf), + property("test Fx[Id].pureOf", IdSpec.testPureOf), + example("test Fx[Id].unitOf", IdSpec.testUnitOf), + ) ++ + IdSpec.testMonadLaws object IoSpec { @@ -76,7 +80,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = { + def testMonadLaws: List[Test] = { import cats.syntax.eq._ implicit val eqIo: Eq[IO[Int]] = @@ -147,7 +151,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = { + def testMonadLaws: List[Test] = { import cats.syntax.eq._ implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global @@ -203,7 +207,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = MonadSpec.testMonadLaws[Id] + def testMonadLaws: List[Test] = MonadSpec.testMonadLaws[Id] } diff --git a/cats-effect/src/test/scala-3/effectie/cats/FxSpec.scala b/cats-effect/src/test/scala-3/effectie/cats/FxSpec.scala index ec4d95ac..aaead90d 100644 --- a/cats-effect/src/test/scala-3/effectie/cats/FxSpec.scala +++ b/cats-effect/src/test/scala-3/effectie/cats/FxSpec.scala @@ -16,16 +16,20 @@ object FxSpec extends Properties { property("test Fx[IO].effectOf", IoSpec.testEffectOf), property("test Fx[IO].pureOf", IoSpec.testPureOf), example("test Fx[IO].unitOf", IoSpec.testUnitOf), - property("test Fx[IO] Monad laws", IoSpec.testMonadLaws), - property("test Fx[Future].effectOf", FutureSpec.testEffectOf), - property("test Fx[Future].pureOf", FutureSpec.testPureOf), - example("test Fx[Future].unitOf", FutureSpec.testUnitOf), - property("test Fx[Future] Monad laws", FutureSpec.testMonadLaws), - property("test Fx[Id].effectOf", IdSpec.testEffectOf), - property("test Fx[Id].pureOf", IdSpec.testPureOf), - example("test Fx[Id].unitOf", IdSpec.testUnitOf), - property("test Fx[Id] Monad laws", IdSpec.testMonadLaws), - ) + ) ++ + IoSpec.testMonadLaws ++ + List( + property("test Fx[Future].effectOf", FutureSpec.testEffectOf), + property("test Fx[Future].pureOf", FutureSpec.testPureOf), + example("test Fx[Future].unitOf", FutureSpec.testUnitOf), + ) ++ + FutureSpec.testMonadLaws ++ + List( + property("test Fx[Id].effectOf", IdSpec.testEffectOf), + property("test Fx[Id].pureOf", IdSpec.testPureOf), + example("test Fx[Id].unitOf", IdSpec.testUnitOf), + ) ++ + IdSpec.testMonadLaws object IoSpec { @@ -74,7 +78,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = { + def testMonadLaws: List[Test] = { import cats.syntax.eq.* implicit val eqIo: Eq[IO[Int]] = @@ -143,7 +147,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = { + def testMonadLaws: List[Test] = { import cats.syntax.eq.* implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global @@ -197,7 +201,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = MonadSpec.testMonadLaws[Id] + def testMonadLaws: List[Test] = MonadSpec.testMonadLaws[Id] } diff --git a/cats-effect/src/test/scala/effectie/cats/MonadSpec.scala b/cats-effect/src/test/scala/effectie/cats/MonadSpec.scala index 59ae5f4b..a7429f0e 100644 --- a/cats-effect/src/test/scala/effectie/cats/MonadSpec.scala +++ b/cats-effect/src/test/scala/effectie/cats/MonadSpec.scala @@ -1,17 +1,9 @@ package effectie.cats import cats.Eq -import effectie.testing.cats.{Gens, Specs} -import hedgehog.Property +import hedgehog.runner.Test object MonadSpec { - def testMonadLaws[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = - Specs - .monadLaws - .laws[F]( - Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), - Gens.genIntFromMinToMax, - Gens.genIntToInt, - Gens.genAToMonadA(Gens.genIntToInt) - ) + def testMonadLaws[F[_]: Fx](implicit eqF: Eq[F[Int]]): List[Test] = + effectie.testing.cats.MonadSpec.testAllLaws[F] } diff --git a/cats-effect3/src/test/scala/effectie/cats/LawsProperties.scala b/cats-effect3/src/test/scala/effectie/cats/LawsProperties.scala deleted file mode 100644 index 2c39f5fa..00000000 --- a/cats-effect3/src/test/scala/effectie/cats/LawsProperties.scala +++ /dev/null @@ -1,134 +0,0 @@ -package effectie.cats - -import cats.{Applicative, Eq, Functor, Monad} - -/** @author Kevin Lee - * @since 2021-08-04 - */ -object LawsProperties { - trait FunctorLaws { - /* Functors must preserve identity morphisms - * fmap id = id - */ - def identity[F[_], A](fa: F[A])( - implicit F: Functor[F], - FA: Eq[F[A]] - ): Boolean = - FA.eqv( - F.map(fa)(scala.Predef.identity), - fa - ) - - /* Functors preserve composition of morphisms - * fmap (f . g) == fmap f . fmap g - */ - def composition[F[_]: Functor, A, B, C](fa: F[A], f: B => C, g: A => B)( - implicit F: Functor[F], - FC: Eq[F[C]] - ): Boolean = - FC.eqv( - F.map(fa)(f compose g), - F.map(F.map(fa)(g))(f) - ) - } - object FunctorLaws extends FunctorLaws - - trait ApplicativeLaws extends FunctorLaws { - /* Identity - * pure id <*> v = v - */ - def identityAp[F[_]: Applicative, A](fa: => F[A])( - implicit F: Functor[F], - FA: Eq[F[A]] - ): Boolean = - FA.eqv( - Applicative[F].ap[A, A](Applicative[F].pure(scala.Predef.identity))(fa), - fa - ) - - /* Homomorphism - * pure f <*> pure x = pure (f x) - */ - def homomorphism[F[_]: Applicative, A, B](f: A => B, a: => A)( - implicit F: Functor[F], - FB: Eq[F[B]] - ): Boolean = - FB.eqv( - Applicative[F].ap(Applicative[F].pure(f))(Applicative[F].pure(a)), - Applicative[F].pure(f(a)) - ) - - /* Interchange - * u <*> pure y = pure ($ y) <*> u - */ - def interchange[F[_], A, B](a: => A, f: F[A => B])( - implicit F: Applicative[F], - FB: Eq[F[B]] - ): Boolean = - FB.eqv( - F.ap[A, B](f)(F.pure(a)), - F.ap[A => B, B](F.pure(g => g(a)))(f) - ) - - /* Composition - * pure (.) <*> u <*> v <*> w = u <*> (v <*> w) - */ - def compositionAp[F[_], A, B, C](fa: F[A], f: F[B => C], g: F[A => B])( - implicit F: Applicative[F], - FC: Eq[F[C]] - ): Boolean = - FC.eqv( - F.ap[A, C]( - F.ap[A => B, A => C]( - F.ap[B => C, (A => B) => (A => C)]( - F.pure(bc => ab => bc compose ab) - )(f) - )(g) - )(fa), - F.ap[B, C](f)( - F.ap[A, B](g)(fa) - ) - ) - } - object ApplicativeLaws extends ApplicativeLaws - - trait MonadLaws extends ApplicativeLaws { - /* - * return a >>= f === f a - */ - def leftIdentity[F[_], A, B](a: A, f: A => F[B])( - implicit F: Monad[F], - FB: Eq[F[B]] - ): Boolean = - FB.eqv( - F.flatMap(F.pure(a))(f), - f(a) - ) - - /* - * m >>= return === m - */ - def rightIdentity[F[_], A](fa: F[A])( - implicit F: Monad[F], - FA: Eq[F[A]] - ): Boolean = - FA.eqv( - F.flatMap(fa)(F.pure(_: A)), - fa - ) - - /* - * (m >>= f) >>= g === m >>= (\x -> f x >>= g) - */ - def associativity[F[_], A, B, C](fa: F[A], f: A => F[B], g: B => F[C])( - implicit F: Monad[F], - FC: Eq[F[C]] - ): Boolean = - FC.eqv( - F.flatMap(F.flatMap(fa)(f))(g), - F.flatMap(fa)(x => F.flatMap(f(x))(g)) - ) - } - object MonadLaws extends MonadLaws - -} diff --git a/cats-effect3/src/test/scala/effectie/cats/MonadSpec.scala b/cats-effect3/src/test/scala/effectie/cats/MonadSpec.scala index a1639b58..0aadff1a 100644 --- a/cats-effect3/src/test/scala/effectie/cats/MonadSpec.scala +++ b/cats-effect3/src/test/scala/effectie/cats/MonadSpec.scala @@ -1,20 +1,20 @@ package effectie.cats import cats.Eq -import effectie.testing.cats.Gens +import effectie.testing.cats.{Specs, Gens} import hedgehog.Property object MonadSpec { def test1_Identity[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .identity[F]( Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), ) def test2_Composition[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .composition[F]( Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), Gens.genIntToInt, @@ -22,14 +22,14 @@ object MonadSpec { def test3_IdentityAp[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .identityAp[F]( Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), ) def test4_Homomorphism[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .homomorphism[F]( Gens.genIntFromMinToMax, Gens.genIntToInt, @@ -37,7 +37,7 @@ object MonadSpec { def test5_Interchange[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .interchange[F]( Gens.genIntFromMinToMax, Gens.genIntToInt, @@ -45,7 +45,7 @@ object MonadSpec { def test6_CompositionAp[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .compositionAp[F]( Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), Gens.genIntToInt, @@ -53,7 +53,7 @@ object MonadSpec { def test7_LeftIdentity[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .leftIdentity[F]( Gens.genIntFromMinToMax, Gens.genAToMonadA(Gens.genIntToInt) @@ -61,14 +61,14 @@ object MonadSpec { def test8_RightIdentity[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .rightIdentity[F]( Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), ) def test9_Associativity[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws + .MonadLaws .associativity[F]( Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), Gens.genAToMonadA(Gens.genIntToInt) diff --git a/cats-effect3/src/test/scala/effectie/cats/Specs.scala b/cats-effect3/src/test/scala/effectie/cats/Specs.scala deleted file mode 100644 index 97466c64..00000000 --- a/cats-effect3/src/test/scala/effectie/cats/Specs.scala +++ /dev/null @@ -1,192 +0,0 @@ -package effectie.cats - -import cats._ -import effectie.testing.cats.Laws -import hedgehog._ - -object Specs { - - object functorLaws { - def identity[F[_]]( - genM: Gen[F[Int]] - )(implicit functor: Functor[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - } yield { - (Laws.FunctorLaws.identity[F, Int](m) ==== true) - .log("functorLaw.identity") - } - - def composition[F[_]]( - genM: Gen[F[Int]], - genF: Gen[Int => Int] - )(implicit functor: Functor[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - f <- genF.log("f: Int => Int") - f2 <- genF.log("f2: Int => Int") - } yield { - (Laws.FunctorLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) - .log("functorLaw.composition") - } - } - - object applicativeLaws { - def identity[F[_]]( - genM: Gen[F[Int]] - )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - } yield { - (Laws.ApplicativeLaws.identity[F, Int](m) ==== true) - .log("functorLaw.identity") - } - def composition[F[_]]( - genM: Gen[F[Int]], - genF: Gen[Int => Int] - )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - f <- genF.log("f: Int => Int") - f2 <- genF.log("f2: Int => Int") - } yield { - (Laws.ApplicativeLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) - .log("functorLaw.composition") - } - def identityAp[F[_]]( - genM: Gen[F[Int]] - )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - } yield { - (Laws.ApplicativeLaws.identityAp[F, Int](m) ==== true) - .log("applicativeLaw.identityAp") - } - def homomorphism[F[_]]( - genInt: Gen[Int], - genF: Gen[Int => Int] - )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { - x <- genInt.log("x: Int") - f <- genF.log("f: Int => Int") - } yield { - (Laws.ApplicativeLaws.homomorphism[F, Int, Int](f, x) ==== true) - .log("applicativeLaw.homomorphism") - } - def interchange[F[_]]( - genInt: Gen[Int], - genF: Gen[Int => Int] - )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { - x <- genInt.log("x: Int") - f <- genF.log("f: Int => Int") - } yield { - (Laws.ApplicativeLaws.interchange[F, Int, Int](x, applicative.pure(f)) ==== true) - .log("applicativeLaw.interchange") - } - def compositionAp[F[_]]( - genM: Gen[F[Int]], - genF: Gen[Int => Int] - )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - f <- genF.log("f: Int => Int") - f2 <- genF.log("f2: Int => Int") - } yield { - (Laws.ApplicativeLaws.compositionAp[F, Int, Int, Int](m, applicative.pure(f), applicative.pure(f2)) ==== true) - .log("applicativeLaw.compositionAp") - } - } - - object monadLaws { - def identity[F[_]]( - genM: Gen[F[Int]], - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - } yield { - (Laws.MonadLaws.identity[F, Int](m) ==== true) - .log("functorLaw.identity") - } - - def composition[F[_]]( - genM: Gen[F[Int]], - genF: Gen[Int => Int], - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - f <- genF.log("f: Int => Int") - f2 <- genF.log("f2: Int => Int") - } yield { - (Laws.MonadLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) - .log("functorLaw.composition") - } - - def identityAp[F[_]]( - genM: Gen[F[Int]], - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - } yield { - (Laws.MonadLaws.identityAp[F, Int](m) ==== true) - .log("applicativeLaw.identityAp") - } - - def homomorphism[F[_]]( - genInt: Gen[Int], - genF: Gen[Int => Int], - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - x <- genInt.log("x: Int") - f <- genF.log("f: Int => Int") - } yield { - (Laws.MonadLaws.homomorphism[F, Int, Int](f, x) ==== true) - .log("applicativeLaw.homomorphism") - } - - def interchange[F[_]]( - genInt: Gen[Int], - genF: Gen[Int => Int], - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - x <- genInt.log("x: Int") - f <- genF.log("f: Int => Int") - } yield { - (Laws.MonadLaws.interchange[F, Int, Int](x, monad.pure(f)) ==== true) - .log("applicativeLaw.interchange") - } - - def compositionAp[F[_]]( - genM: Gen[F[Int]], - genF: Gen[Int => Int], - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - f <- genF.log("f: Int => Int") - f2 <- genF.log("f2: Int => Int") - } yield { - (Laws.MonadLaws.compositionAp[F, Int, Int, Int](m, monad.pure(f), monad.pure(f2)) ==== true) - .log("applicativeLaw.compositionAp") - } - - def leftIdentity[F[_]]( - genInt: Gen[Int], - genFm: Gen[Int => F[Int]] - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - x <- genInt.log("x: Int") - fm <- genFm.log("fm: Int => F[Int]") - } yield { - (Laws.MonadLaws.leftIdentity[F, Int, Int](x, fm) ==== true) - .log("monadLaw.leftIdentity") - } - - def rightIdentity[F[_]]( - genM: Gen[F[Int]], - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - } yield { - (Laws.MonadLaws.rightIdentity[F, Int](m) ==== true) - .log("monadLaw.rightIdentity") - } - - def associativity[F[_]]( - genM: Gen[F[Int]], - genFm: Gen[Int => F[Int]] - )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { - m <- genM.log("m: F[Int]") - fm <- genFm.log("fm: Int => F[Int]") - fm2 <- genFm.log("fm2: Int => F[Int]") - } yield { - (Laws.MonadLaws.associativity[F, Int, Int, Int](m, fm, fm2) ==== true) - .log("monadLaw.associativity") - } - - } - -} diff --git a/effectie-monix/src/test/scala/effectie/monix/FxSpec.scala b/effectie-monix/src/test/scala/effectie/monix/FxSpec.scala index bb4488b5..66b842fd 100644 --- a/effectie-monix/src/test/scala/effectie/monix/FxSpec.scala +++ b/effectie-monix/src/test/scala/effectie/monix/FxSpec.scala @@ -17,20 +17,26 @@ object FxSpec extends Properties { property("test Fx[Task].effectOf", TaskSpec.testEffectOf), property("test Fx[Task].pureOf", TaskSpec.testPureOf), example("test Fx[Task].unitOf", TaskSpec.testUnitOf), - property("test Fx[Task] Monad laws", TaskSpec.testMonadLaws), - property("test Fx[IO].effectOf", IoSpec.testEffectOf), - property("test Fx[IO].pureOf", IoSpec.testPureOf), - example("test Fx[IO].unitOf", IoSpec.testUnitOf), - property("test Fx[IO] Monad laws", IoSpec.testMonadLaws), - property("test Fx[Future].effectOf", FutureSpec.testEffectOf), - property("test Fx[Future].pureOf", FutureSpec.testPureOf), - example("test Fx[Future].unitOf", FutureSpec.testUnitOf), - property("test Fx[Future] Monad laws", FutureSpec.testMonadLaws), - property("test Fx[Id].effectOf", IdSpec.testEffectOf), - property("test Fx[Id].pureOf", IdSpec.testPureOf), - example("test Fx[Id].unitOf", IdSpec.testUnitOf), - property("test Fx[Id] Monad laws", IdSpec.testMonadLaws), - ) + ) ++ + TaskSpec.testMonadLaws ++ + List( + property("test Fx[IO].effectOf", IoSpec.testEffectOf), + property("test Fx[IO].pureOf", IoSpec.testPureOf), + example("test Fx[IO].unitOf", IoSpec.testUnitOf), + ) ++ + IoSpec.testMonadLaws ++ + List( + property("test Fx[Future].effectOf", FutureSpec.testEffectOf), + property("test Fx[Future].pureOf", FutureSpec.testPureOf), + example("test Fx[Future].unitOf", FutureSpec.testUnitOf), + ) ++ + FutureSpec.testMonadLaws ++ + List( + property("test Fx[Id].effectOf", IdSpec.testEffectOf), + property("test Fx[Id].pureOf", IdSpec.testPureOf), + example("test Fx[Id].unitOf", IdSpec.testUnitOf), + ) ++ + IdSpec.testMonadLaws object TaskSpec { import monix.execution.Scheduler.Implicits.global @@ -82,7 +88,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = { + def testMonadLaws: List[Test] = { import cats.syntax.eq._ implicit val eqIo: Eq[Task[Int]] = @@ -144,7 +150,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = { + def testMonadLaws: List[Test] = { import cats.syntax.eq._ implicit val eqIo: Eq[IO[Int]] = @@ -215,7 +221,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = { + def testMonadLaws: List[Test] = { import cats.syntax.eq._ implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global @@ -271,7 +277,7 @@ object FxSpec extends Properties { actual ==== expected } - def testMonadLaws: Property = MonadSpec.testMonadLaws[Id] + def testMonadLaws: List[Test] = MonadSpec.testMonadLaws[Id] } diff --git a/effectie-monix/src/test/scala/effectie/monix/MonadSpec.scala b/effectie-monix/src/test/scala/effectie/monix/MonadSpec.scala index 7ff860f9..19ffbf6a 100644 --- a/effectie-monix/src/test/scala/effectie/monix/MonadSpec.scala +++ b/effectie-monix/src/test/scala/effectie/monix/MonadSpec.scala @@ -1,17 +1,9 @@ package effectie.monix import cats.Eq -import effectie.testing.cats.{Gens, Specs} -import hedgehog.Property +import hedgehog.runner.Test object MonadSpec { - def testMonadLaws[F[_]: Fx](implicit eqF: Eq[F[Int]]): Property = - Specs - .monadLaws - .laws[F]( - Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), - Gens.genIntFromMinToMax, - Gens.genIntToInt, - Gens.genAToMonadA(Gens.genIntToInt) - ) + def testMonadLaws[F[_]: Fx](implicit eqF: Eq[F[Int]]): List[Test] = + effectie.testing.cats.MonadSpec.testAllLaws[F] } diff --git a/test4cats/src/main/scala/effectie/testing/cats/MonadSpec.scala b/test4cats/src/main/scala/effectie/testing/cats/MonadSpec.scala index 32adbdc6..78285b18 100644 --- a/test4cats/src/main/scala/effectie/testing/cats/MonadSpec.scala +++ b/test4cats/src/main/scala/effectie/testing/cats/MonadSpec.scala @@ -2,18 +2,92 @@ package effectie.testing.cats import cats.{Eq, Monad} import hedgehog._ +import hedgehog.runner._ /** @author Kevin Lee * @since 2021-08-04 */ object MonadSpec { - def testLaws[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + def testAllLaws[F[_]: Monad](implicit eqF: Eq[F[Int]]): List[Test] = + List( + property("test Monad laws - Identity", test1_Identity), + property("test Monad laws - Composition", test2_Composition), + property("test Monad laws - IdentityAp", test3_IdentityAp), + property("test Monad laws - Homomorphism", test4_Homomorphism), + property("test Monad laws - Interchange", test5_Interchange), + property("test Monad laws - CompositionAp", test6_CompositionAp), + property("test Monad laws - LeftIdentity", test7_LeftIdentity), + property("test Monad laws - RightIdentity", test8_RightIdentity), + property("test Monad laws - Associativity", test9_Associativity), + ) + + def test1_Identity[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .identity[F]( + Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), + ) + + def test2_Composition[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .composition[F]( + Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), + Gens.genIntToInt, + ) + + def test3_IdentityAp[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = Specs - .monadLaws - .laws[F]( + .MonadLaws + .identityAp[F]( Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), + ) + + def test4_Homomorphism[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .homomorphism[F]( Gens.genIntFromMinToMax, Gens.genIntToInt, + ) + + def test5_Interchange[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .interchange[F]( + Gens.genIntFromMinToMax, + Gens.genIntToInt, + ) + + def test6_CompositionAp[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .compositionAp[F]( + Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), + Gens.genIntToInt, + ) + + def test7_LeftIdentity[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .leftIdentity[F]( + Gens.genIntFromMinToMax, + Gens.genAToMonadA(Gens.genIntToInt) + ) + + def test8_RightIdentity[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .rightIdentity[F]( + Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), + ) + + def test9_Associativity[F[_]: Monad](implicit eqF: Eq[F[Int]]): Property = + Specs + .MonadLaws + .associativity[F]( + Gens.genFA[F, Int](Gens.genInt(Int.MinValue, Int.MaxValue)), Gens.genAToMonadA(Gens.genIntToInt) ) + } diff --git a/test4cats/src/main/scala/effectie/testing/cats/Specs.scala b/test4cats/src/main/scala/effectie/testing/cats/Specs.scala index fdd73b54..36458eaf 100644 --- a/test4cats/src/main/scala/effectie/testing/cats/Specs.scala +++ b/test4cats/src/main/scala/effectie/testing/cats/Specs.scala @@ -5,8 +5,17 @@ import hedgehog._ object Specs { - object functorLaws { - def laws[F[_]]( + object FunctorLaws { + def identity[F[_]]( + genM: Gen[F[Int]] + )(implicit functor: Functor[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + } yield { + (Laws.FunctorLaws.identity[F, Int](m) ==== true) + .log("functorLaw.identity") + } + + def composition[F[_]]( genM: Gen[F[Int]], genF: Gen[Int => Int] )(implicit functor: Functor[F], eqM: Eq[F[Int]]): Property = for { @@ -14,84 +23,169 @@ object Specs { f <- genF.log("f: Int => Int") f2 <- genF.log("f2: Int => Int") } yield { - Result.all( - List( - (Laws.FunctorLaws.identity[F, Int](m) ==== true) - .log("functorLaw.identity"), - (Laws.FunctorLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) - .log("functorLaw.composition") - ) - ) + (Laws.FunctorLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) + .log("functorLaw.composition") } } - object applicativeLaws { - def laws[F[_]]( + object ApplicativeLaws { + def identity[F[_]]( + genM: Gen[F[Int]] + )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + } yield { + (Laws.ApplicativeLaws.identity[F, Int](m) ==== true) + .log("functorLaw.identity") + } + def composition[F[_]]( genM: Gen[F[Int]], + genF: Gen[Int => Int] + )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + f <- genF.log("f: Int => Int") + f2 <- genF.log("f2: Int => Int") + } yield { + (Laws.ApplicativeLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) + .log("functorLaw.composition") + } + def identityAp[F[_]]( + genM: Gen[F[Int]] + )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + } yield { + (Laws.ApplicativeLaws.identityAp[F, Int](m) ==== true) + .log("applicativeLaw.identityAp") + } + def homomorphism[F[_]]( + genInt: Gen[Int], + genF: Gen[Int => Int] + )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { + x <- genInt.log("x: Int") + f <- genF.log("f: Int => Int") + } yield { + (Laws.ApplicativeLaws.homomorphism[F, Int, Int](f, x) ==== true) + .log("applicativeLaw.homomorphism") + } + def interchange[F[_]]( genInt: Gen[Int], genF: Gen[Int => Int] + )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { + x <- genInt.log("x: Int") + f <- genF.log("f: Int => Int") + } yield { + (Laws.ApplicativeLaws.interchange[F, Int, Int](x, applicative.pure(f)) ==== true) + .log("applicativeLaw.interchange") + } + def compositionAp[F[_]]( + genM: Gen[F[Int]], + genF: Gen[Int => Int] )(implicit applicative: Applicative[F], eqM: Eq[F[Int]]): Property = for { m <- genM.log("m: F[Int]") - x <- genInt.log("x: Int") f <- genF.log("f: Int => Int") f2 <- genF.log("f2: Int => Int") } yield { - Result.all( - List( - (Laws.ApplicativeLaws.identity[F, Int](m) ==== true) - .log("functorLaw.identity"), - (Laws.ApplicativeLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) - .log("functorLaw.composition"), - (Laws.ApplicativeLaws.identityAp[F, Int](m) ==== true) - .log("applicativeLaw.identityAp"), - (Laws.ApplicativeLaws.homomorphism[F, Int, Int](f, x) ==== true) - .log("applicativeLaw.homomorphism"), - (Laws.ApplicativeLaws.interchange[F, Int, Int](x, applicative.pure(f)) ==== true) - .log("applicativeLaw.interchange"), - (Laws.ApplicativeLaws.compositionAp[F, Int, Int, Int](m, applicative.pure(f), applicative.pure(f2)) ==== true) - .log("applicativeLaw.compositionAp") - ) - ) + (Laws.ApplicativeLaws.compositionAp[F, Int, Int, Int](m, applicative.pure(f), applicative.pure(f2)) ==== true) + .log("applicativeLaw.compositionAp") } } - object monadLaws { - def laws[F[_]]( + object MonadLaws { + def identity[F[_]]( + genM: Gen[F[Int]], + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + } yield { + (Laws.MonadLaws.identity[F, Int](m) ==== true) + .log("functorLaw.identity") + } + + def composition[F[_]]( genM: Gen[F[Int]], + genF: Gen[Int => Int], + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + f <- genF.log("f: Int => Int") + f2 <- genF.log("f2: Int => Int") + } yield { + (Laws.MonadLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) + .log("functorLaw.composition") + } + + def identityAp[F[_]]( + genM: Gen[F[Int]], + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + } yield { + (Laws.MonadLaws.identityAp[F, Int](m) ==== true) + .log("applicativeLaw.identityAp") + } + + def homomorphism[F[_]]( + genInt: Gen[Int], + genF: Gen[Int => Int], + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + x <- genInt.log("x: Int") + f <- genF.log("f: Int => Int") + } yield { + (Laws.MonadLaws.homomorphism[F, Int, Int](f, x) ==== true) + .log("applicativeLaw.homomorphism") + } + + def interchange[F[_]]( genInt: Gen[Int], genF: Gen[Int => Int], + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + x <- genInt.log("x: Int") + f <- genF.log("f: Int => Int") + } yield { + (Laws.MonadLaws.interchange[F, Int, Int](x, monad.pure(f)) ==== true) + .log("applicativeLaw.interchange") + } + + def compositionAp[F[_]]( + genM: Gen[F[Int]], + genF: Gen[Int => Int], + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + f <- genF.log("f: Int => Int") + f2 <- genF.log("f2: Int => Int") + } yield { + (Laws.MonadLaws.compositionAp[F, Int, Int, Int](m, monad.pure(f), monad.pure(f2)) ==== true) + .log("applicativeLaw.compositionAp") + } + + def leftIdentity[F[_]]( + genInt: Gen[Int], + genFm: Gen[Int => F[Int]] + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + x <- genInt.log("x: Int") + fm <- genFm.log("fm: Int => F[Int]") + } yield { + (Laws.MonadLaws.leftIdentity[F, Int, Int](x, fm) ==== true) + .log("monadLaw.leftIdentity") + } + + def rightIdentity[F[_]]( + genM: Gen[F[Int]], + )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { + m <- genM.log("m: F[Int]") + } yield { + (Laws.MonadLaws.rightIdentity[F, Int](m) ==== true) + .log("monadLaw.rightIdentity") + } + + def associativity[F[_]]( + genM: Gen[F[Int]], genFm: Gen[Int => F[Int]] )(implicit monad: Monad[F], eqM: Eq[F[Int]]): Property = for { m <- genM.log("m: F[Int]") - x <- genInt.log("x: Int") - f <- genF.log("f: Int => Int") - f2 <- genF.log("f2: Int => Int") fm <- genFm.log("fm: Int => F[Int]") fm2 <- genFm.log("fm2: Int => F[Int]") } yield { - Result.all( - List( - (Laws.MonadLaws.identity[F, Int](m) ==== true) - .log("functorLaw.identity"), - (Laws.MonadLaws.composition[F, Int, Int, Int](m, f, f2) ==== true) - .log("functorLaw.composition"), - (Laws.MonadLaws.identityAp[F, Int](m) ==== true) - .log("applicativeLaw.identityAp"), - (Laws.MonadLaws.homomorphism[F, Int, Int](f, x) ==== true) - .log("applicativeLaw.homomorphism"), - (Laws.MonadLaws.interchange[F, Int, Int](x, monad.pure(f)) ==== true) - .log("applicativeLaw.interchange"), - (Laws.MonadLaws.compositionAp[F, Int, Int, Int](m, monad.pure(f), monad.pure(f2)) ==== true) - .log("applicativeLaw.compositionAp"), - (Laws.MonadLaws.leftIdentity[F, Int, Int](x, fm) ==== true) - .log("monadLaw.leftIdentity"), - (Laws.MonadLaws.rightIdentity[F, Int](m) ==== true) - .log("monadLaw.rightIdentity"), - (Laws.MonadLaws.associativity[F, Int, Int, Int](m, fm, fm2) ==== true) - .log("monadLaw.associativity") - ) - ) + (Laws.MonadLaws.associativity[F, Int, Int, Int](m, fm, fm2) ==== true) + .log("monadLaw.associativity") } + } }