diff --git a/build.sbt b/build.sbt index 9c9122c794..c7f9d1fe4b 100644 --- a/build.sbt +++ b/build.sbt @@ -340,6 +340,26 @@ def mimaSettings(moduleName: String) = Seq( exclude[DirectMissingMethodProblem]("cats.data.RWSTAlternative.sequence"), exclude[ReversedMissingMethodProblem]("cats.MonadError.rethrow"), exclude[ReversedMissingMethodProblem]("cats.syntax.MonadErrorSyntax.catsSyntaxMonadErrorRethrow"), + exclude[InheritedNewAbstractMethodProblem]("cats.InvariantSemigroupal.composeApply"), + exclude[DirectMissingMethodProblem]("cats.Semigroupal.catsSemigroupalForMonoid"), + exclude[ReversedMissingMethodProblem]("cats.instances.InvariantMonoidalInstances.catsSemigroupalForMonoid"), + exclude[DirectMissingMethodProblem]("cats.implicits.catsContravariantSemigroupalForPartialOrder"), + exclude[DirectMissingMethodProblem]("cats.instances.PartialOrderingInstances.catsContravariantSemigroupalForPartialOrdering"), + exclude[ReversedMissingMethodProblem]("cats.instances.PartialOrderingInstances.catsContravariantMonoidalForPartialOrdering"), + exclude[DirectMissingMethodProblem]("cats.instances.package#partialOrdering.catsContravariantSemigroupalForPartialOrdering"), + exclude[DirectMissingMethodProblem]("cats.instances.package#all.catsContravariantSemigroupalForPartialOrder"), + exclude[DirectMissingMethodProblem]("cats.instances.package#all.catsContravariantSemigroupalForPartialOrdering"), + exclude[DirectMissingMethodProblem]("cats.data.ConstInstances1.catsConstInvariantMonoidal"), + exclude[ReversedMissingMethodProblem]("cats.instances.PartialOrderInstances.cats$instances$PartialOrderInstances$_setter_$catsContravariantMonoidalForPartialOrder_="), + exclude[ReversedMissingMethodProblem]("cats.instances.PartialOrderingInstances.cats$instances$PartialOrderingInstances$_setter_$catsContravariantMonoidalForPartialOrdering_="), + exclude[DirectMissingMethodProblem]("cats.instances.package#partialOrder.catsContravariantSemigroupalForPartialOrder"), + exclude[DirectMissingMethodProblem]("cats.instances.PartialOrderInstances.catsContravariantSemigroupalForPartialOrder"), + exclude[ReversedMissingMethodProblem]("cats.instances.PartialOrderInstances.catsContravariantMonoidalForPartialOrder"), + exclude[DirectMissingMethodProblem]("cats.implicits.catsContravariantSemigroupalForPartialOrdering"), + exclude[InheritedNewAbstractMethodProblem]("cats.InvariantMonoidal.point"), + exclude[DirectMissingMethodProblem]("cats.InvariantMonoidal.pure"), + exclude[ReversedMissingMethodProblem]("cats.InvariantMonoidal.point"), + exclude[ReversedMissingMethodProblem]("cats.InvariantMonoidal.unit"), exclude[DirectMissingMethodProblem]("cats.data.CokleisliArrow.id"), exclude[IncompatibleResultTypeProblem]("cats.data.CokleisliArrow.id"), exclude[DirectMissingMethodProblem]("cats.Apply#Ops.followedBy"), diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 93d2019ae9..0a6a3fa937 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -14,7 +14,9 @@ import simulacrum.typeclass * * Must obey the laws defined in cats.laws.ApplicativeLaws. */ -@typeclass trait Applicative[F[_]] extends Apply[F] { self => +@typeclass trait Applicative[F[_]] extends Apply[F] with InvariantMonoidal[F] { self => + + /** * `pure` lifts any value into the Applicative Functor. @@ -184,6 +186,7 @@ import simulacrum.typeclass */ def whenA[A](cond: Boolean)(f: => F[A]): F[Unit] = if (cond) void(f) else pure(()) + } object Applicative { diff --git a/core/src/main/scala/cats/Apply.scala b/core/src/main/scala/cats/Apply.scala index f9673c156d..58d21dd553 100644 --- a/core/src/main/scala/cats/Apply.scala +++ b/core/src/main/scala/cats/Apply.scala @@ -9,7 +9,7 @@ import simulacrum.noop * Must obey the laws defined in cats.laws.ApplyLaws. */ @typeclass(excludeParents = List("ApplyArityFunctions")) -trait Apply[F[_]] extends Functor[F] with Semigroupal[F] with ApplyArityFunctions[F] { self => +trait Apply[F[_]] extends Functor[F] with InvariantSemigroupal[F] with ApplyArityFunctions[F] { self => /** * Given a value and a function in the Apply context, applies the @@ -94,6 +94,7 @@ trait Apply[F[_]] extends Functor[F] with Semigroupal[F] with ApplyArityFunction val F = self val G = Apply[G] } + } object Apply { diff --git a/core/src/main/scala/cats/Composed.scala b/core/src/main/scala/cats/Composed.scala index 99b7494547..dca29899bf 100644 --- a/core/src/main/scala/cats/Composed.scala +++ b/core/src/main/scala/cats/Composed.scala @@ -125,7 +125,7 @@ private[cats] trait ComposedApplicativeContravariantMonoidal[F[_], G[_]] extends def F: Applicative[F] def G: ContravariantMonoidal[G] - override def unit[A]: F[G[A]] = F.pure(G.unit) + override def unit: F[G[Unit]] = F.pure(G.unit) override def contramap[A, B](fa: F[G[A]])(f: B => A): F[G[B]] = F.map(fa)(G.contramap(_)(f)) @@ -144,6 +144,18 @@ private[cats] trait ComposedSemigroupal[F[_], G[_]] extends ContravariantSemigro } } +private[cats] trait ComposedInvariantApplySemigroupal[F[_], G[_]] extends InvariantSemigroupal[λ[α => F[G[α]]]] with ComposedInvariantCovariant[F, G] { outer => + def F: InvariantSemigroupal[F] + def G: Apply[G] + + def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] = + F.imap(F.product(fa, fb)) { case (ga, gb) => + G.map2(ga, gb)(_ -> _) + } { g: G[(A, B)] => + (G.map(g)(_._1), G.map(g)(_._2)) + } +} + private[cats] trait ComposedCovariantContravariant[F[_], G[_]] extends Contravariant[λ[α => F[G[α]]]] { outer => def F: Functor[F] def G: Contravariant[G] diff --git a/core/src/main/scala/cats/ContravariantMonoidal.scala b/core/src/main/scala/cats/ContravariantMonoidal.scala index f4f6881436..276a7ce31e 100644 --- a/core/src/main/scala/cats/ContravariantMonoidal.scala +++ b/core/src/main/scala/cats/ContravariantMonoidal.scala @@ -11,12 +11,20 @@ import simulacrum.typeclass * Based on ekmett's contravariant library: * https://hackage.haskell.org/package/contravariant-1.4/docs/Data-Functor-Contravariant-Divisible.html */ -@typeclass trait ContravariantMonoidal[F[_]] extends ContravariantSemigroupal[F] { self => +@typeclass trait ContravariantMonoidal[F[_]] extends ContravariantSemigroupal[F] with InvariantMonoidal[F] { /** - * `unit` produces an instance of `F` for any type `A` + * `trivial` produces an instance of `F` for any type `A` * that is trivial with respect to `contramap2` along * the diagonal */ - def unit[A]: F[A] + def trivial[A]: F[A] = contramap(unit)(_ => ()) + +} +object ContravariantMonoidal extends SemigroupalArityFunctions { + def monoid[F[_], A](implicit f: ContravariantMonoidal[F]): Monoid[F[A]] = + new ContravariantMonoidalMonoid[F, A](f) +} + +private[cats] class ContravariantMonoidalMonoid[F[_], A](f: ContravariantMonoidal[F]) extends ContravariantSemigroupalSemigroup[F, A](f) with Monoid[F[A]] { + def empty: F[A] = f.trivial } -object ContravariantMonoidal extends SemigroupalArityFunctions diff --git a/core/src/main/scala/cats/ContravariantSemigroupal.scala b/core/src/main/scala/cats/ContravariantSemigroupal.scala index d641fe9067..77910a31e0 100644 --- a/core/src/main/scala/cats/ContravariantSemigroupal.scala +++ b/core/src/main/scala/cats/ContravariantSemigroupal.scala @@ -6,11 +6,21 @@ import simulacrum.typeclass * [[ContravariantSemigroupal]] is nothing more than something both contravariant * and Semigroupal. It comes up enough to be useful, and composes well */ -@typeclass trait ContravariantSemigroupal[F[_]] extends Semigroupal[F] with Contravariant[F] { self => +@typeclass trait ContravariantSemigroupal[F[_]] extends InvariantSemigroupal[F] with Contravariant[F] { self => override def composeFunctor[G[_]: Functor]: ContravariantSemigroupal[λ[α => F[G[α]]]] = new ComposedSemigroupal[F, G] { def F = self def G = Functor[G] } + +} + +object ContravariantSemigroupal extends SemigroupalArityFunctions { + def semigroup[F[_], A](implicit f: ContravariantSemigroupal[F]): Semigroup[F[A]] = + new ContravariantSemigroupalSemigroup[F, A](f) +} + +private[cats] class ContravariantSemigroupalSemigroup[F[_], A](f: ContravariantSemigroupal[F]) extends Semigroup[F[A]] { + def combine(a: F[A], b: F[A]): F[A] = + ContravariantSemigroupal.contramap2(a, b)((a: A) => (a, a))(f, f) } -object ContravariantSemigroupal extends SemigroupalArityFunctions diff --git a/core/src/main/scala/cats/InvariantMonoidal.scala b/core/src/main/scala/cats/InvariantMonoidal.scala index 37c3ada5de..d5eeb5498c 100644 --- a/core/src/main/scala/cats/InvariantMonoidal.scala +++ b/core/src/main/scala/cats/InvariantMonoidal.scala @@ -7,6 +7,34 @@ import simulacrum.typeclass * * Must obey the laws defined in cats.laws.InvariantMonoidalLaws. */ -@typeclass trait InvariantMonoidal[F[_]] extends Invariant[F] with Semigroupal[F] { - def pure[A](a: A): F[A] +@typeclass trait InvariantMonoidal[F[_]] extends InvariantSemigroupal[F] { + /** + * `point` lifts any value into a Monoidal Functor. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * + * scala> InvariantMonoidal[Option].point(10) + * res0: Option[Int] = Some(10) + * }}} + */ + def point[A](a: A): F[A] = imap(unit)(_ => a)(_ => ()) + + def unit: F[Unit] + + +} + +object InvariantMonoidal { + /** + * Gives a `Monoid` instance if A itself has a `Monoid` instance. + */ + def monoid[F[_], A](implicit F: InvariantMonoidal[F], A: Monoid[A]): Monoid[F[A]] = + new InvariantMonoidalMonoid[F, A](F, A) +} + + +private[cats] class InvariantMonoidalMonoid[F[_], A](f: InvariantMonoidal[F], monoid: Monoid[A]) extends InvariantSemigroupalSemigroup(f, monoid) with Monoid[F[A]] { + def empty: F[A] = f.point(monoid.empty) } diff --git a/core/src/main/scala/cats/InvariantSemigroupal.scala b/core/src/main/scala/cats/InvariantSemigroupal.scala new file mode 100644 index 0000000000..a323f933a7 --- /dev/null +++ b/core/src/main/scala/cats/InvariantSemigroupal.scala @@ -0,0 +1,30 @@ +package cats + +import simulacrum.typeclass + +/** + * [[InvariantSemigroupal]] is nothing more than something both invariant + * and Semigroupal. It comes up enough to be useful, and composes well + */ +@typeclass trait InvariantSemigroupal[F[_]] extends Semigroupal[F] with Invariant[F] { self => + + def composeApply[G[_]: Apply]: InvariantSemigroupal[λ[α => F[G[α]]]] = + new ComposedInvariantApplySemigroupal[F, G] { + def F = self + def G = Apply[G] + } + +} + +object InvariantSemigroupal extends SemigroupalArityFunctions { + /** + * Gives a `Semigroup` instance if A itself has a `Semigroup` instance. + */ + def semigroup[F[_], A](implicit F: InvariantSemigroupal[F], A: Semigroup[A]): Semigroup[F[A]] = + new InvariantSemigroupalSemigroup[F, A](F, A) +} + +private[cats] class InvariantSemigroupalSemigroup[F[_], A](f: InvariantSemigroupal[F], sg: Semigroup[A]) extends Semigroup[F[A]] { + def combine(a: F[A], b: F[A]): F[A] = + InvariantSemigroupal.imap2(a, b)(sg.combine)(a => (a, a))(f, f) +} diff --git a/core/src/main/scala/cats/Semigroupal.scala b/core/src/main/scala/cats/Semigroupal.scala index 70d10ace51..6fed1ca0e9 100644 --- a/core/src/main/scala/cats/Semigroupal.scala +++ b/core/src/main/scala/cats/Semigroupal.scala @@ -16,11 +16,4 @@ import simulacrum.typeclass def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] } -object Semigroupal extends SemigroupalArityFunctions { - implicit def catsSemigroupalForMonoid: Semigroupal[Monoid] = new Semigroupal[Monoid] { - def product[A, B](fa: Monoid[A], fb: Monoid[B]): Monoid[(A, B)] = new Monoid[(A, B)] { - val empty = fa.empty -> fb.empty - def combine(x: (A, B), y: (A, B)): (A, B) = fa.combine(x._1, y._1) -> fb.combine(x._2, y._2) - } - } -} +object Semigroupal extends SemigroupalArityFunctions diff --git a/core/src/main/scala/cats/data/Const.scala b/core/src/main/scala/cats/data/Const.scala index b076870bdf..631a9327cd 100644 --- a/core/src/main/scala/cats/data/Const.scala +++ b/core/src/main/scala/cats/data/Const.scala @@ -66,7 +66,7 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 { } implicit def catsDataContravariantMonoidalForConst[D: Monoid]: ContravariantMonoidal[Const[D, ?]] = new ContravariantMonoidal[Const[D, ?]] { - override def unit[A] = Const.empty[D, A] + override def unit = Const.empty[D, Unit] override def contramap[A, B](fa: Const[D, A])(f: B => A): Const[D, B] = fa.retag[B] override def product[A, B](fa: Const[D, A], fb: Const[D, B]): Const[D, (A, B)] = @@ -135,16 +135,6 @@ private[data] sealed abstract class ConstInstances0 extends ConstInstances1 { } private[data] sealed abstract class ConstInstances1 { - implicit def catsConstInvariantMonoidal[C: Monoid]: InvariantMonoidal[Const[C, ?]] = new InvariantMonoidal[Const[C, ?]] { - def pure[A](a: A): Const[C, A] = - Const.empty - - def imap[A, B](fa: Const[C, A])(f: A => B)(g: B => A): Const[C, B] = - fa.retag[B] - - def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] = - fa.retag[(A, B)] combine fb.retag[(A, B)] - } implicit def catsDataEqForConst[A: Eq, B]: Eq[Const[A, B]] = new Eq[Const[A, B]] { def eqv(x: Const[A, B], y: Const[A, B]): Boolean = diff --git a/core/src/main/scala/cats/data/IdT.scala b/core/src/main/scala/cats/data/IdT.scala index 9e555b547a..2c8bcfef51 100644 --- a/core/src/main/scala/cats/data/IdT.scala +++ b/core/src/main/scala/cats/data/IdT.scala @@ -76,7 +76,7 @@ private[data] sealed trait IdTApplicative[F[_]] extends Applicative[IdT[F, ?]] w private[data] sealed trait IdTContravariantMonoidal[F[_]] extends ContravariantMonoidal[IdT[F, ?]] { implicit val F0: ContravariantMonoidal[F] - override def unit[A]: IdT[F, A] = IdT(F0.unit[A]) + override def unit: IdT[F, Unit] = IdT(F0.unit) override def contramap[A, B](fa: IdT[F, A])(f: B => A): IdT[F, B] = IdT(F0.contramap(fa.value)(f)) diff --git a/core/src/main/scala/cats/data/IndexedStateT.scala b/core/src/main/scala/cats/data/IndexedStateT.scala index 85fb6b4f32..d6b6c2e4d3 100644 --- a/core/src/main/scala/cats/data/IndexedStateT.scala +++ b/core/src/main/scala/cats/data/IndexedStateT.scala @@ -247,10 +247,6 @@ private[data] sealed abstract class IndexedStateTInstances extends IndexedStateT implicit def catsDataAlternativeForIndexedStateT[F[_], S](implicit FM: Monad[F], FA: Alternative[F]): Alternative[IndexedStateT[F, S, S, ?]] with Monad[IndexedStateT[F, S, S, ?]] = new IndexedStateTAlternative[F, S] { implicit def F = FM; implicit def G = FA } - - implicit def catsDataContravariantMonoidalForIndexedStateT[F[_], S](implicit FD: ContravariantMonoidal[F], - FA: Applicative[F]): ContravariantMonoidal[IndexedStateT[F, S, S, ?]] = - new IndexedStateTContravariantMonoidal[F, S] { implicit def F = FD; implicit def G = FA } } private[data] sealed abstract class IndexedStateTInstances1 extends IndexedStateTInstances2 { @@ -387,11 +383,11 @@ private[data] sealed abstract class IndexedStateTContravariantMonoidal[F[_], S] implicit def F: ContravariantMonoidal[F] implicit def G: Applicative[F] - override def unit[A]: IndexedStateT[F, S, S, A] = - IndexedStateT.applyF(G.pure((s: S) => F.unit[(S, A)])) + override def unit: IndexedStateT[F, S, S, Unit] = + IndexedStateT.applyF(G.pure((s: S) => F.trivial[(S, Unit)])) override def contramap[A, B](fa: IndexedStateT[F, S, S, A])(f: B => A): IndexedStateT[F, S, S, B] = - contramap2(fa, unit)(((a: A) => (a, a)) compose f) + contramap2(fa, trivial)(((a: A) => (a, a)) compose f) override def product[A, B](fa: IndexedStateT[F, S, S, A], fb: IndexedStateT[F, S, S, B]): IndexedStateT[F, S, S, (A, B)] = contramap2(fa, fb)(identity) diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index a62fc0d8a1..05857c1d44 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -338,7 +338,7 @@ private[data] trait KleisliAlternative[F[_], A] extends Alternative[Kleisli[F, A private[data] sealed trait KleisliContravariantMonoidal[F[_], D] extends ContravariantMonoidal[Kleisli[F, D, ?]] { implicit def F: ContravariantMonoidal[F] - override def unit[A]: Kleisli[F, D, A] = Kleisli(Function.const(F.unit[A])) + override def unit: Kleisli[F, D, Unit] = Kleisli(Function.const(F.unit)) override def contramap[A, B](fa: Kleisli[F, D, A])(f: B => A): Kleisli[F, D, B] = Kleisli(d => F.contramap(fa.run(d))(f)) diff --git a/core/src/main/scala/cats/data/Nested.scala b/core/src/main/scala/cats/data/Nested.scala index 7449730517..2dd937e288 100644 --- a/core/src/main/scala/cats/data/Nested.scala +++ b/core/src/main/scala/cats/data/Nested.scala @@ -147,27 +147,34 @@ private[data] sealed abstract class NestedInstances8 extends NestedInstances9 { } private[data] sealed abstract class NestedInstances9 extends NestedInstances10 { + implicit def catsDataInvariantSemigroupalApplyForNested[F[_]: InvariantSemigroupal, G[_]: Apply]: InvariantSemigroupal[Nested[F, G, ?]] = + new NestedInvariantSemigroupalApply[F, G] { + val FG: InvariantSemigroupal[λ[α => F[G[α]]]] = InvariantSemigroupal[F].composeApply[G] + } +} + +private[data] sealed abstract class NestedInstances10 extends NestedInstances11 { implicit def catsDataFunctorForNested[F[_]: Functor, G[_]: Functor]: Functor[Nested[F, G, ?]] = new NestedFunctor[F, G] { val FG: Functor[λ[α => F[G[α]]]] = Functor[F].compose[G] } } -private[data] sealed abstract class NestedInstances10 extends NestedInstances11 { +private[data] sealed abstract class NestedInstances11 extends NestedInstances12 { implicit def catsDataInvariantForNested[F[_]: Invariant, G[_]: Invariant]: Invariant[Nested[F, G, ?]] = new NestedInvariant[F, G] { val FG: Invariant[λ[α => F[G[α]]]] = Invariant[F].compose[G] } } -private[data] sealed abstract class NestedInstances11 extends NestedInstances12 { +private[data] sealed abstract class NestedInstances12 extends NestedInstances13 { implicit def catsDataInvariantForCovariantNested[F[_]: Invariant, G[_]: Functor]: Invariant[Nested[F, G, ?]] = new NestedInvariant[F, G] { val FG: Invariant[λ[α => F[G[α]]]] = Invariant[F].composeFunctor[G] } } -private[data] sealed abstract class NestedInstances12 { +private[data] sealed abstract class NestedInstances13 { implicit def catsDataInvariantForNestedContravariant[F[_]: Invariant, G[_]: Contravariant]: Invariant[Nested[F, G, ?]] = new NestedInvariant[F, G] { val FG: Invariant[λ[α => F[G[α]]]] = Invariant[F].composeContravariant[G] @@ -284,7 +291,7 @@ private[data] trait NestedContravariant[F[_], G[_]] extends Contravariant[Nested private[data] trait NestedContravariantMonoidal[F[_], G[_]] extends ContravariantMonoidal[Nested[F, G, ?]] { def FG: ContravariantMonoidal[λ[α => F[G[α]]]] - def unit[A]: Nested[F, G, A] = Nested(FG.unit) + def unit: Nested[F, G, Unit] = Nested(FG.unit) def contramap[A, B](fa: Nested[F, G, A])(f: B => A): Nested[F, G, B] = Nested(FG.contramap(fa.value)(f)) @@ -292,3 +299,13 @@ private[data] trait NestedContravariantMonoidal[F[_], G[_]] extends Contravarian def product[A, B](fa: Nested[F, G, A], fb: Nested[F, G, B]): Nested[F, G, (A, B)] = Nested(FG.product(fa.value, fb.value)) } + +private[data] trait NestedInvariantSemigroupalApply[F[_], G[_]] extends InvariantSemigroupal[Nested[F, G, ?]] { + def FG: InvariantSemigroupal[λ[α => F[G[α]]]] + + def imap[A, B](fa: Nested[F, G, A])(f: A => B)(g: B => A): Nested[F, G, B] = + Nested(FG.imap(fa.value)(f)(g)) + + def product[A, B](fa: Nested[F, G, A], fb: Nested[F, G, B]): Nested[F, G, (A, B)] = + Nested(FG.product(fa.value, fb.value)) +} diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index df24420c1b..106891bcbe 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -300,7 +300,7 @@ private trait OptionTMonadError[F[_], E] extends MonadError[OptionT[F, ?], E] wi private trait OptionTContravariantMonoidal[F[_]] extends ContravariantMonoidal[OptionT[F, ?]] { def F: ContravariantMonoidal[F] - override def unit[A]: OptionT[F, A] = OptionT (F.unit) + override def unit: OptionT[F, Unit] = OptionT(F.trivial) override def contramap[A, B](fa: OptionT[F, A])(f: B => A): OptionT[F, B] = OptionT(F.contramap(fa.value)(_ map f)) diff --git a/core/src/main/scala/cats/data/Tuple2K.scala b/core/src/main/scala/cats/data/Tuple2K.scala index c35fd7c797..efa0242c7a 100644 --- a/core/src/main/scala/cats/data/Tuple2K.scala +++ b/core/src/main/scala/cats/data/Tuple2K.scala @@ -145,7 +145,7 @@ private[data] sealed trait Tuple2KContravariant[F[_], G[_]] extends Contravarian private[data] sealed trait Tuple2KContravariantMonoidal[F[_], G[_]] extends ContravariantMonoidal[λ[α => Tuple2K[F, G, α]]] { def F: ContravariantMonoidal[F] def G: ContravariantMonoidal[G] - def unit[A]: Tuple2K[F, G, A] = Tuple2K(F.unit, G.unit) + def unit: Tuple2K[F, G, Unit] = Tuple2K(F.unit, G.unit) def product[A, B](fa: Tuple2K[F, G, A], fb: Tuple2K[F, G, B]): Tuple2K[F, G, (A, B)] = Tuple2K(F.product(fa.first, fb.first), G.product(fa.second, fb.second)) def contramap[A, B](fa: Tuple2K[F, G, A])(f: B => A): Tuple2K[F, G, B] = diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 82f5ce71c2..95620f4cac 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -378,7 +378,7 @@ private[data] sealed trait WriterTAlternative[F[_], L] extends Alternative[Write private[data] sealed trait WriterTContravariantMonoidal[F[_], L] extends ContravariantMonoidal[WriterT[F, L, ?]] { implicit def F0: ContravariantMonoidal[F] - override def unit[A]: WriterT[F, L, A] = WriterT(F0.unit[(L, A)]) + override def unit: WriterT[F, L, Unit] = WriterT(F0.trivial[(L, Unit)]) override def contramap[A, B](fa: WriterT[F, L, A])(f: B => A): WriterT[F, L, B] = WriterT(F0.contramap(fa.run)((d: (L, B)) => (d._1, f(d._2)))) diff --git a/core/src/main/scala/cats/instances/eq.scala b/core/src/main/scala/cats/instances/eq.scala index 364c1962d7..310975db7e 100644 --- a/core/src/main/scala/cats/instances/eq.scala +++ b/core/src/main/scala/cats/instances/eq.scala @@ -8,7 +8,7 @@ trait EqInstances extends kernel.instances.EqInstances { * Defaults to the trivial equivalence relation * contracting the type to a point */ - def unit[A]: Eq[A] = Eq.allEqual + def unit: Eq[Unit] = Eq.allEqual /** Derive an `Eq` for `B` given an `Eq[A]` and a function `B => A`. * diff --git a/core/src/main/scala/cats/instances/equiv.scala b/core/src/main/scala/cats/instances/equiv.scala index 012d189ff7..6ae653cf98 100644 --- a/core/src/main/scala/cats/instances/equiv.scala +++ b/core/src/main/scala/cats/instances/equiv.scala @@ -8,8 +8,8 @@ trait EquivInstances { * Defaults to trivially contracting the type * to a point */ - def unit[A]: Equiv[A] = new Equiv[A] { - def equiv(x: A, y: A): Boolean = true + def unit: Equiv[Unit] = new Equiv[Unit] { + def equiv(x: Unit, y: Unit): Boolean = true } /** Derive an `Equiv` for `B` given an `Equiv[A]` and a function `B => A`. diff --git a/core/src/main/scala/cats/instances/function.scala b/core/src/main/scala/cats/instances/function.scala index b711fb37a4..aa3374e8c2 100644 --- a/core/src/main/scala/cats/instances/function.scala +++ b/core/src/main/scala/cats/instances/function.scala @@ -46,7 +46,7 @@ private[instances] sealed trait Function0Instances0 { private[instances] sealed trait Function1Instances extends Function1Instances0 { implicit def catsStdContravariantMonoidalForFunction1[R: Monoid]: ContravariantMonoidal[? => R] = new ContravariantMonoidal[? => R] { - def unit[A]: A => R = Function.const(Monoid[R].empty) + def unit: Unit => R = Function.const(Monoid[R].empty) def contramap[A, B](fa: A => R)(f: B => A): B => R = fa compose f def product[A, B](fa: A => R, fb: B => R): ((A, B)) => R = diff --git a/core/src/main/scala/cats/instances/invariant.scala b/core/src/main/scala/cats/instances/invariant.scala index c2e9a968bb..e683941543 100644 --- a/core/src/main/scala/cats/instances/invariant.scala +++ b/core/src/main/scala/cats/instances/invariant.scala @@ -1,10 +1,24 @@ package cats.instances import cats.kernel._ -import cats.InvariantMonoidal +import cats.kernel.instances.unit._ +import cats.{InvariantMonoidal, Monoid, InvariantSemigroupal} trait InvariantMonoidalInstances { + implicit def catsSemigroupalForMonoid: InvariantSemigroupal[Monoid] = new InvariantSemigroupal[Monoid] { + def product[A, B](fa: Monoid[A], fb: Monoid[B]): Monoid[(A, B)] = new Monoid[(A, B)] { + val empty = fa.empty -> fb.empty + def combine(x: (A, B), y: (A, B)): (A, B) = fa.combine(x._1, y._1) -> fb.combine(x._2, y._2) + } + + def imap[A, B](fa: Monoid[A])(f: A => B)(g: B => A): Monoid[B] = new Monoid[B] { + def empty: B = f(fa.empty) + + def combine(x: B, y: B): B = f(fa.combine(g(x), g(y))) + } + } + implicit val catsInvariantMonoidalSemigroup: InvariantMonoidal[Semigroup] = new InvariantMonoidal[Semigroup] { def product[A, B](fa: Semigroup[A], fb: Semigroup[B]): Semigroup[(A, B)] = new Semigroup[(A, B)] { def combine(x: (A, B), y: (A, B)): (A, B) = fa.combine(x._1, y._1) -> fb.combine(x._2, y._2) @@ -12,17 +26,9 @@ trait InvariantMonoidalInstances { def imap[A, B](fa: Semigroup[A])(f: A => B)(g: B => A): Semigroup[B] = new Semigroup[B] { def combine(x: B, y: B): B = f(fa.combine(g(x), g(y))) - override def combineAllOption(bs: TraversableOnce[B]): Option[B] = - fa.combineAllOption(bs.map(g)).map(f) } - def pure[A](a: A): Semigroup[A] = new Semigroup[A] { - def combine(x: A, y: A): A = a - override def combineAllOption(as: TraversableOnce[A]): Option[A] = - if (as.isEmpty) None - else if (as.size == 1) as.toList.headOption - else Some(a) - } + def unit: Semigroup[Unit] = implicitly } implicit val catsInvariantMonoidalCommutativeSemigroup: InvariantMonoidal[CommutativeSemigroup] = new InvariantMonoidal[CommutativeSemigroup] { @@ -32,17 +38,9 @@ trait InvariantMonoidalInstances { def imap[A, B](fa: CommutativeSemigroup[A])(f: A => B)(g: B => A): CommutativeSemigroup[B] = new CommutativeSemigroup[B] { def combine(x: B, y: B): B = f(fa.combine(g(x), g(y))) - override def combineAllOption(bs: TraversableOnce[B]): Option[B] = - fa.combineAllOption(bs.map(g)).map(f) } - def pure[A](a: A): CommutativeSemigroup[A] = new CommutativeSemigroup[A] { - def combine(x: A, y: A): A = a - override def combineAllOption(as: TraversableOnce[A]): Option[A] = - if (as.isEmpty) None - else if (as.size == 1) as.toList.headOption - else Some(a) - } + def unit: CommutativeSemigroup[Unit] = implicitly } } diff --git a/core/src/main/scala/cats/instances/order.scala b/core/src/main/scala/cats/instances/order.scala index 1e44c3f932..4cc59ac312 100644 --- a/core/src/main/scala/cats/instances/order.scala +++ b/core/src/main/scala/cats/instances/order.scala @@ -1,13 +1,16 @@ package cats package instances +import cats.kernel.instances.unit._ + trait OrderInstances extends kernel.instances.OrderInstances { + implicit val catsContravariantMonoidalForOrder: ContravariantMonoidal[Order] = new ContravariantMonoidal[Order] { /** * Provides trivial order */ - def unit[A]: Order[A] = Order.from[A]((x: A, y: A) => 0) + def unit: Order[Unit] = Order[Unit] /** Derive an `Order` for `B` given an `Order[A]` and a function `B => A`. * * Note: resulting instances are law-abiding only when the functions used are injective (represent a one-to-one mapping) diff --git a/core/src/main/scala/cats/instances/ordering.scala b/core/src/main/scala/cats/instances/ordering.scala index 5afd3b58e7..69ef920384 100644 --- a/core/src/main/scala/cats/instances/ordering.scala +++ b/core/src/main/scala/cats/instances/ordering.scala @@ -1,6 +1,8 @@ package cats package instances +import cats.kernel.instances.unit._ + trait OrderingInstances { implicit val catsContravariantMonoidalForOrdering: ContravariantMonoidal[Ordering] = new ContravariantMonoidal[Ordering] { @@ -8,9 +10,7 @@ trait OrderingInstances { * Note: resulting instances are law-abiding only when the functions used are injective (represent a one-to-one mapping) */ - def unit[A]: Ordering[A] = new Ordering[A] { - def compare(l: A, r: A): Int = 0 - } + def unit: Ordering[Unit] = Order[Unit].toOrdering def contramap[A, B](fa: Ordering[A])(f: B => A): Ordering[B] = fa.on(f) diff --git a/core/src/main/scala/cats/instances/partialOrder.scala b/core/src/main/scala/cats/instances/partialOrder.scala index cfdab8db32..5f9949eb55 100644 --- a/core/src/main/scala/cats/instances/partialOrder.scala +++ b/core/src/main/scala/cats/instances/partialOrder.scala @@ -1,9 +1,11 @@ package cats package instances +import cats.kernel.instances.unit._ + trait PartialOrderInstances extends kernel.instances.PartialOrderInstances { - implicit val catsContravariantSemigroupalForPartialOrder: ContravariantSemigroupal[PartialOrder] = - new ContravariantSemigroupal[PartialOrder] { + implicit val catsContravariantMonoidalForPartialOrder: ContravariantMonoidal[PartialOrder] = + new ContravariantMonoidal[PartialOrder] { /** Derive a `PartialOrder` for `B` given a `PartialOrder[A]` and a function `B => A`. * * Note: resulting instances are law-abiding only when the functions used are injective (represent a one-to-one mapping) @@ -17,5 +19,7 @@ trait PartialOrderInstances extends kernel.instances.PartialOrderInstances { if (z == 0.0) fb.partialCompare(x._2, y._2) else z } } + + def unit: PartialOrder[Unit] = Order[Unit] } } diff --git a/core/src/main/scala/cats/instances/partialOrdering.scala b/core/src/main/scala/cats/instances/partialOrdering.scala index d44d33e185..3560286635 100644 --- a/core/src/main/scala/cats/instances/partialOrdering.scala +++ b/core/src/main/scala/cats/instances/partialOrdering.scala @@ -2,8 +2,8 @@ package cats package instances trait PartialOrderingInstances { - implicit val catsContravariantSemigroupalForPartialOrdering: ContravariantSemigroupal[PartialOrdering] = - new ContravariantSemigroupal[PartialOrdering] { + implicit val catsContravariantMonoidalForPartialOrdering: ContravariantMonoidal[PartialOrdering] = + new ContravariantMonoidal[PartialOrdering] { /** Derive a `PartialOrdering` for `B` given a `PartialOrdering[A]` and a function `B => A`. * * Note: resulting instances are law-abiding only when the functions used are injective (represent a one-to-one mapping) @@ -24,5 +24,7 @@ trait PartialOrderingInstances { case option => option } } + + def unit: PartialOrdering[Unit] = cats.instances.unit.catsKernelStdOrderForUnit.toOrdering } } diff --git a/docs/src/main/tut/typeclasses/contravariantmonoidal.md b/docs/src/main/tut/typeclasses/contravariantmonoidal.md index ddcd56cfa1..de5d3f5011 100644 --- a/docs/src/main/tut/typeclasses/contravariantmonoidal.md +++ b/docs/src/main/tut/typeclasses/contravariantmonoidal.md @@ -14,7 +14,7 @@ The `ContravariantMonoidal` type class is for [`Contravariant`](contravariant.ht import cats.Contravariant trait ContravariantMonoidal[F[_]] extends Contravariant[F] { - def unit[A]: F[A] + def unit: F[Unit] def product[A, B](fa: F[A], fc: F[B]): F[(A, B)] @@ -52,7 +52,7 @@ Then, we can exhibit a `ContravariantMonoidal` for `Predicate` by basing it on t ```tut:book:silent implicit val contravariantMonoidalPredicate: ContravariantMonoidal[Predicate] = new ContravariantMonoidal [Predicate] { - def unit[A]: Predicate[A] = Predicate[A](Function.const(true)) + def unit: Predicate[Unit] = Predicate[Unit](Function.const(true)) def product[A, B](fa: Predicate[A], fb: Predicate[B]): Predicate[(A, B)] = Predicate(x => fa.run(x._1) && fb.run(x._2)) diff --git a/docs/src/main/tut/typeclasses/invariantmonoidal.md b/docs/src/main/tut/typeclasses/invariantmonoidal.md index a8fdb49c4e..181f336dcc 100644 --- a/docs/src/main/tut/typeclasses/invariantmonoidal.md +++ b/docs/src/main/tut/typeclasses/invariantmonoidal.md @@ -7,11 +7,11 @@ scaladoc: "#cats.InvariantMonoidal" --- # Invariant Monoidal -`InvariantMonoidal` combines [`Invariant`](invariant.html) and [`Monoidal`](monoidal.html) with the addition of a `pure` methods, defined in isolation the `InvariantMonoidal` type class could be defined as follows: +`InvariantMonoidal` combines [`Invariant`](invariant.html) and `Semigroupal` with the addition of a `unit` methods, defined in isolation the `InvariantMonoidal` type class could be defined as follows: ```tut:silent trait InvariantMonoidal[F[_]] { - def pure[A](x: A): F[A] + def unit: F[Unit] def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] } @@ -23,16 +23,16 @@ This tutorial first shows how `Semigroup` is `InvariantMonoidal`, and how this c # `Semigroup` is `InvariantMonoidal` -As explained in the [`Invariant` tutorial](invariant.html), `Semigroup` forms an invariant functor. Indeed, given a `Semigroup[A]` and two functions `A => B` and `B => A`, one can construct a `Semigroup[B]` by transforming two values from type `B` to type `A`, combining these using the `Semigroup[A]`, and transforming the result back to type `B`. Thus to define an `InvariantMonoidal[Semigroup]` we need implementations for `pure` and `product`. +As explained in the [`Invariant` tutorial](invariant.html), `Semigroup` forms an invariant functor. Indeed, given a `Semigroup[A]` and two functions `A => B` and `B => A`, one can construct a `Semigroup[B]` by transforming two values from type `B` to type `A`, combining these using the `Semigroup[A]`, and transforming the result back to type `B`. Thus to define an `InvariantMonoidal[Semigroup]` we need implementations for `unit` and `product`. To construct a `Semigroup` from a single value, we can define a trivial `Semigroup` with a combine that always outputs the given value. A `Semigroup[(A, B)]` can be obtained from two `Semigroup`s for type `A` and `B` by deconstructing two pairs into elements of type `A` and `B`, combining these element using their respective `Semigroup`s, and reconstructing a pair from the results: ```tut:silent import cats.Semigroup -def pure[A](a: A): Semigroup[A] = - new Semigroup[A] { - def combine(x: A, y: A): A = a +def unit: Semigroup[Unit] = + new Semigroup[Unit] { + def combine(x: Unit, y: Unit): Unit = () } def product[A, B](fa: Semigroup[A], fb: Semigroup[B]): Semigroup[(A, B)] = @@ -88,10 +88,10 @@ forAll { (c: CsvCodec[A], a: A) => c.read(c.write(a)) == ((Some(a), List())) Let's now see how we could define an `InvariantMonoidal` instance for `CsvCodec`. Lifting a single value into a `CsvCodec` can be done "the trivial way" by consuming nothing from CSV and producing that value, and writing this value as the empty CSV: ```tut:silent -trait CCPure { - def pure[A](a: A): CsvCodec[A] = new CsvCodec[A] { - def read(s: CSV): (Option[A], CSV) = (Some(a), s) - def write(a: A): CSV = List.empty +trait CCUnit { + def unit: CsvCodec[Unit] = new CsvCodec[Unit] { + def read(s: CSV): (Option[Unit], CSV) = (Some(()), s) + def write(u: Unit): CSV = List.empty } } ``` @@ -137,7 +137,7 @@ Putting it all together: import cats.InvariantMonoidal implicit val csvCodecIsInvariantMonoidal: InvariantMonoidal[CsvCodec] = - new InvariantMonoidal[CsvCodec] with CCPure with CCProduct with CCImap + new InvariantMonoidal[CsvCodec] with CCUnit with CCProduct with CCImap ``` We can now define a few `CsvCodec` instances and use the methods provided by `InvariantMonoidal` to define `CsvCodec` from existing `CsvCodec`s: @@ -224,7 +224,7 @@ trait MultiInvariantImap3[F[_]] extends MultiInvariant[F] { } ``` -We can observe that `MultiInvariant` is none other than an alternative formulation for `InvariantMonoidal`. Indeed, `imap0` and `pure` have exactly the same signature, `imap1` and `imap` only differ by the order of their argument, and `imap2` can easily be defined in terms of `imap` and `product`: +We can observe that `MultiInvariant` is none other than an alternative formulation for `InvariantMonoidal`. Indeed, `imap1` and `imap` only differ by the order of their argument, and `imap2` can easily be defined in terms of `imap` and `product`: ```tut:silent trait Imap2FromImapProduct[F[_]] extends cats.InvariantMonoidal[F] { diff --git a/free/src/main/scala/cats/free/FreeInvariantMonoidal.scala b/free/src/main/scala/cats/free/FreeInvariantMonoidal.scala index d96383ee33..7407d7f750 100644 --- a/free/src/main/scala/cats/free/FreeInvariantMonoidal.scala +++ b/free/src/main/scala/cats/free/FreeInvariantMonoidal.scala @@ -43,7 +43,7 @@ object FreeInvariantMonoidal { private final case class Pure[F[_], A](a: A) extends FA[F, A] { def foldMap[G[_]](nt: FunctionK[F, G])(implicit im: InvariantMonoidal[G]): G[A] = - im.pure(a) + im.point(a) } private final case class Suspend[F[_], A](fa: F[A]) extends FA[F, A] { @@ -70,7 +70,7 @@ object FreeInvariantMonoidal { /** `FreeInvariantMonoidal[S, ?]` has a FreeInvariantMonoidal for any type constructor `S[_]`. */ implicit def catsFreeInvariantMonoidal[S[_]]: InvariantMonoidal[FA[S, ?]] = new InvariantMonoidal[FA[S, ?]] { - def pure[A](a: A): FA[S, A] = FreeInvariantMonoidal.pure(a) + def unit: FA[S, Unit] = FreeInvariantMonoidal.pure(()) def imap[A, B](fa: FA[S, A])(f: A => B)(g: B => A): FA[S, B] = fa.imap(f)(g) def product[A, B](fa: FA[S, A], fb: FA[S, B]): FA[S, (A, B)] = fa.product(fb) } diff --git a/free/src/test/scala/cats/free/FreeInvariantMonoidalSuite.scala b/free/src/test/scala/cats/free/FreeInvariantMonoidalSuite.scala index 86894e8d30..23e9dfed9b 100644 --- a/free/src/test/scala/cats/free/FreeInvariantMonoidalSuite.scala +++ b/free/src/test/scala/cats/free/FreeInvariantMonoidalSuite.scala @@ -31,7 +31,7 @@ class FreeInvariantMonoidalSuite extends CatsSuite { test("FreeInvariantMonoidal#fold") { val n = 2 val i1 = numericSystemCodec(8) - val i2 = InvariantMonoidal[CsvCodec].pure(n) + val i2 = InvariantMonoidal[CsvCodec].point(n) val iExpr = i1.product(i2.imap(_ * 2)(_ / 2)) val f1 = FreeInvariantMonoidal.lift[CsvCodec, Int](i1) @@ -44,7 +44,7 @@ class FreeInvariantMonoidalSuite extends CatsSuite { implicit val idIsInvariantMonoidal: InvariantMonoidal[Id] = new InvariantMonoidal[Id] { def product[A, B](fa: Id[A], fb: Id[B]): Id[(A, B)] = fa -> fb def imap[A, B](fa: Id[A])(f: A => B)(g: B => A): Id[B] = f(fa) - def pure[A](a: A): Id[A] = a + def unit: Id[Unit] = () } test("FreeInvariantMonoidal#compile") { diff --git a/laws/src/main/scala/cats/laws/ContravariantMonoidalLaws.scala b/laws/src/main/scala/cats/laws/ContravariantMonoidalLaws.scala index 07a1cb24c6..ef99653482 100644 --- a/laws/src/main/scala/cats/laws/ContravariantMonoidalLaws.scala +++ b/laws/src/main/scala/cats/laws/ContravariantMonoidalLaws.scala @@ -12,16 +12,16 @@ trait ContravariantMonoidalLaws[F[_]] extends ContravariantSemigroupalLaws[F] { implicit override def F: ContravariantMonoidal[F] def contravariantMonoidalUnitRight[A](fa: F[A]): IsEq[F[A]] = - (fa, F.unit[A]).contramapN(delta[A]) <-> fa + (fa, F.trivial[A]).contramapN(delta[A]) <-> fa def contravariantMonoidalUnitLeft[A](fa: F[A]): IsEq[F[A]] = - (F.unit[A], fa).contramapN(delta[A]) <-> fa + (F.trivial[A], fa).contramapN(delta[A]) <-> fa def contravariantMonoidalContramap2CompatibleContramapLeft[A, B, C](fa: F[A], f: B => (A, C)): IsEq[F[B]] = - (fa, F.unit[C]).contramapN(f) <-> fa.contramap(f andThen (_._1)) + (fa, F.trivial[C]).contramapN(f) <-> fa.contramap(f andThen (_._1)) def contravariantMonoidalContramap2CompatibleContramapRight[A, B, C](fa: F[A], f: C => (B, A)): IsEq[F[C]] = - (F.unit[B], fa).contramapN(f) <-> fa.contramap(f andThen (_._2)) + (F.trivial[B], fa).contramapN(f) <-> fa.contramap(f andThen (_._2)) } object ContravariantMonoidalLaws { diff --git a/laws/src/main/scala/cats/laws/InvariantMonoidalLaws.scala b/laws/src/main/scala/cats/laws/InvariantMonoidalLaws.scala index 99ed3761ce..528bd7ae72 100644 --- a/laws/src/main/scala/cats/laws/InvariantMonoidalLaws.scala +++ b/laws/src/main/scala/cats/laws/InvariantMonoidalLaws.scala @@ -4,16 +4,16 @@ package laws /** * Laws that must be obeyed by any `cats.InvariantMonoidal`. */ -trait InvariantMonoidalLaws[F[_]] extends InvariantLaws[F] with SemigroupalLaws[F] { +trait InvariantMonoidalLaws[F[_]] extends InvariantSemigroupalLaws[F] { override implicit def F: InvariantMonoidal[F] import cats.syntax.semigroupal._ import cats.syntax.invariant._ - def invariantMonoidalLeftIdentity[A, B](fa: F[A], b: B): IsEq[F[A]] = - F.pure(b).product(fa).imap(_._2)(a => (b, a)) <-> fa + def invariantMonoidalLeftIdentity[A, B](fa: F[A]): IsEq[F[A]] = + F.unit.product(fa).imap(_._2)(a => ((), a)) <-> fa - def invariantMonoidalRightIdentity[A, B](fa: F[A], b: B): IsEq[F[A]] = - fa.product(F.pure(b)).imap(_._1)(a => (a, b)) <-> fa + def invariantMonoidalRightIdentity[A, B](fa: F[A]): IsEq[F[A]] = + fa.product(F.unit).imap(_._1)(a => (a, ())) <-> fa def invariantMonoidalAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): IsEq[F[(A, (B, C))]] = diff --git a/laws/src/main/scala/cats/laws/InvariantSemigroupalLaws.scala b/laws/src/main/scala/cats/laws/InvariantSemigroupalLaws.scala new file mode 100644 index 0000000000..f1c301787c --- /dev/null +++ b/laws/src/main/scala/cats/laws/InvariantSemigroupalLaws.scala @@ -0,0 +1,15 @@ +package cats +package laws + + +/** + * Laws that are expected for any `cats.InvariantSemigroupal`. + */ +trait InvariantSemigroupalLaws[F[_]] extends InvariantLaws[F] with SemigroupalLaws[F] { + implicit override def F: InvariantSemigroupal[F] + +} +object InvariantSemigroupalLaws { + def apply[F[_]](implicit ev: InvariantSemigroupal[F]): InvariantSemigroupalLaws[F] = + new InvariantSemigroupalLaws[F] { def F: InvariantSemigroupal[F] = ev } +} diff --git a/laws/src/main/scala/cats/laws/discipline/InvariantMonoidalTests.scala b/laws/src/main/scala/cats/laws/discipline/InvariantMonoidalTests.scala index e68f46a12b..0f868c1ebd 100644 --- a/laws/src/main/scala/cats/laws/discipline/InvariantMonoidalTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/InvariantMonoidalTests.scala @@ -6,7 +6,7 @@ import cats.laws.discipline.SemigroupalTests.Isomorphisms import org.scalacheck.{Arbitrary, Cogen} import org.scalacheck.Prop._ -trait InvariantMonoidalTests[F[_]] extends InvariantTests[F] with SemigroupalTests[F] { +trait InvariantMonoidalTests[F[_]] extends InvariantSemigroupalTests[F] { def laws: InvariantMonoidalLaws[F] def invariantMonoidal[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit @@ -27,8 +27,8 @@ trait InvariantMonoidalTests[F[_]] extends InvariantTests[F] with SemigroupalTes val parents = Seq(invariant[A, B, C], semigroupal[A, B, C]) val bases = Seq.empty val props = Seq( - "invariant monoidal left identity" -> forAll((fa: F[A], b: B) => laws.invariantMonoidalLeftIdentity(fa, b)), - "invariant monoidal right identity" -> forAll((fa: F[A], b: B) => laws.invariantMonoidalRightIdentity(fa, b)), + "invariant monoidal left identity" -> forAll((fa: F[A]) => laws.invariantMonoidalLeftIdentity(fa)), + "invariant monoidal right identity" -> forAll((fa: F[A]) => laws.invariantMonoidalRightIdentity(fa)), "invariant monoidal associativity" -> forAll((fa: F[A], fb: F[B], fc: F[C]) => laws.invariantMonoidalAssociativity(fa, fb, fc)) ) } diff --git a/laws/src/main/scala/cats/laws/discipline/InvariantSemigroupalTests.scala b/laws/src/main/scala/cats/laws/discipline/InvariantSemigroupalTests.scala new file mode 100644 index 0000000000..3eae12a930 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/InvariantSemigroupalTests.scala @@ -0,0 +1,34 @@ +package cats +package laws +package discipline + +import cats.InvariantSemigroupal +import cats.laws.discipline.SemigroupalTests.Isomorphisms +import org.scalacheck.{Arbitrary, Cogen} + +trait InvariantSemigroupalTests[F[_]] extends InvariantTests[F] with SemigroupalTests[F] { + def laws: InvariantSemigroupalLaws[F] + + def invariantSemigroupal[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit + arbFA: Arbitrary[F[A]], + arbFB: Arbitrary[F[B]], + arbFC: Arbitrary[F[C]], + CogenA: Cogen[A], + CogenB: Cogen[B], + CogenC: Cogen[C], + EqFA: Eq[F[A]], + EqFB: Eq[F[B]], + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F]): RuleSet = new RuleSet { + val name = "invariantSemigroupal" + val parents = Seq(invariant[A, B, C], semigroupal[A, B, C]) + val bases = Nil + val props = Nil + } +} + +object InvariantSemigroupalTests { + def apply[F[_]: InvariantSemigroupal]: InvariantSemigroupalTests[F] = + new InvariantSemigroupalTests[F] { def laws: InvariantSemigroupalLaws[F] = InvariantSemigroupalLaws[F] } +} diff --git a/tests/src/test/scala/cats/tests/AlgebraInvariantSuite.scala b/tests/src/test/scala/cats/tests/AlgebraInvariantSuite.scala index f9305c294d..01a1ed62db 100644 --- a/tests/src/test/scala/cats/tests/AlgebraInvariantSuite.scala +++ b/tests/src/test/scala/cats/tests/AlgebraInvariantSuite.scala @@ -4,7 +4,7 @@ package tests import cats.Invariant import cats.kernel._ import cats.kernel.laws.discipline.{SemigroupTests, MonoidTests, GroupTests, _} -import cats.laws.discipline.{InvariantMonoidalTests, InvariantTests, SerializableTests, SemigroupalTests} +import cats.laws.discipline.{InvariantMonoidalTests, InvariantTests, SerializableTests, InvariantSemigroupalTests} import cats.laws.discipline.eq._ import org.scalacheck.{Arbitrary, Gen} @@ -61,10 +61,10 @@ class AlgebraInvariantSuite extends CatsSuite { - checkAll("InvariantMonoidal[Semigroup]", SemigroupTests[Int](InvariantMonoidal[Semigroup].pure(0)).semigroup) - checkAll("InvariantMonoidal[CommutativeSemigroup]", CommutativeSemigroupTests[Int](InvariantMonoidal[CommutativeSemigroup].pure(0)).commutativeSemigroup) + checkAll("InvariantMonoidal[Semigroup]", SemigroupTests[Int](InvariantMonoidal[Semigroup].point(0)).semigroup) + checkAll("InvariantMonoidal[CommutativeSemigroup]", CommutativeSemigroupTests[Int](InvariantMonoidal[CommutativeSemigroup].point(0)).commutativeSemigroup) - checkAll("Semigroupal[Monoid]", SemigroupalTests[Monoid].semigroupal[Int, Int, Int]) + checkAll("InvariantSemigroupal[Monoid]", InvariantSemigroupalTests[Monoid].invariantSemigroupal[Int, Int, Int]) { diff --git a/tests/src/test/scala/cats/tests/ApplicativeSuite.scala b/tests/src/test/scala/cats/tests/ApplicativeSuite.scala index 021dd29c2c..4d2c4559b0 100644 --- a/tests/src/test/scala/cats/tests/ApplicativeSuite.scala +++ b/tests/src/test/scala/cats/tests/ApplicativeSuite.scala @@ -2,11 +2,13 @@ package cats package tests import cats.Applicative +import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests} import cats.data.{Validated, Const} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.CoflatMapTests + class ApplicativeSuite extends CatsSuite { test("replicateA creates a List of 'n' copies of given Applicative 'fa'") { @@ -39,14 +41,27 @@ class ApplicativeSuite extends CatsSuite { } } - implicit val listwrapperApplicative = ListWrapper.applicative - implicit val listwrapperCoflatMap = Applicative.coflatMap[ListWrapper] - checkAll("Applicative[ListWrapper].coflatMap", CoflatMapTests[ListWrapper].coflatMap[String, String, String]) + { + implicit val listwrapperApplicative = ListWrapper.applicative + implicit val listwrapperMonoid = Applicative.monoid[ListWrapper, Int] + checkAll("Applicative[ListWrapper].monoid", MonoidTests[ListWrapper[Int]].monoid) + } - implicit val validatedCoflatMap = Applicative.coflatMap[Validated[String, ?]] - checkAll("Applicative[Validated].coflatMap", CoflatMapTests[Validated[String, ?]].coflatMap[String, String, String]) + { + implicit val listwrapperApply = ListWrapper.applyInstance + implicit val listwrapperSemigroup = Apply.semigroup[ListWrapper, Int] + checkAll("Apply[ListWrapper].semigroup", SemigroupTests[ListWrapper[Int]].semigroup) + } + { + implicit val listwrapperApplicative = ListWrapper.applicative + implicit val listwrapperCoflatMap = Applicative.coflatMap[ListWrapper] + checkAll("Applicative[ListWrapper].coflatMap", CoflatMapTests[ListWrapper].coflatMap[String, String, String]) - implicit val constCoflatMap = Applicative.coflatMap[Const[String, ?]] - checkAll("Applicative[Const].coflatMap", CoflatMapTests[Const[String, ?]].coflatMap[String, String, String]) + implicit val validatedCoflatMap = Applicative.coflatMap[Validated[String, ?]] + checkAll("Applicative[Validated].coflatMap", CoflatMapTests[Validated[String, ?]].coflatMap[String, String, String]) + + implicit val constCoflatMap = Applicative.coflatMap[Const[String, ?]] + checkAll("Applicative[Const].coflatMap", CoflatMapTests[Const[String, ?]].coflatMap[String, String, String]) + } } diff --git a/tests/src/test/scala/cats/tests/ContravariantSuite.scala b/tests/src/test/scala/cats/tests/ContravariantSuite.scala index c7007b0f8b..3524633c45 100644 --- a/tests/src/test/scala/cats/tests/ContravariantSuite.scala +++ b/tests/src/test/scala/cats/tests/ContravariantSuite.scala @@ -2,7 +2,11 @@ package cats package tests import cats.data.Const +import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests} +import cats.laws.discipline.ContravariantMonoidalTests import org.scalactic.CanEqual +import org.scalacheck.{Arbitrary, Cogen} +import cats.laws.discipline.eq._ class ContravariantSuite extends CatsSuite { @@ -18,4 +22,33 @@ class ContravariantSuite extends CatsSuite { } } + case class Predicate[A](run: A => Boolean) + + implicit val contravariantMonoidalPredicate: ContravariantMonoidal[Predicate] = + new ContravariantMonoidal[Predicate] { + def unit: Predicate[Unit] = Predicate[Unit](Function.const(true)) + def product[A, B](fa: Predicate[A], fb: Predicate[B]): Predicate[(A, B)] = + Predicate(x => fa.run(x._1) && fb.run(x._2)) + def contramap[A, B](fa: Predicate[A])(f: B => A): Predicate[B] = + Predicate(x => fa.run(f(x))) + } + + implicit def eqPredicate[A: Arbitrary]: Eq[Predicate[A]] = + Eq.by[Predicate[A], A => Boolean](_.run) + + + implicit def arbPredicate[A: Cogen]: Arbitrary[Predicate[A]] = + Arbitrary(implicitly[Arbitrary[A => Boolean]].arbitrary.map(f => Predicate(f))) + + checkAll("ContravariantMonoidal[Predicate]", ContravariantMonoidalTests[Predicate].contravariantMonoidal[Int, Int, Int]) + + { + implicit val predicateMonoid = ContravariantMonoidal.monoid[Predicate, Int] + checkAll("ContravariantMonoidal[Predicate].monoid", MonoidTests[Predicate[Int]].monoid) + } + { + implicit val predicateSemigroup = ContravariantSemigroupal.semigroup[Predicate, Int] + checkAll("ContravariantSemigroupal[Predicate].semigroup", SemigroupTests[Predicate[Int]].semigroup) + } + } diff --git a/tests/src/test/scala/cats/tests/CsvCodecInvariantMonoidalSuite.scala b/tests/src/test/scala/cats/tests/CsvCodecInvariantMonoidalSuite.scala index 37d0a3b0ec..96cbf7d18d 100644 --- a/tests/src/test/scala/cats/tests/CsvCodecInvariantMonoidalSuite.scala +++ b/tests/src/test/scala/cats/tests/CsvCodecInvariantMonoidalSuite.scala @@ -6,6 +6,7 @@ import cats.laws.discipline.{InvariantMonoidalTests, SerializableTests} import cats.instances.all._ import cats.syntax.apply._ import cats.Eq +import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests} import org.scalacheck.{Arbitrary, Gen} object CsvCodecInvariantMonoidalSuite { @@ -30,9 +31,9 @@ object CsvCodecInvariantMonoidalSuite { // In tut/invariantmonoidal.md pure, product and imap are defined in // their own trait to be introduced one by one, trait CCPure { - def pure[A](a: A): CsvCodec[A] = new CsvCodec[A] { - def read(s: CSV): (Option[A], CSV) = (Some(a), s) - def write(a: A): CSV = List.empty + def unit: CsvCodec[Unit] = new CsvCodec[Unit] { + def read(s: CSV): (Option[Unit], CSV) = (Some(()), s) + def write(a: Unit): CSV = List.empty } } @@ -92,4 +93,14 @@ class CsvCodecInvariantMonoidalSuite extends CatsSuite { checkAll("InvariantMonoidal[CsvCodec]", InvariantMonoidalTests[CsvCodec].invariantMonoidal[Int, Int, Int]) checkAll("InvariantMonoidal[CsvCodec]", SerializableTests.serializable(InvariantMonoidal[CsvCodec])) + + { + implicit val csvMonoid = InvariantMonoidal.monoid[CsvCodec, Int] + checkAll("InvariantMonoidal[CsvCodec].monoid", MonoidTests[CsvCodec[Int]].monoid) + } + + { + implicit val csvSemigroup = InvariantSemigroupal.semigroup[CsvCodec, Int] + checkAll("InvariantSemigroupal[CsvCodec].semigroup", SemigroupTests[CsvCodec[Int]].semigroup) + } } diff --git a/tests/src/test/scala/cats/tests/EqSuite.scala b/tests/src/test/scala/cats/tests/EqSuite.scala index 56fea4358c..7b70689a01 100644 --- a/tests/src/test/scala/cats/tests/EqSuite.scala +++ b/tests/src/test/scala/cats/tests/EqSuite.scala @@ -1,26 +1,20 @@ package cats package tests -import org.scalatest._ +import cats.kernel.laws.discipline.SerializableTests +import cats.laws.discipline.ContravariantMonoidalTests +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ -class EqSuite extends FunSuite { - { - import cats.implicits._ +class EqSuite extends CatsSuite { Invariant[Eq] Contravariant[Eq] Semigroupal[Eq] ContravariantSemigroupal[Eq] - ContravariantMonoidal[Eq] - } - { - import cats.instances.eq._ - Invariant[Eq] - Contravariant[Eq] - Semigroupal[Eq] - ContravariantSemigroupal[Eq] - ContravariantMonoidal[Eq] - } + checkAll("Eq[Int]", ContravariantMonoidalTests[Eq].contravariantMonoidal[Int, Int, Int]) + checkAll("ContravariantMonoidal[Eq]", SerializableTests.serializable(ContravariantMonoidal[Eq])) + } diff --git a/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala b/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala index 8eef8ecedc..015e75b3fd 100644 --- a/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala +++ b/tests/src/test/scala/cats/tests/IndexedStateTSuite.scala @@ -2,7 +2,7 @@ package cats package tests import cats.arrow.{Profunctor, Strong} -import cats.data.{Const, EitherT, IndexedStateT, State, StateT} +import cats.data.{EitherT, IndexedStateT, State, StateT} import cats.arrow.Profunctor import cats.kernel.instances.tuple._ @@ -372,13 +372,6 @@ class IndexedStateTSuite extends CatsSuite { SemigroupK[IndexedStateT[ListWrapper, Int, Int, ?]] } - { - // F has a ContravariantMonoidal - val SD = ContravariantMonoidal[StateT[Const[String, ?], String, ?]] - - checkAll("ContravariantMonoidal[StateT[Const[String, ?], String, ?]]", SerializableTests.serializable(SD)) - } - { implicit val iso = SemigroupalTests.Isomorphisms.invariant[State[Long, ?]] diff --git a/tests/src/test/scala/cats/tests/ListWrapper.scala b/tests/src/test/scala/cats/tests/ListWrapper.scala index 821a04865a..212d9d61ce 100644 --- a/tests/src/test/scala/cats/tests/ListWrapper.scala +++ b/tests/src/test/scala/cats/tests/ListWrapper.scala @@ -1,7 +1,6 @@ package cats package tests -import cats.Invariant import cats.instances.list._ import org.scalacheck.{Arbitrary, Cogen} import org.scalacheck.Arbitrary.arbitrary @@ -61,7 +60,15 @@ object ListWrapper { val functor: Functor[ListWrapper] = traverse - val invariant: Invariant[ListWrapper] = functor + val invariantSemigroupal: InvariantSemigroupal[ListWrapper] = new InvariantSemigroupal[ListWrapper] { + def product[A, B](fa: ListWrapper[A], fb: ListWrapper[B]): ListWrapper[(A, B)] = + ListWrapper(fa.list.flatMap(a => fb.list.map(b => (a, b)))) + + def imap[A, B](fa: ListWrapper[A])(f: A => B)(g: B => A) = + ListWrapper(fa.list.map(f)) + } + + val invariant: Invariant[ListWrapper] = invariantSemigroupal val semigroupK: SemigroupK[ListWrapper] = new SemigroupK[ListWrapper] { diff --git a/tests/src/test/scala/cats/tests/MonoidSuite.scala b/tests/src/test/scala/cats/tests/MonoidSuite.scala index b96a146418..702297b92c 100644 --- a/tests/src/test/scala/cats/tests/MonoidSuite.scala +++ b/tests/src/test/scala/cats/tests/MonoidSuite.scala @@ -6,7 +6,7 @@ package tests class MonoidSuite extends CatsSuite { { Invariant[Monoid] - Semigroupal[Monoid] + InvariantSemigroupal[Monoid] } test("companion object syntax") { @@ -19,7 +19,6 @@ class MonoidSuite extends CatsSuite { object MonoidSuite { def summonInstance(): Unit = { Invariant[Monoid] - Semigroupal[Monoid] () } diff --git a/tests/src/test/scala/cats/tests/NestedSuite.scala b/tests/src/test/scala/cats/tests/NestedSuite.scala index d4b079123d..3b52227594 100644 --- a/tests/src/test/scala/cats/tests/NestedSuite.scala +++ b/tests/src/test/scala/cats/tests/NestedSuite.scala @@ -50,6 +50,15 @@ class NestedSuite extends CatsSuite { checkAll("Contravariant[Nested[Option, Show, ?]]", SerializableTests.serializable(Contravariant[Nested[Option, Show, ?]])) } + { + // InvariantSemigroupal + Apply functor composition + implicit val instance = ListWrapper.invariantSemigroupal + checkAll("Nested[ListWrapper, Option, ?]", + InvariantSemigroupalTests[Nested[ListWrapper, Option, ?]].invariantSemigroupal[Int, Int, Int]) + checkAll("InvariantSemigroupal[Nested[ListWrapper, Const[String, ?], ?]", + SerializableTests.serializable(InvariantSemigroupal[Nested[ListWrapper, Option, ?]])) + } + { // Applicative + ContravariantMonoidal functor composition checkAll("Nested[Option, Const[String, ?], ?]", diff --git a/tests/src/test/scala/cats/tests/OrderSuite.scala b/tests/src/test/scala/cats/tests/OrderSuite.scala index d93130924b..db973db828 100644 --- a/tests/src/test/scala/cats/tests/OrderSuite.scala +++ b/tests/src/test/scala/cats/tests/OrderSuite.scala @@ -2,7 +2,10 @@ package cats package tests import Helpers.Ord -import cats.kernel.laws.discipline.OrderTests +import cats.kernel.laws.discipline.{OrderTests, SerializableTests} +import cats.laws.discipline.ContravariantMonoidalTests +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ class OrderSuite extends CatsSuite { { @@ -16,6 +19,9 @@ class OrderSuite extends CatsSuite { checkAll("Float", OrderTests[Float].order) checkAll("Long", OrderTests[Long].order) + checkAll("Order", ContravariantMonoidalTests[Order].contravariantMonoidal[Int, Int, Int]) + checkAll("ContravariantMonoidal[Order]", SerializableTests.serializable(ContravariantMonoidal[Order])) + test("order ops syntax"){ forAll { (i: Ord, j: Ord) => (i compare j) should ===(Order.compare(i, j)) diff --git a/tests/src/test/scala/cats/tests/PartialOrderSuite.scala b/tests/src/test/scala/cats/tests/PartialOrderSuite.scala index 120b75f40b..5db795ecac 100644 --- a/tests/src/test/scala/cats/tests/PartialOrderSuite.scala +++ b/tests/src/test/scala/cats/tests/PartialOrderSuite.scala @@ -2,7 +2,11 @@ package cats package tests import Helpers.POrd +import cats.kernel.laws.discipline.SerializableTests +import cats.laws.discipline.ContravariantMonoidalTests import org.scalatest.Assertion +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ class PartialOrderSuite extends CatsSuite { @@ -19,6 +23,9 @@ class PartialOrderSuite extends CatsSuite { Contravariant[PartialOrder] } + checkAll("PartialOrder[Int]", ContravariantMonoidalTests[PartialOrder].contravariantMonoidal[Int, Int, Int]) + checkAll("ContravariantMonoidal[PartialOrder]", SerializableTests.serializable(ContravariantMonoidal[PartialOrder])) + test("companion object syntax") { forAll { (i: Int, j: Int) => checkPartialCompare(PartialOrder.partialCompare(i, j), catsKernelStdOrderForInt.partialCompare(i, j)) diff --git a/tests/src/test/scala/cats/tests/PartialOrderingSuite.scala b/tests/src/test/scala/cats/tests/PartialOrderingSuite.scala index 81d3e37b2a..4e1c99d657 100644 --- a/tests/src/test/scala/cats/tests/PartialOrderingSuite.scala +++ b/tests/src/test/scala/cats/tests/PartialOrderingSuite.scala @@ -16,4 +16,7 @@ class PartialOrderingSuite extends CatsSuite { checkAll("Contravariant[PartialOrdering]", ContravariantTests[PartialOrdering].contravariant[Int, Int, Int]) checkAll("Semigroupal[PartialOrdering]", SemigroupalTests[PartialOrdering].semigroupal[Int, Int, Int]) checkAll("Contravariant[PartialOrdering]", SerializableTests.serializable(Contravariant[PartialOrdering])) + + checkAll("PartialOrdering[Int]", ContravariantMonoidalTests[PartialOrdering].contravariantMonoidal[Int, Int, Int]) + checkAll("ContravariantMonoidal[PartialOrdering]", SerializableTests.serializable(ContravariantMonoidal[PartialOrdering])) }