From 957317b8ea9d040a2e905fcab3ec2d1c5e2f5c35 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 --- .../main/scala/cats/syntax/applicative.scala | 2 +- .../scala/cats/syntax/applicativeError.scala | 20 +++++++-------- core/src/main/scala/cats/syntax/apply.scala | 2 +- .../main/scala/cats/syntax/bitraverse.scala | 12 ++++----- .../main/scala/cats/syntax/cartesian.scala | 10 ++++---- .../main/scala/cats/syntax/coflatMap.scala | 2 +- core/src/main/scala/cats/syntax/comonad.scala | 2 +- .../scala/cats/syntax/contravariant.scala | 2 +- .../main/scala/cats/syntax/coproduct.scala | 2 +- core/src/main/scala/cats/syntax/either.scala | 8 +++--- 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/list.scala | 2 +- .../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 +- .../main/scala/cats/syntax/transLift.scala | 2 +- .../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 | 25 +++++++++++++++++++ 34 files changed, 101 insertions(+), 75 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 16dc9c55e47..978dc63a329 100644 --- a/core/src/main/scala/cats/syntax/applicative.scala +++ b/core/src/main/scala/cats/syntax/applicative.scala @@ -2,7 +2,7 @@ package cats package syntax trait ApplicativeSyntax { - implicit def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) + implicit final def catsSyntaxApplicativeId[A](a: A): ApplicativeIdOps[A] = new ApplicativeIdOps[A](a) } final class ApplicativeIdOps[A](val a: A) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/applicativeError.scala b/core/src/main/scala/cats/syntax/applicativeError.scala index 10dc9a60795..4454edf3742 100644 --- a/core/src/main/scala/cats/syntax/applicativeError.scala +++ b/core/src/main/scala/cats/syntax/applicativeError.scala @@ -4,35 +4,35 @@ 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 8d9be51c9ed..2671087bf13 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 e34372ade3d..d24049def74 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 64ce35084a9..a639cae0732 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,11 @@ 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 (a, 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, b) => a } } diff --git a/core/src/main/scala/cats/syntax/coflatMap.scala b/core/src/main/scala/cats/syntax/coflatMap.scala index e63612cc782..e394677da42 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 95dcdeb47a6..cbeaf7bb0fe 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 9cabd49807b..7eda421d6be 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/coproduct.scala b/core/src/main/scala/cats/syntax/coproduct.scala index af6b45760ae..97ddf6f3c0a 100644 --- a/core/src/main/scala/cats/syntax/coproduct.scala +++ b/core/src/main/scala/cats/syntax/coproduct.scala @@ -4,7 +4,7 @@ package syntax import cats.data.Coproduct trait CoproductSyntax { - implicit def catsSyntaxCoproduct[F[_], A](a: F[A]): CoproductOps[F, A] = new CoproductOps(a) + implicit final def catsSyntaxCoproduct[F[_], A](a: F[A]): CoproductOps[F, A] = new CoproductOps(a) } final class CoproductOps[F[_], A](val fa: F[A]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/either.scala b/core/src/main/scala/cats/syntax/either.scala index c333a00e4a3..a7bb413f101 100644 --- a/core/src/main/scala/cats/syntax/either.scala +++ b/core/src/main/scala/cats/syntax/either.scala @@ -6,13 +6,13 @@ 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) } final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal { diff --git a/core/src/main/scala/cats/syntax/eq.scala b/core/src/main/scala/cats/syntax/eq.scala index 87250f86430..3737e95d89c 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 1723dddf835..596019e8605 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 67589a6f58d..9a765f08f07 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 ba8be22cbd1..478299c92aa 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 eb7757bee58..dfefedcce9a 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 dca6c12780a..01d29cf3ae6 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 6271cc8077e..3f425c002c3 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/list.scala b/core/src/main/scala/cats/syntax/list.scala index 88defb39da7..605ba71b4ee 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/monadCombine.scala b/core/src/main/scala/cats/syntax/monadCombine.scala index 40e53f24495..f970cf6b9b4 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 8d0bf677a17..8ea1d590d1e 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 21c98a869b2..8413fb64f59 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 0858424a04d..dcbf0fd680f 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 e5c14bfe4d2..36c33386a30 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 21b350f732e..3e5d2e0489a 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 8d5b89f80cd..01e4f776b28 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](fga: F[G[A]]) { + 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 b87cfce3ba2..54338b7ff0f 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 b9e788404c0..39f05b546dd 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/transLift.scala b/core/src/main/scala/cats/syntax/transLift.scala index d9855035060..9e5ef2418fe 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -2,7 +2,7 @@ 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 d578b4dbcc5..f004d7ba98e 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 4317db2929f..6b241092514 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 11e6972a42d..3b7ae5c8ade 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 64b8fe2f685..213c221ccff 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 828592e6d2a..31b36c66dda 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 00000000000..2c2dcf56133 --- /dev/null +++ b/docs/src/main/tut/guidelines.md @@ -0,0 +1,25 @@ +--- +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. +One notable exception is the case where all of the ops in the class are provided by zero-cost macros anyway. + +#### TODO: +Once we drop 2.10 support, AnyVal-extending class constructor parameters can be marked as private. \ No newline at end of file