From 6c6103448bb74720a930dd370bace119e3487b10 Mon Sep 17 00:00:00 2001 From: Thanh Le Date: Sun, 9 Jul 2023 16:04:02 +0200 Subject: [PATCH] Make ByColor more powerful --- src/main/scala/ByColor.scala | 23 +++++++++++++++++------ src/test/scala/ByColorLawsTests.scala | 4 ++-- src/test/scala/ByColorTest.scala | 4 ++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/scala/ByColor.scala b/src/main/scala/ByColor.scala index 593aa5f4d..1289b8974 100644 --- a/src/main/scala/ByColor.scala +++ b/src/main/scala/ByColor.scala @@ -1,11 +1,11 @@ package chess -import cats.{ Applicative, Eq, Eval, Functor, Monoid, Traverse } +import cats.{ Applicative, Eq, Eval, FlatMap, Functor, Monoid, Semigroupal, Traverse } import cats.syntax.all.* import scala.annotation.targetName import alleycats.Zero -case class ByColor[A](white: A, black: A): +case class ByColor[A](white: A, black: A) extends ByColorSyntax: inline def apply(inline color: Color) = if color.white then white else black @@ -110,10 +110,9 @@ object ByColor: def combine(x: ByColor[A], y: ByColor[A]) = ByColor(Monoid[A].combine(x.white, y.white), Monoid[A].combine(x.black, y.black)) - given Functor[ByColor] with - def map[A, B](fa: ByColor[A])(f: A => B): ByColor[B] = fa.map(f) + given Functor[ByColor] with Applicative[ByColor] with Traverse[ByColor] with - given Traverse[ByColor] with + override def map[A, B](fa: ByColor[A])(f: A => B): ByColor[B] = fa.map(f) override def foldLeft[A, B](fa: ByColor[A], b: B)(f: (B, A) => B): B = fa.fold(b)(f) @@ -124,4 +123,16 @@ object ByColor: def traverse[G[_]: Applicative, A, B](fa: ByColor[A])(f: A => G[B]): G[ByColor[B]] = fa.traverse(f) - extension [A](p: (A, A)) def asByColor: ByColor[A] = ByColor(p._1, p._2) + def pure[A](a: A): ByColor[A] = ByColor.fill(a) + def ap[A, B](ff: ByColor[A => B])(fa: ByColor[A]): ByColor[B] = + ByColor(ff.white(fa.white), ff.black(fa.black)) + +trait ByColorSyntax: + + extension [F[_], A](bc: ByColor[F[A]]) + + def mapN[Z](f: (A, A) => Z)(using functor: Functor[F], semigroupal: Semigroupal[F]): F[Z] = + Semigroupal.map2(bc.white, bc.black)(f) + + def flatMapN[Z](f: (A, A) => F[Z])(using flatMap: FlatMap[F]): F[Z] = + flatMap.flatMap2(bc.white, bc.black)(f) diff --git a/src/test/scala/ByColorLawsTests.scala b/src/test/scala/ByColorLawsTests.scala index 15382287c..391c3e24e 100644 --- a/src/test/scala/ByColorLawsTests.scala +++ b/src/test/scala/ByColorLawsTests.scala @@ -1,11 +1,11 @@ package chess -import cats.laws.discipline.FunctorTests import munit.DisciplineSuite import org.scalacheck.* import Arbitraries.given -import cats.laws.discipline.TraverseTests +import cats.laws.discipline.{ ApplicativeTests, FunctorTests, TraverseTests } class ByColorLawsTest extends DisciplineSuite: checkAll("ByColor.FunctorLaws", FunctorTests[ByColor].functor[Int, Int, String]) checkAll("ByColor.TraverseLaws", TraverseTests[ByColor].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("ByColor.ApplicativeLaws", ApplicativeTests[ByColor].applicative[Int, Int, String]) diff --git a/src/test/scala/ByColorTest.scala b/src/test/scala/ByColorTest.scala index 288e31200..a997fc523 100644 --- a/src/test/scala/ByColorTest.scala +++ b/src/test/scala/ByColorTest.scala @@ -83,6 +83,10 @@ class ByColorTest extends ScalaCheckSuite: forAll: (bc: ByColor[Int], f: Int => Boolean) => bc.exists(f) == bc.all.exists(f) + test("sequence"): + forAll: (bc: ByColor[Option[Int]]) => + bc.sequence == (bc.white, bc.black).mapN(ByColor(_, _)) + test("findColor && exists"): forAll: (bc: ByColor[Int], f: Int => Boolean) => bc.findColor(f).isDefined == bc.all.exists(f)