Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistency for ops classes #1456

Merged
merged 1 commit into from
Apr 13, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
edmundnoble committed Apr 13, 2017
commit 22d15ea46d6512206b0b501f456e4a735478e3ea
14 changes: 8 additions & 6 deletions core/src/main/scala/cats/syntax/applicative.scala
Original file line number Diff line number Diff line change
@@ -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)
}
21 changes: 10 additions & 11 deletions core/src/main/scala/cats/syntax/applicativeError.scala
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/apply.scala
Original file line number Diff line number Diff line change
@@ -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
12 changes: 6 additions & 6 deletions core/src/main/scala/cats/syntax/bitraverse.scala
Original file line number Diff line number Diff line change
@@ -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)
}
12 changes: 7 additions & 5 deletions core/src/main/scala/cats/syntax/cartesian.scala
Original file line number Diff line number Diff line change
@@ -2,27 +2,29 @@ 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
}
}

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
}
}

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 }

}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/coflatMap.scala
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/comonad.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/contravariant.scala
Original file line number Diff line number Diff line change
@@ -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
10 changes: 5 additions & 5 deletions core/src/main/scala/cats/syntax/either.scala
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/eitherK.scala
Original file line number Diff line number Diff line change
@@ -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 {
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/eq.scala
Original file line number Diff line number Diff line change
@@ -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)
}
14 changes: 7 additions & 7 deletions core/src/main/scala/cats/syntax/flatMap.scala
Original file line number Diff line number Diff line change
@@ -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)
}
10 changes: 5 additions & 5 deletions core/src/main/scala/cats/syntax/foldable.scala
Original file line number Diff line number Diff line change
@@ -2,20 +2,20 @@ 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
}
}

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)
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/functor.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/functorFilter.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/group.scala
Original file line number Diff line number Diff line change
@@ -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)
}

2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/invariant.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/ior.scala
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/list.scala
Original file line number Diff line number Diff line change
@@ -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 {
16 changes: 8 additions & 8 deletions core/src/main/scala/cats/syntax/monad.scala
Original file line number Diff line number Diff line change
@@ -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)
}
12 changes: 6 additions & 6 deletions core/src/main/scala/cats/syntax/monadCombine.scala
Original file line number Diff line number Diff line change
@@ -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)
}
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/syntax/monadError.scala
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/monadFilter.scala
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/syntax/monoid.scala
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is untested.

}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/order.scala
Original file line number Diff line number Diff line change
@@ -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)
}

2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/partialOrder.scala
Original file line number Diff line number Diff line change
@@ -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)
}

8 changes: 4 additions & 4 deletions core/src/main/scala/cats/syntax/reducible.scala
Original file line number Diff line number Diff line change
@@ -2,18 +2,18 @@ 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
}
}

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)
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/semigroup.scala
Original file line number Diff line number Diff line change
@@ -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)
}

2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/semigroupk.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/show.scala
Original file line number Diff line number Diff line change
@@ -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)
}
3 changes: 2 additions & 1 deletion core/src/main/scala/cats/syntax/transLift.scala
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/traverse.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/traverseFilter.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/validated.scala
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/vector.scala
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/writer.scala
Original file line number Diff line number Diff line change
@@ -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 {
27 changes: 27 additions & 0 deletions docs/src/main/tut/guidelines.md
Original file line number Diff line number Diff line change
@@ -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.