Skip to content

Commit

Permalink
Merge branch 'master' into parallel-zip-instances
Browse files Browse the repository at this point in the history
  • Loading branch information
LukaJCB authored Nov 14, 2017
2 parents 2ca66c9 + c5f76a2 commit c92a34a
Show file tree
Hide file tree
Showing 16 changed files with 71 additions and 29 deletions.
5 changes: 4 additions & 1 deletion alleycats-core/src/main/scala/alleycats/std/iterable.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package alleycats
package std

import cats.{Eval, Foldable}
import cats.{Eval, Foldable, Monoid}
import export._

@reexports(IterableInstances)
Expand All @@ -16,6 +16,9 @@ object IterableInstances {

override def foldRight[A, B](fa: Iterable[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa, lb)(f)

override def foldMap[A, B](fa: Iterable[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))
}

}
Expand Down
3 changes: 3 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/set.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ object SetInstances {
def foldRight[A, B](fa: Set[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa, lb)(f)

override def foldMap[A, B](fa: Set[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))

def traverse[G[_]: Applicative, A, B](sa: Set[A])(f: A => G[B]): G[Set[B]] = {
val G = Applicative[G]
sa.foldLeft(G.pure(Set.empty[B])) { (buf, a) =>
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ def mimaSettings(moduleName: String) = Seq(
exclude[ReversedMissingMethodProblem]("cats.instances.ParallelInstances.catsStdNonEmptyParallelForZipVector"),
exclude[ReversedMissingMethodProblem]("cats.instances.ParallelInstances.catsStdParallelForZipStream"),
exclude[ReversedMissingMethodProblem]("cats.instances.ParallelInstances.catsStdNonEmptyParallelForZipList"),
exclude[ReversedMissingMethodProblem]("cats.instances.ParallelInstances.catsStdParallelForFailFastFuture")
exclude[ReversedMissingMethodProblem]("cats.instances.ParallelInstances.catsStdParallelForFailFastFuture"),
exclude[DirectMissingMethodProblem]("cats.data.EitherTInstances2.catsDataMonadErrorForEitherT")
)
}
)
Expand Down
40 changes: 20 additions & 20 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -483,26 +483,6 @@ private[data] abstract class EitherTInstances1 extends EitherTInstances2 {
val F0: Traverse[F] = F
}

/** Monad error instance for recovering errors in F instead of
* the underlying Either.
*
* {{{
* scala> import cats.data.EitherT
* scala> import cats.MonadError
* scala> import cats.instances.option._
* scala> val noInt: Option[Either[String, Int]] = None
* scala> val et = EitherT[Option, String, Int](noInt)
* scala> val me = MonadError[EitherT[Option, String, ?], Unit]
* scala> me.recover(et) { case () => 1 }
* res0: cats.data.EitherT[Option,String,Int] = EitherT(Some(Right(1)))
* }}}
*/
implicit def catsDataMonadErrorFForEitherT[F[_], E, L](implicit FE0: MonadError[F, E]): MonadError[EitherT[F, L, ?], E] =
new EitherTMonadErrorF[F, E, L] { implicit val F = FE0 }
}

private[data] abstract class EitherTInstances2 extends EitherTInstances3 {

implicit def catsDataMonadErrorForEitherT[F[_], L](implicit F0: Monad[F]): MonadError[EitherT[F, L, ?], L] =
new EitherTMonadError[F, L] {
implicit val F = F0
Expand All @@ -512,6 +492,26 @@ private[data] abstract class EitherTInstances2 extends EitherTInstances3 {
override def ensureOr[A](fa: EitherT[F, L, A])(error: (A) => L)(predicate: (A) => Boolean): EitherT[F, L, A] =
fa.ensureOr(error)(predicate)(F)
}
}

private[data] abstract class EitherTInstances2 extends EitherTInstances3 {
/** Monad error instance for recovering errors in F instead of
* the underlying Either.
*
* {{{
* scala> import cats.data.EitherT
* scala> import cats.MonadError
* scala> import cats.instances.option._
* scala> val noInt: Option[Either[String, Int]] = None
* scala> val et = EitherT[Option, String, Int](noInt)
* scala> val me = MonadError[EitherT[Option, String, ?], Unit]
* scala> me.recover(et) { case () => 1 }
* res0: cats.data.EitherT[Option,String,Int] = EitherT(Some(Right(1)))
* }}}
*/
implicit def catsDataMonadErrorFForEitherT[F[_], E, L](implicit FE0: MonadError[F, E]): MonadError[EitherT[F, L, ?], E] =
new EitherTMonadErrorF[F, E, L] { implicit val F = FE0 }


implicit def catsDataSemigroupKForEitherT[F[_], L](implicit F0: Monad[F]): SemigroupK[EitherT[F, L, ?]] =
new EitherTSemigroupK[F, L] { implicit val F = F0 }
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ private[data] sealed abstract class NonEmptyListInstances extends NonEmptyListIn
override def foldRight[A, B](fa: NonEmptyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fa.foldRight(lb)(f)

override def foldMap[A, B](fa: NonEmptyList[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.toList.iterator.map(f))

def tailRecM[A, B](a: A)(f: A => NonEmptyList[Either[A, B]]): NonEmptyList[B] = {
val buf = new ListBuffer[B]
@tailrec def go(v: NonEmptyList[Either[A, B]]): Unit = v.head match {
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ private[data] sealed abstract class NonEmptyVectorInstances {
override def foldRight[A, B](fa: NonEmptyVector[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fa.foldRight(lb)(f)

override def foldMap[A, B](fa: NonEmptyVector[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.toVector.iterator.map(f))

override def nonEmptyPartition[A, B, C](fa: NonEmptyVector[A])(f: (A) => Either[B, C]): Ior[NonEmptyList[B], NonEmptyList[C]] = {
import cats.syntax.either._
import cats.syntax.reducible._
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ trait ListInstances extends cats.kernel.instances.ListInstances {
Eval.defer(loop(fa))
}

override def foldMap[A, B](fa: List[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))

def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] =
foldRight[A, G[List[B]]](fa, Always(G.pure(List.empty))){ (a, lglb) =>
G.map2Eval(f(a), lglb)(_ :: _)
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/scala/cats/instances/queue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cats
package instances

import cats.syntax.show._

import scala.annotation.tailrec
import scala.collection.immutable.Queue
import scala.util.Try
Expand Down Expand Up @@ -75,6 +74,9 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances {
Eval.defer(loop(fa))
}

override def foldMap[A, B](fa: Queue[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))

def traverse[G[_], A, B](fa: Queue[A])(f: A => G[B])(implicit G: Applicative[G]): G[Queue[B]] =
foldRight[A, G[Queue[B]]](fa, Always(G.pure(Queue.empty))){ (a, lglb) =>
G.map2Eval(f(a), lglb)(_ +: _)
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/sortedSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ trait SortedSetInstances extends SortedSetInstances1 {
def foldRight[A, B](fa: SortedSet[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa, lb)(f)

override def foldMap[A, B](fa: SortedSet[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))

override def get[A](fa: SortedSet[A])(idx: Long): Option[A] = {
@tailrec
def go(idx: Int, it: Iterator[A]): Option[A] = {
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {
if (s.isEmpty) lb else f(s.head, Eval.defer(foldRight(s.tail, lb)(f)))
}

override def foldMap[A, B](fa: Stream[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))

def traverse[G[_], A, B](fa: Stream[A])(f: A => G[B])(implicit G: Applicative[G]): G[Stream[B]] = {
// We use foldRight to avoid possible stack overflows. Since
// we don't want to return a Eval[_] instance, we call .value
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances {
Eval.defer(loop(0))
}

override def foldMap[A, B](fa: Vector[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))

def tailRecM[A, B](a: A)(fn: A => Vector[Either[A, B]]): Vector[B] = {
val buf = Vector.newBuilder[B]
var state = List(fn(a).iterator)
Expand Down
4 changes: 2 additions & 2 deletions docs/src/main/tut/datatypes/validated.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ object FormValidatorNel extends FormValidatorNel
Let's see what changed here:

1. In this new implementation, we're using a [NonEmptyList](https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/data/NonEmptyList.scala), a data structure that guarantees that at least one element will be present. In case that multiple errors arise, you'll get a list of `DomainValidation`.
2. `ValidatedNel[DomainValidation, A]` is an alias for `Validation[NonEmptyList[DomainValidation], A]`. When you use `ValidatedNel` you're stating that your accumulative structure will be a `NonEmptyList`. With `Validation`, you have the choice about which data structure you want for reporting the errors (more on that soon).
2. `ValidatedNel[DomainValidation, A]` is an alias for `Validated[NonEmptyList[DomainValidation], A]`. When you use `ValidatedNel` you're stating that your accumulative structure will be a `NonEmptyList`. With `Validated`, you have the choice about which data structure you want for reporting the errors (more on that soon).
3. We've declared the type alias `ValidationResult` that conveniently expresses the return type of our validation.
4. `.validNel` and `.invalidNel` combinators lets you _lift_ the success or failure in their respective container (either a `Valid` or `Invalid[NonEmptyList[A]]`).
5. The [applicative](../typeclasses/applicative.html) syntax `(a, b, c, ...).mapN(...)` provides us a way to accumulatively apply the validation functions and yield a product with their successful result or the accumulated errors in the `NonEmptyList`. Then, we transform that product with `mapN` into a valid instance of `RegistrationData`.
Expand Down Expand Up @@ -285,7 +285,7 @@ Sweet success! Now you can take your validation process to the next level!

### A short detour

As previously stated, `ValidatedNel[DomainValidation, A]` is an alias for `Validation[NonEmptyList[DomainValidation], A]`. Typically, you'll see that `Validated` is accompanied by a `NonEmptyList` when it comes to accumulation. The thing here is that you can define your own accumulative data structure and you're not limited to the aforementioned construction.
As previously stated, `ValidatedNel[DomainValidation, A]` is an alias for `Validated[NonEmptyList[DomainValidation], A]`. Typically, you'll see that `Validated` is accompanied by a `NonEmptyList` when it comes to accumulation. The thing here is that you can define your own accumulative data structure and you're not limited to the aforementioned construction.

For doing this, you have to provide a `Semigroup` instance. `NonEmptyList`, by definition has its own `Semigroup`. For those who don't know what a `Semigroup` is, you can find out more [here](../typeclasses/semigroup.html).

Expand Down
1 change: 1 addition & 0 deletions docs/src/main/tut/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ We can even perform more complicated operations, such as a `traverse` of the nes
import cats.data.ValidatedNel
type ErrorsOr[A] = ValidatedNel[String, A]
def even(i: Int): ErrorsOr[Int] = if (i % 2 == 0) i.validNel else s"$i is odd".invalidNel
```

```tut:book
nl.traverse(even)
Expand Down
8 changes: 4 additions & 4 deletions docs/src/main/tut/typeclasses/lawtesting.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ To make things easier, we'll also include the `scalacheck-shapeless` library in

```scala
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-laws" % "1.0.0-MF" % Test,
"org.typelevel" %% "cats-testkit" % "1.0.0-MF"% Test,
"com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.5" % Test
"org.typelevel" %% "cats-laws" % "1.0.0-RC1" % Test,
"org.typelevel" %% "cats-testkit" % "1.0.0-RC1"% Test,
"com.github.alexarchambault" %% "scalacheck-shapeless_1.13" % "1.1.6" % Test
)
```

Expand Down Expand Up @@ -68,7 +68,7 @@ Cats has defined rulesets for all type class laws in `cats.laws.discipline.*`.
So for our example we will want to import `cats.laws.discipline.FunctorTests` and call `checkAll` with it.
Before we do so, however,
we will have to bring our instances into scope as well as the derived `Arbitrary` instances from `scalacheck-shapeless`
(We have defined an Arbitrary instance for `Tree` here, but you won't need it if you import `org.scalacheck.Shapeless._`).
(We have defined an Arbitrary instance for `Tree` here, but you won't need it if you import `org.scalacheck.ScalacheckShapeless._`).



Expand Down
14 changes: 14 additions & 0 deletions tests/src/test/scala/cats/tests/RegressionSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,18 @@ class RegressionSuite extends CatsSuite {
checkAndResetCount(1)
}

test("#2022 EitherT syntax no long works the old way") {
import data._


EitherT.right[String](Option(1)).handleErrorWith((_: String) => EitherT.pure(2))

{
implicit val me = MonadError[EitherT[Option, String, ?], Unit]
EitherT.right[String](Option(1)).handleErrorWith((_: Unit) => EitherT.pure(2))
}


}

}

0 comments on commit c92a34a

Please sign in to comment.