From 22d15ea46d6512206b0b501f456e4a735478e3ea Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sun, 6 Nov 2016 16:23:43 -0500 Subject: [PATCH] Consistency for ops classes 1. Extend AnyVal when possible, unless there are macro ops 1a. Don't take implicit params in the ops class param list, again unless there are macro ops. 2. Make all conversions to ops classes final. They are all declared in traits, so users with their own syntax hierarchy have no chance of inlining unless they are marked final. Plus, it's consistent. 3. Make all ops class constructor fields private by default, to avoid polluting the namespace of the type. Remove private val part, because scala 2.10 doesn't support it Ops class guidelines Add CartesianTest --- .../main/scala/cats/syntax/applicative.scala | 14 +++++----- .../scala/cats/syntax/applicativeError.scala | 21 +++++++-------- core/src/main/scala/cats/syntax/apply.scala | 2 +- .../main/scala/cats/syntax/bitraverse.scala | 12 ++++----- .../main/scala/cats/syntax/cartesian.scala | 12 +++++---- .../main/scala/cats/syntax/coflatMap.scala | 2 +- core/src/main/scala/cats/syntax/comonad.scala | 2 +- .../scala/cats/syntax/contravariant.scala | 2 +- core/src/main/scala/cats/syntax/either.scala | 10 +++---- core/src/main/scala/cats/syntax/eitherK.scala | 2 +- core/src/main/scala/cats/syntax/eq.scala | 1 + core/src/main/scala/cats/syntax/flatMap.scala | 14 +++++----- .../src/main/scala/cats/syntax/foldable.scala | 10 +++---- core/src/main/scala/cats/syntax/functor.scala | 2 +- .../scala/cats/syntax/functorFilter.scala | 2 +- core/src/main/scala/cats/syntax/group.scala | 2 +- .../main/scala/cats/syntax/invariant.scala | 2 +- core/src/main/scala/cats/syntax/ior.scala | 2 +- core/src/main/scala/cats/syntax/list.scala | 2 +- core/src/main/scala/cats/syntax/monad.scala | 16 +++++------ .../main/scala/cats/syntax/monadCombine.scala | 12 ++++----- .../main/scala/cats/syntax/monadError.scala | 6 ++--- .../main/scala/cats/syntax/monadFilter.scala | 2 +- core/src/main/scala/cats/syntax/monoid.scala | 6 ++--- core/src/main/scala/cats/syntax/order.scala | 2 +- .../main/scala/cats/syntax/partialOrder.scala | 2 +- .../main/scala/cats/syntax/reducible.scala | 8 +++--- .../main/scala/cats/syntax/semigroup.scala | 2 +- .../main/scala/cats/syntax/semigroupk.scala | 2 +- core/src/main/scala/cats/syntax/show.scala | 2 +- .../main/scala/cats/syntax/transLift.scala | 3 ++- .../src/main/scala/cats/syntax/traverse.scala | 2 +- .../scala/cats/syntax/traverseFilter.scala | 2 +- .../main/scala/cats/syntax/validated.scala | 2 +- core/src/main/scala/cats/syntax/vector.scala | 2 +- core/src/main/scala/cats/syntax/writer.scala | 2 +- docs/src/main/tut/guidelines.md | 27 +++++++++++++++++++ 37 files changed, 124 insertions(+), 92 deletions(-) create mode 100644 docs/src/main/tut/guidelines.md diff --git a/core/src/main/scala/cats/syntax/applicative.scala b/core/src/main/scala/cats/syntax/applicative.scala index 322593327b..53e92664b7 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -2,16 +2,18 @@ package cats package syntax trait ApplicativeSyntax { - implicit def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) - implicit def catsSyntaxApplicative[F[_], A](fa: F[A])(implicit F: Applicative[F]): ApplicativeOps[F, A] = new ApplicativeOps[F, A](fa) + implicit final def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = + new ApplicativeIdOps[A](a) + implicit final def catsSyntaxApplicative[F[_], A](fa: F[A]): ApplicativeOps[F, A] = + new ApplicativeOps[F, A](fa) } final class ApplicativeIdOps[A](val a: A) extends AnyVal { def pure[F[_]](implicit F: Applicative[F]): F[A] = F.pure(a) } -final class ApplicativeOps[F[_], A](fa: F[A])(implicit F: Applicative[F]) { - def replicateA(n: Int): F[List[A]] = F.replicateA(n, fa) - def unlessA(cond: Boolean): F[Unit] = F.unlessA(cond)(fa) - def whenA(cond: Boolean): F[Unit] = F.whenA(cond)(fa) +final class ApplicativeOps[F[_], A](val fa: F[A]) extends AnyVal { + def replicateA(n: Int)(implicit F: Applicative[F]): F[List[A]] = F.replicateA(n, fa) + def unlessA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.unlessA(cond)(fa) + def whenA(cond: Boolean)(implicit F: Applicative[F]): F[Unit] = F.whenA(cond)(fa) } diff --git a/core/src/main/scala/cats/syntax/applicativeError.scala b/core/src/main/scala/cats/syntax/applicativeError.scala index 10dc9a6079..166adddab3 100644 --- a/core/src/main/scala/cats/syntax/applicativeError.scala +++ b/core/src/main/scala/cats/syntax/applicativeError.scala @@ -4,35 +4,34 @@ package syntax import cats.data.EitherT trait ApplicativeErrorSyntax { - implicit def catsSyntaxApplicativeErrorId[E](e: E): ApplicativeErrorIdOps[E] = + implicit final def catsSyntaxApplicativeErrorId[E](e: E): ApplicativeErrorIdOps[E] = new ApplicativeErrorIdOps(e) - implicit def catsSyntaxApplicativeError[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]): ApplicativeErrorOps[F, E, A] = + implicit final def catsSyntaxApplicativeError[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]): ApplicativeErrorOps[F, E, A] = new ApplicativeErrorOps[F, E, A](fa) - } -final class ApplicativeErrorIdOps[E](e: E) { +final class ApplicativeErrorIdOps[E](val e: E) extends AnyVal { def raiseError[F[_], A](implicit F: ApplicativeError[F, E]): F[A] = F.raiseError(e) } -final class ApplicativeErrorOps[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]) { - def handleError(f: E => A): F[A] = +final class ApplicativeErrorOps[F[_], E, A](val fa: F[A]) extends AnyVal { + def handleError(f: E => A)(implicit F: ApplicativeError[F, E]): F[A] = F.handleError(fa)(f) - def handleErrorWith(f: E => F[A]): F[A] = + def handleErrorWith(f: E => F[A])(implicit F: ApplicativeError[F, E]): F[A] = F.handleErrorWith(fa)(f) - def attempt: F[Either[E, A]] = + def attempt(implicit F: ApplicativeError[F, E]): F[Either[E, A]] = F.attempt(fa) - def attemptT: EitherT[F, E, A] = + def attemptT(implicit F: ApplicativeError[F, E]): EitherT[F, E, A] = F.attemptT(fa) - def recover(pf: PartialFunction[E, A]): F[A] = + def recover(pf: PartialFunction[E, A])(implicit F: ApplicativeError[F, E]): F[A] = F.recover(fa)(pf) - def recoverWith(pf: PartialFunction[E, F[A]]): F[A] = + def recoverWith(pf: PartialFunction[E, F[A]])(implicit F: ApplicativeError[F, E]): F[A] = F.recoverWith(fa)(pf) } diff --git a/core/src/main/scala/cats/syntax/apply.scala b/core/src/main/scala/cats/syntax/apply.scala index 8d9be51c9e..2671087bf1 100644 --- a/core/src/main/scala/cats/syntax/apply.scala +++ b/core/src/main/scala/cats/syntax/apply.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait ApplySyntax1 { - implicit def catsSyntaxUApply[FA](fa: FA)(implicit U: Unapply[Apply, FA]): Apply.Ops[U.M, U.A] = + implicit final def catsSyntaxUApply[FA](fa: FA)(implicit U: Unapply[Apply, FA]): Apply.Ops[U.M, U.A] = new Apply.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/bitraverse.scala b/core/src/main/scala/cats/syntax/bitraverse.scala index e34372ade3..d24049def7 100644 --- a/core/src/main/scala/cats/syntax/bitraverse.scala +++ b/core/src/main/scala/cats/syntax/bitraverse.scala @@ -2,21 +2,21 @@ package cats package syntax trait BitraverseSyntax extends BitraverseSyntax1 { - implicit def catsSyntaxBitraverse[F[_, _]: Bitraverse, A, B](fab: F[A, B]): BitraverseOps[F, A, B] = + implicit final def catsSyntaxBitraverse[F[_, _]: Bitraverse, A, B](fab: F[A, B]): BitraverseOps[F, A, B] = new BitraverseOps[F, A, B](fab) } private[syntax] trait BitraverseSyntax1 { - implicit def catsSyntaxNestedBitraverse[F[_, _]: Bitraverse, G[_], A, B](fgagb: F[G[A], G[B]]): NestedBitraverseOps[F, G, A, B] = + implicit final def catsSyntaxNestedBitraverse[F[_, _]: Bitraverse, G[_], A, B](fgagb: F[G[A], G[B]]): NestedBitraverseOps[F, G, A, B] = new NestedBitraverseOps[F, G, A, B](fgagb) } -final class BitraverseOps[F[_, _], A, B](fab: F[A, B])(implicit F: Bitraverse[F]) { - def bitraverse[G[_]: Applicative, C, D](f: A => G[C], g: B => G[D]): G[F[C, D]] = +final class BitraverseOps[F[_, _], A, B](val fab: F[A, B]) extends AnyVal { + def bitraverse[G[_]: Applicative, C, D](f: A => G[C], g: B => G[D])(implicit F: Bitraverse[F]): G[F[C, D]] = F.bitraverse(fab)(f, g) } -final class NestedBitraverseOps[F[_, _], G[_], A, B](fgagb: F[G[A], G[B]])(implicit F: Bitraverse[F]) { - def bisequence(implicit G: Applicative[G]): G[F[A, B]] = +final class NestedBitraverseOps[F[_, _], G[_], A, B](val fgagb: F[G[A], G[B]]) extends AnyVal { + def bisequence(implicit F: Bitraverse[F], G: Applicative[G]): G[F[A, B]] = F.bisequence(fgagb) } diff --git a/core/src/main/scala/cats/syntax/cartesian.scala b/core/src/main/scala/cats/syntax/cartesian.scala index 64ce35084a..efff7b4396 100644 --- a/core/src/main/scala/cats/syntax/cartesian.scala +++ b/core/src/main/scala/cats/syntax/cartesian.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait CartesianSyntax1 { - implicit def catsSyntaxUCartesian[FA](fa: FA)(implicit U: Unapply[Cartesian, FA]): CartesianOps[U.M, U.A] = + implicit final def catsSyntaxUCartesian[FA](fa: FA)(implicit U: Unapply[Cartesian, FA]): CartesianOps[U.M, U.A] = new CartesianOps[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC @@ -10,7 +10,7 @@ private[syntax] trait CartesianSyntax1 { } trait CartesianSyntax extends CartesianSyntax1 { - implicit def catsSyntaxCartesian[F[_], A](fa: F[A])(implicit F: Cartesian[F]): CartesianOps[F, A] = + implicit final def catsSyntaxCartesian[F[_], A](fa: F[A])(implicit F: Cartesian[F]): CartesianOps[F, A] = new CartesianOps[F, A] { val self = fa val typeClassInstance = F @@ -18,11 +18,13 @@ trait CartesianSyntax extends CartesianSyntax1 { } abstract class CartesianOps[F[_], A] extends Cartesian.Ops[F, A] { - def |@|[B](fb: F[B]): CartesianBuilder[F]#CartesianBuilder2[A, B] = + final def |@|[B](fb: F[B]): CartesianBuilder[F]#CartesianBuilder2[A, B] = new CartesianBuilder[F] |@| self |@| fb - def *>[B](fb: F[B])(implicit F: Functor[F]): F[B] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => b } + final def *>[B](fb: F[B])(implicit F: Functor[F]): F[B] = + F.map(typeClassInstance.product(self, fb)) { case (_, b) => b } - def <*[B](fb: F[B])(implicit F: Functor[F]): F[A] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => a } + final def <*[B](fb: F[B])(implicit F: Functor[F]): F[A] = + F.map(typeClassInstance.product(self, fb)) { case (a, _) => a } } diff --git a/core/src/main/scala/cats/syntax/coflatMap.scala b/core/src/main/scala/cats/syntax/coflatMap.scala index e63612cc78..e394677da4 100644 --- a/core/src/main/scala/cats/syntax/coflatMap.scala +++ b/core/src/main/scala/cats/syntax/coflatMap.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait CoflatMapSyntax1 { - implicit def catsSyntaxUCoflatMap[FA](fa: FA)(implicit U: Unapply[CoflatMap, FA]): CoflatMap.Ops[U.M, U.A] = new CoflatMap.Ops[U.M, U.A] { + implicit final def catsSyntaxUCoflatMap[FA](fa: FA)(implicit U: Unapply[CoflatMap, FA]): CoflatMap.Ops[U.M, U.A] = new CoflatMap.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC } diff --git a/core/src/main/scala/cats/syntax/comonad.scala b/core/src/main/scala/cats/syntax/comonad.scala index 95dcdeb47a..cbeaf7bb0f 100644 --- a/core/src/main/scala/cats/syntax/comonad.scala +++ b/core/src/main/scala/cats/syntax/comonad.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait ComonadSyntax1 { - implicit def catsSyntaxUComonad[FA](fa: FA)(implicit U: Unapply[Comonad, FA]): Comonad.Ops[U.M, U.A] = + implicit final def catsSyntaxUComonad[FA](fa: FA)(implicit U: Unapply[Comonad, FA]): Comonad.Ops[U.M, U.A] = new Comonad.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/contravariant.scala b/core/src/main/scala/cats/syntax/contravariant.scala index 9cabd49807..7eda421d6b 100644 --- a/core/src/main/scala/cats/syntax/contravariant.scala +++ b/core/src/main/scala/cats/syntax/contravariant.scala @@ -4,7 +4,7 @@ package syntax import cats.functor.Contravariant private[syntax] trait ContravariantSyntax1 { - implicit def catsSyntaxUContravariant[FA](fa: FA)(implicit U: Unapply[Contravariant, FA]): Contravariant.Ops[U.M, U.A] = + implicit final def catsSyntaxUContravariant[FA](fa: FA)(implicit U: Unapply[Contravariant, FA]): Contravariant.Ops[U.M, U.A] = new Contravariant.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/either.scala b/core/src/main/scala/cats/syntax/either.scala index 906ba49f2b..740a9f6558 100644 --- a/core/src/main/scala/cats/syntax/either.scala +++ b/core/src/main/scala/cats/syntax/either.scala @@ -6,15 +6,15 @@ import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} trait EitherSyntax { - implicit def catsSyntaxEither[A, B](eab: Either[A, B]): EitherOps[A, B] = new EitherOps(eab) + implicit final def catsSyntaxEither[A, B](eab: Either[A, B]): EitherOps[A, B] = new EitherOps(eab) - implicit def catsSyntaxEitherObject(either: Either.type): EitherObjectOps = new EitherObjectOps(either) // scalastyle:off ensure.single.space.after.token + implicit final def catsSyntaxEitherObject(either: Either.type): EitherObjectOps = new EitherObjectOps(either) // scalastyle:off ensure.single.space.after.token - implicit def catsSyntaxLeft[A, B](left: Left[A, B]): LeftOps[A, B] = new LeftOps(left) + implicit final def catsSyntaxLeft[A, B](left: Left[A, B]): LeftOps[A, B] = new LeftOps(left) - implicit def catsSyntaxRight[A, B](right: Right[A, B]): RightOps[A, B] = new RightOps(right) + implicit final def catsSyntaxRight[A, B](right: Right[A, B]): RightOps[A, B] = new RightOps(right) - implicit def catsSyntaxEitherId[A](a: A): EitherIdOps[A] = new EitherIdOps(a) + implicit final def catsSyntaxEitherId[A](a: A): EitherIdOps[A] = new EitherIdOps(a) } final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/eitherK.scala b/core/src/main/scala/cats/syntax/eitherK.scala index 574b18b5c6..d9f3f0ecb8 100644 --- a/core/src/main/scala/cats/syntax/eitherK.scala +++ b/core/src/main/scala/cats/syntax/eitherK.scala @@ -4,7 +4,7 @@ package syntax import cats.data.EitherK trait EitherKSyntax { - implicit def catsSyntaxEitherK[F[_], A](a: F[A]): EitherKOps[F, A] = new EitherKOps(a) + implicit final def catsSyntaxEitherK[F[_], A](a: F[A]): EitherKOps[F, A] = new EitherKOps(a) } final class EitherKOps[F[_], A](val fa: F[A]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/eq.scala b/core/src/main/scala/cats/syntax/eq.scala index 87250f8643..3737e95d89 100644 --- a/core/src/main/scala/cats/syntax/eq.scala +++ b/core/src/main/scala/cats/syntax/eq.scala @@ -4,6 +4,7 @@ package syntax import cats.macros.Ops trait EqSyntax { + /** not final so it can be disabled in favor of scalactic equality in tests */ implicit def catsSyntaxEq[A: Eq](a: A): EqOps[A] = new EqOps[A](a) } diff --git a/core/src/main/scala/cats/syntax/flatMap.scala b/core/src/main/scala/cats/syntax/flatMap.scala index 1723dddf83..596019e860 100644 --- a/core/src/main/scala/cats/syntax/flatMap.scala +++ b/core/src/main/scala/cats/syntax/flatMap.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait FlatMapSyntax1 { - implicit def catsSyntaxUFlatMap[FA](fa: FA)(implicit U: Unapply[FlatMap, FA]): FlatMap.Ops[U.M, U.A] = + implicit final def catsSyntaxUFlatMap[FA](fa: FA)(implicit U: Unapply[FlatMap, FA]): FlatMap.Ops[U.M, U.A] = new FlatMap.Ops[U.M, U.A]{ val self = U.subst(fa) val typeClassInstance = U.TC @@ -11,14 +11,14 @@ private[syntax] trait FlatMapSyntax1 { trait FlatMapSyntax extends FlatMap.ToFlatMapOps with FlatMapSyntax1 { - implicit def catsSyntaxFlatten[F[_]: FlatMap, A](ffa: F[F[A]]): FlattenOps[F, A] = + implicit final def catsSyntaxFlatten[F[_]: FlatMap, A](ffa: F[F[A]]): FlattenOps[F, A] = new FlattenOps[F, A](ffa) - implicit def catsSyntaxIfM[F[_]: FlatMap](fa: F[Boolean]): IfMOps[F] = + implicit final def catsSyntaxIfM[F[_]: FlatMap](fa: F[Boolean]): IfMOps[F] = new IfMOps[F](fa) } -final class FlattenOps[F[_], A](ffa: F[F[A]])(implicit F: FlatMap[F]) { +final class FlattenOps[F[_], A](val ffa: F[F[A]]) extends AnyVal { /** * Flatten nested `F` values. @@ -32,10 +32,10 @@ final class FlattenOps[F[_], A](ffa: F[F[A]])(implicit F: FlatMap[F]) { * res0: ErrorOr[Int] = Right(3) * }}} */ - def flatten: F[A] = F.flatten(ffa) + def flatten(implicit F: FlatMap[F]): F[A] = F.flatten(ffa) } -final class IfMOps[F[_]](fa: F[Boolean])(implicit F: FlatMap[F]) { +final class IfMOps[F[_]](val fa: F[Boolean]) extends AnyVal { /** * A conditional lifted into the `F` context. @@ -56,5 +56,5 @@ final class IfMOps[F[_]](fa: F[Boolean])(implicit F: FlatMap[F]) { * res1: Int = 0 * }}} */ - def ifM[B](ifTrue: => F[B], ifFalse: => F[B]): F[B] = F.ifM(fa)(ifTrue, ifFalse) + def ifM[B](ifTrue: => F[B], ifFalse: => F[B])(implicit F: FlatMap[F]): F[B] = F.ifM(fa)(ifTrue, ifFalse) } diff --git a/core/src/main/scala/cats/syntax/foldable.scala b/core/src/main/scala/cats/syntax/foldable.scala index 67589a6f58..9a765f08f0 100644 --- a/core/src/main/scala/cats/syntax/foldable.scala +++ b/core/src/main/scala/cats/syntax/foldable.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait FoldableSyntax1 { - implicit def catsSyntaxUFoldable[FA](fa: FA)(implicit U: Unapply[Foldable, FA]): Foldable.Ops[U.M, U.A] = + implicit final def catsSyntaxUFoldable[FA](fa: FA)(implicit U: Unapply[Foldable, FA]): Foldable.Ops[U.M, U.A] = new Foldable.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC @@ -10,12 +10,12 @@ private[syntax] trait FoldableSyntax1 { } trait FoldableSyntax extends Foldable.ToFoldableOps with FoldableSyntax1 { - implicit def catsSyntaxNestedFoldable[F[_]: Foldable, G[_], A](fga: F[G[A]]): NestedFoldableOps[F, G, A] = + implicit final def catsSyntaxNestedFoldable[F[_]: Foldable, G[_], A](fga: F[G[A]]): NestedFoldableOps[F, G, A] = new NestedFoldableOps[F, G, A](fga) } -final class NestedFoldableOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Foldable[F]) { - def sequence_(implicit G: Applicative[G]): G[Unit] = F.sequence_(fga) +final class NestedFoldableOps[F[_], G[_], A](val fga: F[G[A]]) extends AnyVal { + def sequence_(implicit F: Foldable[F], G: Applicative[G]): G[Unit] = F.sequence_(fga) /** * @see [[Foldable.foldK]]. @@ -29,5 +29,5 @@ final class NestedFoldableOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Foldable[ * res0: Set[Int] = Set(1, 2, 3, 4) * }}} */ - def foldK(implicit G: MonoidK[G]): G[A] = F.foldK(fga) + def foldK(implicit F: Foldable[F], G: MonoidK[G]): G[A] = F.foldK(fga) } diff --git a/core/src/main/scala/cats/syntax/functor.scala b/core/src/main/scala/cats/syntax/functor.scala index ba8be22cbd..478299c92a 100644 --- a/core/src/main/scala/cats/syntax/functor.scala +++ b/core/src/main/scala/cats/syntax/functor.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait FunctorSyntax1 { - implicit def catsSyntaxUFunctor[FA](fa: FA)(implicit U: Unapply[Functor, FA]): Functor.Ops[U.M, U.A] = + implicit final def catsSyntaxUFunctor[FA](fa: FA)(implicit U: Unapply[Functor, FA]): Functor.Ops[U.M, U.A] = new Functor.Ops[U.M, U.A]{ val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/functorFilter.scala b/core/src/main/scala/cats/syntax/functorFilter.scala index eb7757bee5..dfefedcce9 100644 --- a/core/src/main/scala/cats/syntax/functorFilter.scala +++ b/core/src/main/scala/cats/syntax/functorFilter.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait FunctorFilterSyntax1 { - implicit def catsSyntaxUFunctorFilter[FA](fa: FA)(implicit U: Unapply[FunctorFilter, FA]): FunctorFilter.Ops[U.M, U.A] = + implicit final def catsSyntaxUFunctorFilter[FA](fa: FA)(implicit U: Unapply[FunctorFilter, FA]): FunctorFilter.Ops[U.M, U.A] = new FunctorFilter.Ops[U.M, U.A]{ val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/group.scala b/core/src/main/scala/cats/syntax/group.scala index dca6c12780..01d29cf3ae 100644 --- a/core/src/main/scala/cats/syntax/group.scala +++ b/core/src/main/scala/cats/syntax/group.scala @@ -5,7 +5,7 @@ import cats.macros.Ops trait GroupSyntax extends SemigroupSyntax { // TODO: use simulacrum instances eventually - implicit def catsSyntaxGroup[A: Group](a: A): GroupOps[A] = + implicit final def catsSyntaxGroup[A: Group](a: A): GroupOps[A] = new GroupOps[A](a) } diff --git a/core/src/main/scala/cats/syntax/invariant.scala b/core/src/main/scala/cats/syntax/invariant.scala index 6271cc8077..3f425c002c 100644 --- a/core/src/main/scala/cats/syntax/invariant.scala +++ b/core/src/main/scala/cats/syntax/invariant.scala @@ -4,7 +4,7 @@ package syntax import cats.functor.Invariant private[syntax] trait InvariantSyntax1 { - implicit def catsSyntaxUInvariant[FA](fa: FA)(implicit U: Unapply[Invariant, FA]): Invariant.Ops[U.M, U.A] = + implicit final def catsSyntaxUInvariant[FA](fa: FA)(implicit U: Unapply[Invariant, FA]): Invariant.Ops[U.M, U.A] = new Invariant.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/ior.scala b/core/src/main/scala/cats/syntax/ior.scala index bc8bdfacba..41ab0bca1b 100644 --- a/core/src/main/scala/cats/syntax/ior.scala +++ b/core/src/main/scala/cats/syntax/ior.scala @@ -3,7 +3,7 @@ package cats.syntax import cats.data.Ior trait IorSyntax { - implicit def catsSyntaxIorId[A](a: A): IorIdOps[A] = new IorIdOps(a) + implicit final def catsSyntaxIorId[A](a: A): IorIdOps[A] = new IorIdOps(a) } final class IorIdOps[A](val a: A) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 7079dbfad4..d354eb7c7c 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -4,7 +4,7 @@ package syntax import cats.data.NonEmptyList trait ListSyntax { - implicit def catsSyntaxList[A](la: List[A]): ListOps[A] = new ListOps(la) + implicit final def catsSyntaxList[A](la: List[A]): ListOps[A] = new ListOps(la) } final class ListOps[A](val la: List[A]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/monad.scala b/core/src/main/scala/cats/syntax/monad.scala index 0dcc5f1b6c..2772c5a8ed 100644 --- a/core/src/main/scala/cats/syntax/monad.scala +++ b/core/src/main/scala/cats/syntax/monad.scala @@ -2,14 +2,14 @@ package cats package syntax trait MonadSyntax { - implicit def catsSyntaxMonad[F[_]: Monad, A](fa: F[A]): MonadOps[F, A] = new MonadOps(fa) + implicit final def catsSyntaxMonad[F[_], A](fa: F[A]): MonadOps[F, A] = new MonadOps(fa) } -final class MonadOps[F[_], A](fa: F[A])(implicit M: Monad[F]) { - def whileM[G[_]](p: F[Boolean])(implicit G: Alternative[G]): F[G[A]] = M.whileM(p)(fa) - def whileM_(p: F[Boolean]): F[Unit] = M.whileM_(p)(fa) - def untilM[G[_]](p: F[Boolean])(implicit G: Alternative[G]): F[G[A]] = M.untilM(fa)(p) - def untilM_(p: F[Boolean]): F[Unit] = M.untilM_(fa)(p) - def iterateWhile(p: A => Boolean): F[A] = M.iterateWhile(fa)(p) - def iterateUntil(p: A => Boolean): F[A] = M.iterateUntil(fa)(p) +final class MonadOps[F[_], A](val fa: F[A]) extends AnyVal { + def whileM[G[_]](p: F[Boolean])(implicit M: Monad[F], G: Alternative[G]): F[G[A]] = M.whileM(p)(fa) + def whileM_(p: F[Boolean])(implicit M: Monad[F]): F[Unit] = M.whileM_(p)(fa) + def untilM[G[_]](p: F[Boolean])(implicit M: Monad[F], G: Alternative[G]): F[G[A]] = M.untilM(fa)(p) + def untilM_(p: F[Boolean])(implicit M: Monad[F]): F[Unit] = M.untilM_(fa)(p) + def iterateWhile(p: A => Boolean)(implicit M: Monad[F]): F[A] = M.iterateWhile(fa)(p) + def iterateUntil(p: A => Boolean)(implicit M: Monad[F]): F[A] = M.iterateUntil(fa)(p) } diff --git a/core/src/main/scala/cats/syntax/monadCombine.scala b/core/src/main/scala/cats/syntax/monadCombine.scala index 40e53f2449..f970cf6b9b 100644 --- a/core/src/main/scala/cats/syntax/monadCombine.scala +++ b/core/src/main/scala/cats/syntax/monadCombine.scala @@ -3,14 +3,14 @@ package syntax trait MonadCombineSyntax { // TODO: use simulacrum instances eventually - implicit def catsSyntaxMonadCombine[F[_]: MonadCombine, G[_], A](fga: F[G[A]]): MonadCombineOps[F, G, A] = + implicit final def catsSyntaxMonadCombine[F[_]: MonadCombine, G[_], A](fga: F[G[A]]): MonadCombineOps[F, G, A] = new MonadCombineOps[F, G, A](fga) - implicit def catsSyntaxMonadCombineSeparate[F[_]: MonadCombine, G[_, _], A, B](fgab: F[G[A, B]]): SeparateOps[F, G, A, B] = + implicit final def catsSyntaxMonadCombineSeparate[F[_]: MonadCombine, G[_, _], A, B](fgab: F[G[A, B]]): SeparateOps[F, G, A, B] = new SeparateOps[F, G, A, B](fgab) } -final class MonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombine[F]) { +final class MonadCombineOps[F[_], G[_], A](val fga: F[G[A]]) extends AnyVal { /** * @see [[MonadCombine.unite]] @@ -23,10 +23,10 @@ final class MonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombin * res0: List[Int] = List(1, 2, 3, 4) * }}} */ - def unite(implicit G: Foldable[G]): F[A] = F.unite(fga) + def unite(implicit F: MonadCombine[F], G: Foldable[G]): F[A] = F.unite(fga) } -final class SeparateOps[F[_], G[_, _], A, B](fgab: F[G[A, B]])(implicit F: MonadCombine[F]) { +final class SeparateOps[F[_], G[_, _], A, B](val fgab: F[G[A, B]]) extends AnyVal { /** * @see [[MonadCombine.separate]] @@ -39,5 +39,5 @@ final class SeparateOps[F[_], G[_, _], A, B](fgab: F[G[A, B]])(implicit F: Monad * res0: (List[String], List[Int]) = (List(error),List(1)) * }}} */ - def separate(implicit G: Bifoldable[G]): (F[A], F[B]) = F.separate(fgab) + def separate(implicit F: MonadCombine[F], G: Bifoldable[G]): (F[A], F[B]) = F.separate(fgab) } diff --git a/core/src/main/scala/cats/syntax/monadError.scala b/core/src/main/scala/cats/syntax/monadError.scala index 8d0bf677a1..8ea1d590d1 100644 --- a/core/src/main/scala/cats/syntax/monadError.scala +++ b/core/src/main/scala/cats/syntax/monadError.scala @@ -2,11 +2,11 @@ package cats package syntax trait MonadErrorSyntax { - implicit def catsSyntaxMonadError[F[_], E, A](fa: F[A])(implicit F: MonadError[F, E]): MonadErrorOps[F, E, A] = + implicit final def catsSyntaxMonadError[F[_], E, A](fa: F[A])(implicit F: MonadError[F, E]): MonadErrorOps[F, E, A] = new MonadErrorOps(fa) } -final class MonadErrorOps[F[_], E, A](fa: F[A])(implicit F: MonadError[F, E]) { - def ensure(error: => E)(predicate: A => Boolean): F[A] = +final class MonadErrorOps[F[_], E, A](val fa: F[A]) extends AnyVal { + def ensure(error: => E)(predicate: A => Boolean)(implicit F: MonadError[F, E]): F[A] = F.ensure(fa)(error)(predicate) } diff --git a/core/src/main/scala/cats/syntax/monadFilter.scala b/core/src/main/scala/cats/syntax/monadFilter.scala index 21c98a869b..8413fb64f5 100644 --- a/core/src/main/scala/cats/syntax/monadFilter.scala +++ b/core/src/main/scala/cats/syntax/monadFilter.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait MonadFilterSyntax1 { - implicit def catsSyntaxUMonadFilter[FA](fa: FA)(implicit U: Unapply[MonadFilter, FA]): MonadFilter.Ops[U.M, U.A] = + implicit final def catsSyntaxUMonadFilter[FA](fa: FA)(implicit U: Unapply[MonadFilter, FA]): MonadFilter.Ops[U.M, U.A] = new MonadFilter.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/monoid.scala b/core/src/main/scala/cats/syntax/monoid.scala index 0858424a04..dcbf0fd680 100644 --- a/core/src/main/scala/cats/syntax/monoid.scala +++ b/core/src/main/scala/cats/syntax/monoid.scala @@ -3,10 +3,10 @@ package syntax trait MonoidSyntax extends SemigroupSyntax { // TODO: use simulacrum instances eventually - implicit def catsSyntaxMonoid[A: Monoid](a: A): MonoidOps[A] = + implicit final def catsSyntaxMonoid[A: Monoid](a: A): MonoidOps[A] = new MonoidOps[A](a) } -final class MonoidOps[A: Monoid](lhs: A) { - def isEmpty(implicit eq: Eq[A]): Boolean = Monoid[A].isEmpty(lhs)(eq) +final class MonoidOps[A](val lhs: A) extends AnyVal { + def isEmpty(implicit A: Monoid[A], eq: Eq[A]): Boolean = A.isEmpty(lhs)(eq) } diff --git a/core/src/main/scala/cats/syntax/order.scala b/core/src/main/scala/cats/syntax/order.scala index e5c14bfe4d..36c33386a3 100644 --- a/core/src/main/scala/cats/syntax/order.scala +++ b/core/src/main/scala/cats/syntax/order.scala @@ -5,7 +5,7 @@ import cats.macros.Ops import cats.kernel.Comparison trait OrderSyntax extends PartialOrderSyntax { - implicit def catsSyntaxOrder[A: Order](a: A): OrderOps[A] = + implicit final def catsSyntaxOrder[A: Order](a: A): OrderOps[A] = new OrderOps[A](a) } diff --git a/core/src/main/scala/cats/syntax/partialOrder.scala b/core/src/main/scala/cats/syntax/partialOrder.scala index 21b350f732..3e5d2e0489 100644 --- a/core/src/main/scala/cats/syntax/partialOrder.scala +++ b/core/src/main/scala/cats/syntax/partialOrder.scala @@ -4,7 +4,7 @@ package syntax import cats.macros.Ops trait PartialOrderSyntax extends EqSyntax { - implicit def catsSyntaxPartialOrder[A: PartialOrder](a: A): PartialOrderOps[A] = + implicit final def catsSyntaxPartialOrder[A: PartialOrder](a: A): PartialOrderOps[A] = new PartialOrderOps[A](a) } diff --git a/core/src/main/scala/cats/syntax/reducible.scala b/core/src/main/scala/cats/syntax/reducible.scala index 8d5b89f80c..a513d88415 100644 --- a/core/src/main/scala/cats/syntax/reducible.scala +++ b/core/src/main/scala/cats/syntax/reducible.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait ReducibleSyntax1 { - implicit def catsSyntaxUReducible[FA](fa: FA)(implicit U: Unapply[Reducible, FA]): Reducible.Ops[U.M, U.A] = + implicit final def catsSyntaxUReducible[FA](fa: FA)(implicit U: Unapply[Reducible, FA]): Reducible.Ops[U.M, U.A] = new Reducible.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC @@ -10,10 +10,10 @@ private[syntax] trait ReducibleSyntax1 { } trait ReducibleSyntax extends Reducible.ToReducibleOps with ReducibleSyntax1 { - implicit def catsSyntaxNestedReducible[F[_]: Reducible, G[_], A](fga: F[G[A]]): NestedReducibleOps[F, G, A] = + implicit final def catsSyntaxNestedReducible[F[_]: Reducible, G[_], A](fga: F[G[A]]): NestedReducibleOps[F, G, A] = new NestedReducibleOps[F, G, A](fga) } -final class NestedReducibleOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Reducible[F]) { - def reduceK(implicit G: SemigroupK[G]): G[A] = F.reduceK(fga) +final class NestedReducibleOps[F[_], G[_], A](val fga: F[G[A]]) extends AnyVal { + def reduceK(implicit F: Reducible[F], G: SemigroupK[G]): G[A] = F.reduceK(fga) } diff --git a/core/src/main/scala/cats/syntax/semigroup.scala b/core/src/main/scala/cats/syntax/semigroup.scala index b87cfce3ba..54338b7ff0 100644 --- a/core/src/main/scala/cats/syntax/semigroup.scala +++ b/core/src/main/scala/cats/syntax/semigroup.scala @@ -5,7 +5,7 @@ import cats.macros.Ops trait SemigroupSyntax { // TODO: use simulacrum instances eventually - implicit def catsSyntaxSemigroup[A: Semigroup](a: A): SemigroupOps[A] = + implicit final def catsSyntaxSemigroup[A: Semigroup](a: A): SemigroupOps[A] = new SemigroupOps[A](a) } diff --git a/core/src/main/scala/cats/syntax/semigroupk.scala b/core/src/main/scala/cats/syntax/semigroupk.scala index b9e788404c..39f05b546d 100644 --- a/core/src/main/scala/cats/syntax/semigroupk.scala +++ b/core/src/main/scala/cats/syntax/semigroupk.scala @@ -3,7 +3,7 @@ package syntax private[syntax] trait SemigroupKSyntax1 { // TODO: use simulacrum instances eventually - implicit def catsSyntaxUSemigroup[FA](fa: FA)(implicit U: Unapply[SemigroupK, FA]): SemigroupK.Ops[U.M, U.A] = + implicit final def catsSyntaxUSemigroup[FA](fa: FA)(implicit U: Unapply[SemigroupK, FA]): SemigroupK.Ops[U.M, U.A] = new SemigroupK.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/show.scala b/core/src/main/scala/cats/syntax/show.scala index c66b6b8853..45e94fe15d 100644 --- a/core/src/main/scala/cats/syntax/show.scala +++ b/core/src/main/scala/cats/syntax/show.scala @@ -2,5 +2,5 @@ package cats package syntax trait ShowSyntax extends Show.ToShowOps { - implicit def showInterpolator(sc: StringContext): Show.ShowInterpolator = Show.ShowInterpolator(sc) + implicit final def showInterpolator(sc: StringContext): Show.ShowInterpolator = Show.ShowInterpolator(sc) } diff --git a/core/src/main/scala/cats/syntax/transLift.scala b/core/src/main/scala/cats/syntax/transLift.scala index d985503506..f85ed47835 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -2,7 +2,8 @@ package cats package syntax trait TransLiftSyntax { - implicit def catsSyntaxTransLift[E](ma: E)(implicit U: Unapply[Trivial.PH1, E]): TransLiftOps[U.M, U.A] = new TransLiftOps(U.subst(ma)) + implicit final def catsSyntaxTransLift[E](ma: E)(implicit U: Unapply[Trivial.PH1, E]): TransLiftOps[U.M, U.A] = + new TransLiftOps(U.subst(ma)) } final class TransLiftOps[M0[_], A](val ma: M0[A]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/traverse.scala b/core/src/main/scala/cats/syntax/traverse.scala index d578b4dbcc..f004d7ba98 100644 --- a/core/src/main/scala/cats/syntax/traverse.scala +++ b/core/src/main/scala/cats/syntax/traverse.scala @@ -2,7 +2,7 @@ package cats package syntax private[syntax] trait TraverseSyntax1 { - implicit def catsSyntaxUTraverse[FA](fa: FA)(implicit U: Unapply[Traverse, FA]): Traverse.Ops[U.M, U.A] = + implicit final def catsSyntaxUTraverse[FA](fa: FA)(implicit U: Unapply[Traverse, FA]): Traverse.Ops[U.M, U.A] = new Traverse.Ops[U.M, U.A]{ val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/traverseFilter.scala b/core/src/main/scala/cats/syntax/traverseFilter.scala index 4317db2929..6b24109251 100644 --- a/core/src/main/scala/cats/syntax/traverseFilter.scala +++ b/core/src/main/scala/cats/syntax/traverseFilter.scala @@ -4,7 +4,7 @@ package syntax trait TraverseFilterSyntax extends TraverseFilter.ToTraverseFilterOps with TraverseFilterSyntax1 private[syntax] trait TraverseFilterSyntax1 { - implicit def catsSyntaxUTraverseFilter[FA](fa: FA)(implicit U: Unapply[TraverseFilter, FA]): TraverseFilter.Ops[U.M, U.A] = + implicit final def catsSyntaxUTraverseFilter[FA](fa: FA)(implicit U: Unapply[TraverseFilter, FA]): TraverseFilter.Ops[U.M, U.A] = new TraverseFilter.Ops[U.M, U.A]{ val self = U.subst(fa) val typeClassInstance = U.TC diff --git a/core/src/main/scala/cats/syntax/validated.scala b/core/src/main/scala/cats/syntax/validated.scala index 11e6972a42..3b7ae5c8ad 100644 --- a/core/src/main/scala/cats/syntax/validated.scala +++ b/core/src/main/scala/cats/syntax/validated.scala @@ -4,7 +4,7 @@ package syntax import cats.data.{ Validated, ValidatedNel } trait ValidatedSyntax { - implicit def catsSyntaxValidatedId[A](a: A): ValidatedIdSyntax[A] = new ValidatedIdSyntax(a) + implicit final def catsSyntaxValidatedId[A](a: A): ValidatedIdSyntax[A] = new ValidatedIdSyntax(a) } final class ValidatedIdSyntax[A](val a: A) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/vector.scala b/core/src/main/scala/cats/syntax/vector.scala index 64b8fe2f68..213c221ccf 100644 --- a/core/src/main/scala/cats/syntax/vector.scala +++ b/core/src/main/scala/cats/syntax/vector.scala @@ -3,7 +3,7 @@ package cats.syntax import cats.data.NonEmptyVector trait VectorSyntax { - implicit def catsSyntaxVectors[A](va: Vector[A]): VectorOps[A] = new VectorOps(va) + implicit final def catsSyntaxVectors[A](va: Vector[A]): VectorOps[A] = new VectorOps(va) } final class VectorOps[A](val va: Vector[A]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/writer.scala b/core/src/main/scala/cats/syntax/writer.scala index 828592e6d2..31b36c66dd 100644 --- a/core/src/main/scala/cats/syntax/writer.scala +++ b/core/src/main/scala/cats/syntax/writer.scala @@ -4,7 +4,7 @@ package syntax import cats.data.Writer trait WriterSyntax { - implicit def catsSyntaxWriterId[A](a: A): WriterIdSyntax[A] = new WriterIdSyntax(a) + implicit final def catsSyntaxWriterId[A](a: A): WriterIdSyntax[A] = new WriterIdSyntax(a) } final class WriterIdSyntax[A](val a: A) extends AnyVal { diff --git a/docs/src/main/tut/guidelines.md b/docs/src/main/tut/guidelines.md new file mode 100644 index 0000000000..2a8f1a8d87 --- /dev/null +++ b/docs/src/main/tut/guidelines.md @@ -0,0 +1,27 @@ +--- +layout: page +title: "Guidelines" +section: "guidelines" +position: 7 +--- + +# Guidelines + +All guidelines in cats should have clear justifications. There is no room for tribal wisdom in a simple library. + +## Syntax + +### Composing Implicit Conversions in Traits + +Implicit syntax conversions provided in publicly-exposed traits should be marked final +so that any composition of the traits provides conversions that can all be inlined. + +### Ops Classes + +Ops classes should be marked final and extend AnyVal, to take full advantage of inlining and prevent unnecessary allocations. + +The most notable exception is the case where all of the ops in the class are provided by zero-cost macros anyway, +for example with Simulacrum. + +#### TODO: +Once we drop 2.10 support, AnyVal-extending class constructor parameters can be marked as private. \ No newline at end of file