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

Workaround for ambiguous implicits when using MTL type classes, fixes… #1379

Closed
wants to merge 12 commits into from
6 changes: 4 additions & 2 deletions core/src/main/scala/cats/Alternative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package cats

import simulacrum.typeclass

@typeclass trait Alternative[F[_]] extends Applicative[F] with MonoidK[F] { self =>
override def compose[G[_]: Applicative]: Alternative[λ[α => F[G[α]]]] =
@typeclass trait Alternative[F[_]] extends MonoidK[F] { self =>
def applicativeInstance: Applicative[F]

def compose[G[_]: Applicative]: Alternative[λ[α => F[G[α]]]] =
new ComposedAlternative[F, G] {
val F = self
val G = Applicative[G]
Expand Down
18 changes: 10 additions & 8 deletions core/src/main/scala/cats/ApplicativeError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import scala.util.control.NonFatal
*
* This type class allows one to abstract over error-handling applicatives.
*/
trait ApplicativeError[F[_], E] extends Applicative[F] {
trait ApplicativeError[F[_], E] extends Serializable {
def applicativeInstance: Applicative[F]

/**
* Lift an error into the `F` context.
*/
Expand All @@ -34,7 +36,7 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
*
* @see [[recover]] to only recover from certain errors.
*/
def handleError[A](fa: F[A])(f: E => A): F[A] = handleErrorWith(fa)(f andThen pure)
def handleError[A](fa: F[A])(f: E => A): F[A] = handleErrorWith(fa)(f andThen applicativeInstance.pure)

/**
* Handle errors by turning them into [[scala.util.Either]] values.
Expand All @@ -44,8 +46,8 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
* All non-fatal errors should be handled by this method.
*/
def attempt[A](fa: F[A]): F[Either[E, A]] = handleErrorWith(
map(fa)(Right(_): Either[E, A])
)(e => pure(Left(e)))
applicativeInstance.map(fa)(Right(_): Either[E, A])
)(e => applicativeInstance.pure(Left(e)))

/**
* Similar to [[attempt]], but wraps the result in a [[cats.data.EitherT]] for
Expand All @@ -63,7 +65,7 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
*/
def recover[A](fa: F[A])(pf: PartialFunction[E, A]): F[A] =
handleErrorWith(fa)(e =>
(pf andThen pure) applyOrElse(e, raiseError))
(pf andThen applicativeInstance.pure) applyOrElse(e, raiseError))

/**
* Recover from certain errors by mapping them to an `F[A]` value.
Expand All @@ -81,7 +83,7 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
* and raise.
*/
def catchNonFatal[A](a: => A)(implicit ev: Throwable <:< E): F[A] =
try pure(a)
try applicativeInstance.pure(a)
catch {
case NonFatal(e) => raiseError(e)
}
Expand All @@ -91,7 +93,7 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
* and raise
*/
def catchNonFatalEval[A](a: Eval[A])(implicit ev: Throwable <:< E): F[A] =
try pure(a.value)
try applicativeInstance.pure(a.value)
catch {
case NonFatal(e) => raiseError(e)
}
Expand All @@ -101,7 +103,7 @@ trait ApplicativeError[F[_], E] extends Applicative[F] {
*/
def fromTry[A](t: Try[A])(implicit ev: Throwable <:< E): F[A] =
t match {
case Success(a) => pure(a)
case Success(a) => applicativeInstance.pure(a)
case Failure(e) => raiseError(e)
}
}
Expand Down
22 changes: 19 additions & 3 deletions core/src/main/scala/cats/Composed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,14 @@ private[cats] trait ComposedMonoidK[F[_], G[_]] extends MonoidK[λ[α => F[G[α]
override def empty[A]: F[G[A]] = F.empty
}

private[cats] trait ComposedAlternative[F[_], G[_]] extends Alternative[λ[α => F[G[α]]]] with ComposedApplicative[F, G] with ComposedMonoidK[F, G] { outer =>
private[cats] trait ComposedAlternative[F[_], G[_]] extends Alternative[λ[α => F[G[α]]]] with ComposedMonoidK[F, G] { outer =>
def F: Alternative[F]
def G: Applicative[G]

val applicativeInstance: Applicative[λ[α => F[G[α]]]] = new ComposedApplicative[F, G] {
def F = outer.F.applicativeInstance
def G = outer.G
}
}

private[cats] trait ComposedFoldable[F[_], G[_]] extends Foldable[λ[α => F[G[α]]]] { outer =>
Expand All @@ -71,18 +77,28 @@ private[cats] trait ComposedTraverse[F[_], G[_]] extends Traverse[λ[α => F[G[
F.traverse(fga)(ga => G.traverse(ga)(f))
}

private[cats] trait ComposedTraverseFilter[F[_], G[_]] extends TraverseFilter[λ[α => F[G[α]]]] with ComposedTraverse[F, G] {
private[cats] trait ComposedTraverseFilter[F[_], G[_]] extends TraverseFilter[λ[α => F[G[α]]]] { outer =>
def F: Traverse[F]
def G: TraverseFilter[G]

val traverseInstance: Traverse[λ[α => F[G[α]]]] = new ComposedTraverse[F, G] {
def F = outer.F
def G = outer.G.traverseInstance
}

override def traverseFilter[H[_]: Applicative, A, B](fga: F[G[A]])(f: A => H[Option[B]]): H[F[G[B]]] =
F.traverse[H, G[A], G[B]](fga)(ga => G.traverseFilter(ga)(f))
}

private[cats] trait ComposedFunctorFilter[F[_], G[_]] extends FunctorFilter[λ[α => F[G[α]]]] with ComposedFunctor[F, G] {
private[cats] trait ComposedFunctorFilter[F[_], G[_]] extends FunctorFilter[λ[α => F[G[α]]]] { outer =>
def F: Functor[F]
def G: FunctorFilter[G]

val functorInstance: Functor[λ[α => F[G[α]]]] = new ComposedFunctor[F, G] {
def F = outer.F
def G = outer.G.functorInstance
}

override def mapFilter[A, B](fga: F[G[A]])(f: A => Option[B]): F[G[B]] =
F.map(fga)(G.mapFilter(_)(f))
}
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/scala/cats/FunctorFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package cats

import simulacrum.typeclass

@typeclass trait FunctorFilter[F[_]] extends Functor[F] {
@typeclass trait FunctorFilter[F[_]] extends Serializable {
def functorInstance: Functor[F]

/**
* A combined [[map]] and [[filter]]. Filtering is handled via `Option`
* A combined `map` and [[filter]]. Filtering is handled via `Option`
* instead of `Boolean` such that the output type `B` can be different than
* the input type `A`.
*
Expand Down
10 changes: 6 additions & 4 deletions core/src/main/scala/cats/MonadCombine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import simulacrum.typeclass
* The combination of a Monad with a MonoidK
*/
@typeclass trait MonadCombine[F[_]] extends MonadFilter[F] with Alternative[F] {
def monadInstance: Monad[F]
def applicativeInstance: Applicative[F] = monadInstance

/**
* Fold over the inner structure to combine all of the values with
Expand All @@ -15,14 +17,14 @@ import simulacrum.typeclass
* we collect all the Right values, etc.
*/
def unite[G[_], A](fga: F[G[A]])(implicit G: Foldable[G]): F[A] =
flatMap(fga) { ga =>
G.foldLeft(ga, empty[A])((acc, a) => combineK(acc, pure(a)))
monadInstance.flatMap(fga) { ga =>
G.foldLeft(ga, empty[A])((acc, a) => combineK(acc, applicativeInstance.pure(a)))
}

/** Separate the inner foldable values into the "lefts" and "rights" */
def separate[G[_, _], A, B](fgab: F[G[A, B]])(implicit G: Bifoldable[G]): (F[A], F[B]) = {
val as = flatMap(fgab)(gab => G.bifoldMap(gab)(pure, _ => empty[A])(algebra[A]))
val bs = flatMap(fgab)(gab => G.bifoldMap(gab)(_ => empty[B], pure)(algebra[B]))
val as = monadInstance.flatMap(fgab)(gab => G.bifoldMap(gab)(applicativeInstance.pure, _ => empty[A])(algebra[A]))
val bs = monadInstance.flatMap(fgab)(gab => G.bifoldMap(gab)(_ => empty[B], applicativeInstance.pure)(algebra[B]))
(as, bs)
}
}
6 changes: 4 additions & 2 deletions core/src/main/scala/cats/MonadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ package cats
*
* This type class allows one to abstract over error-handling monads.
*/
trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] {
trait MonadError[F[_], E] extends ApplicativeError[F, E] {
def monadInstance: Monad[F]
def applicativeInstance: Applicative[F] = monadInstance

/**
* Turns a successful value into an error if it does not satisfy a given predicate.
*/
def ensure[A](fa: F[A])(error: => E)(predicate: A => Boolean): F[A] =
flatMap(fa)(a => if (predicate(a)) pure(a) else raiseError(error))
monadInstance.flatMap(fa)(a => if (predicate(a)) applicativeInstance.pure(a) else raiseError(error))

}

Expand Down
6 changes: 4 additions & 2 deletions core/src/main/scala/cats/MonadFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import simulacrum.typeclass
* us since it allows us to add a `filter` method to a Monad, which is
* used when pattern matching or using guards in for comprehensions.
*/
@typeclass trait MonadFilter[F[_]] extends Monad[F] with FunctorFilter[F] {
@typeclass trait MonadFilter[F[_]] extends FunctorFilter[F] {
def monadInstance: Monad[F]
def functorInstance: Functor[F] = monadInstance

def empty[A]: F[A]

override def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B] =
flatMap(fa)(a => f(a).fold(empty[B])(pure))
monadInstance.flatMap(fa)(a => f(a).fold(empty[B])(monadInstance.pure))
}
4 changes: 3 additions & 1 deletion core/src/main/scala/cats/MonadReader.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cats

/** A monad that has the ability to read from an environment. */
trait MonadReader[F[_], R] extends Monad[F] {
trait MonadReader[F[_], R] extends Serializable {
def monadInstance: Monad[F]

/** Get the environment */
def ask: F[R]

Expand Down
8 changes: 5 additions & 3 deletions core/src/main/scala/cats/MonadState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ package cats
* } yield r
* }}}
*/
trait MonadState[F[_], S] extends Monad[F] {
trait MonadState[F[_], S] extends Serializable {
def monadInstance: Monad[F]

def get: F[S]

def set(s: S): F[Unit]

def modify(f: S => S): F[Unit] = flatMap(get)(s => set(f(s)))
def modify(f: S => S): F[Unit] = monadInstance.flatMap(get)(s => set(f(s)))

def inspect[A](f: S => A): F[A] = map(get)(f)
def inspect[A](f: S => A): F[A] = monadInstance.map(get)(f)
}

object MonadState {
Expand Down
8 changes: 5 additions & 3 deletions core/src/main/scala/cats/MonadWriter.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cats

/** A monad that support monoidal accumulation (e.g. logging List[String]) */
trait MonadWriter[F[_], W] extends Monad[F] {
trait MonadWriter[F[_], W] extends Serializable {
def monadInstance: Monad[F]

/** Lift a writer action into the effect */
def writer[A](aw: (W, A)): F[A]

Expand All @@ -16,11 +18,11 @@ trait MonadWriter[F[_], W] extends Monad[F] {

/** Pair the value with an inspection of the accumulator */
def listens[A, B](fa: F[A])(f: W => B): F[(B, A)] =
map(listen(fa)) { case (w, a) => (f(w), a) }
monadInstance.map(listen(fa)) { case (w, a) => (f(w), a) }

/** Modify the accumulator */
def censor[A](fa: F[A])(f: W => W): F[A] =
flatMap(listen(fa)) { case (w, a) => writer((f(w), a)) }
monadInstance.flatMap(listen(fa)) { case (w, a) => writer((f(w), a)) }
}

object MonadWriter {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/SemigroupK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import simulacrum.typeclass
* The combination operation just depends on the structure of F,
* but not the structure of A.
*/
@typeclass trait SemigroupK[F[_]] { self =>
@typeclass trait SemigroupK[F[_]] extends Serializable { self =>

/**
* Combine two F[A] values.
Expand Down
11 changes: 5 additions & 6 deletions core/src/main/scala/cats/TraverseFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import simulacrum.typeclass

/**
* `TraverseFilter`, also known as `Witherable`, represents list-like structures
* that can essentially have a [[traverse]] and a [[filter]] applied as a single
* that can essentially have a `traverse` and a [[filter]] applied as a single
* combined operation ([[traverseFilter]]).
*
* Must obey the laws defined in cats.laws.TraverseFilterLaws.
*
* Based on Haskell's [[https://hackage.haskell.org/package/witherable-0.1.3.3/docs/Data-Witherable.html Data.Witherable]]
*/
@typeclass trait TraverseFilter[F[_]] extends Traverse[F] with FunctorFilter[F] { self =>
@typeclass trait TraverseFilter[F[_]] extends FunctorFilter[F] { self =>
def traverseInstance : Traverse[F]
def functorInstance: Functor[F] = traverseInstance

/**
* A combined [[traverse]] and [[filter]]. Filtering is handled via `Option`
* A combined `traverse` and [[filter]]. Filtering is handled via `Option`
* instead of `Boolean` such that the output type `B` can be different than
* the input type `A`.
*
Expand Down Expand Up @@ -59,7 +61,4 @@ import simulacrum.typeclass

override def filter[A](fa: F[A])(f: A => Boolean): F[A] =
filterA[Id, A](fa)(f)

override def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[F[B]] =
traverseFilter(fa)(a => G.map(f(a))(Some(_)))
}
19 changes: 11 additions & 8 deletions core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,20 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 {
fa.retag[B]
}

implicit def catsDataTraverseFilterForConst[C]: TraverseFilter[Const[C, ?]] = new TraverseFilter[Const[C, ?]] {
def traverseFilter[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[Option[B]]): G[Const[C, B]] =
fa.traverseFilter(f)
implicit def catsDataTraverseFilterForConst[C]: TraverseFilter[Const[C, ?]] with Traverse[Const[C, ?]] =
new TraverseFilter[Const[C, ?]] with Traverse[Const[C, ?]] {
val traverseInstance = this

def foldLeft[A, B](fa: Const[C, A], b: B)(f: (B, A) => B): B = b
def traverseFilter[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[Option[B]]): G[Const[C, B]] =
fa.traverseFilter(f)

def foldRight[A, B](fa: Const[C, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb
def foldLeft[A, B](fa: Const[C, A], b: B)(f: (B, A) => B): B = b

override def traverse[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[B]): G[Const[C, B]] =
fa.traverse(f)
}
def foldRight[A, B](fa: Const[C, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb

override def traverse[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[B]): G[Const[C, B]] =
fa.traverse(f)
}

implicit def catsDataMonoidForConst[A: Monoid, B]: Monoid[Const[A, B]] = new Monoid[Const[A, B]]{
def empty: Const[A, B] =
Expand Down
Loading