Skip to content

Commit

Permalink
Merge branch 'master' into fix/3262
Browse files Browse the repository at this point in the history
  • Loading branch information
travisbrown committed Mar 2, 2020
2 parents 672ee12 + a8e2c21 commit 2d87f95
Show file tree
Hide file tree
Showing 45 changed files with 345 additions and 103 deletions.
3 changes: 2 additions & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=2.3.2
version=2.4.2
align.openParenCallSite = true
align.openParenDefnSite = true
maxColumn = 120
Expand All @@ -7,3 +7,4 @@ assumeStandardLibraryStripMargin = true
danglingParentheses = true
rewrite.rules = [AvoidInfix, SortImports, RedundantBraces, RedundantParens, SortModifiers]
docstrings = JavaDoc
newlines.afterCurlyLambda = preserve
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## Version 2.1.1

_2020 February 25_

This release introduces support for Scala.js 1.0.0. There are no changes since 2.1.0 for the
Scala.js 0.6 or JVM artifacts. This is likely to be the last Cats release supporting Scala.js 0.6,
and we encourage Scala.js users to upgrade to 1.0.0 as soon as possible.

## Version 2.1.0

_2019 December 18_
Expand Down
8 changes: 4 additions & 4 deletions alleycats-core/src/main/scala/alleycats/std/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ trait MapInstances {
val gba: Eval[G[Map[K, B]]] = Always(G.pure(Map.empty))
val gbb = Foldable
.iterateRight(fa, gba) { (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (b, buf) =>
G.map2Eval(f(kv._2), lbuf) { (b, buf) =>
buf + (kv._1 -> b)
})
}
}
.value
G.map(gbb)(_.toMap)
Expand Down Expand Up @@ -67,9 +67,9 @@ trait MapInstances {
val gba: Eval[G[Map[K, B]]] = Always(G.pure(Map.empty))
Foldable
.iterateRight(fa, gba) { (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (ob, buf) =>
G.map2Eval(f(kv._2), lbuf) { (ob, buf) =>
ob.fold(buf)(b => buf + (kv._1 -> b))
})
}
}
.value
}
Expand Down
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ isTravisBuild in Global := sys.env.get("TRAVIS").isDefined

val scalaCheckVersion = "1.14.3"

val scalatestplusScalaCheckVersion = "3.1.0.1"
val scalatestplusScalaCheckVersion = "3.1.1.1"

val disciplineVersion = "1.0.2"

val disciplineScalatestVersion = "1.0.0"
val disciplineScalatestVersion = "1.0.1"

val kindProjectorVersion = "0.11.0"

Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala-2.13+/cats/instances/arraySeq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ private[cats] object ArraySeqInstances {
}

override def map2[A, B, Z](fa: ArraySeq[A], fb: ArraySeq[B])(f: (A, B) => Z): ArraySeq[Z] =
if (fb.isEmpty) ArraySeq.empty // do O(1) work if fb is empty
if (fb.isEmpty) ArraySeq.untagged.empty // do O(1) work if fb is empty
else fa.flatMap(a => fb.map(b => f(a, b))) // already O(1) if fa is empty

override def map2Eval[A, B, Z](fa: ArraySeq[A], fb: Eval[ArraySeq[B]])(f: (A, B) => Z): Eval[ArraySeq[Z]] =
if (fa.isEmpty) Eval.now(ArraySeq.empty) // no need to evaluate fb
if (fa.isEmpty) Eval.now(ArraySeq.untagged.empty) // no need to evaluate fb
else fb.map(fb => map2(fa, fb)(f))

def foldLeft[A, B](fa: ArraySeq[A], b: B)(f: (B, A) => B): B =
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,10 @@ import Foldable.sentinel
* }}}
*/
def intercalate[A](fa: F[A], a: A)(implicit A: Monoid[A]): A =
A.combineAll(intersperseList(toList(fa), a))
combineAllOption(fa)(A.intercalate(a)) match {
case None => A.empty
case Some(a) => a
}

protected def intersperseList[A](xs: List[A], x: A): List[A] = {
val bld = List.newBuilder[A]
Expand Down
6 changes: 1 addition & 5 deletions core/src/main/scala/cats/Reducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,7 @@ import simulacrum.{noop, typeclass}
* }}}
*/
def nonEmptyIntercalate[A](fa: F[A], a: A)(implicit A: Semigroup[A]): A =
toNonEmptyList(fa) match {
case NonEmptyList(hd, Nil) => hd
case NonEmptyList(hd, tl) =>
Reducible[NonEmptyList].reduce(NonEmptyList(hd, a :: intersperseList(tl, a)))
}
reduce(fa)(A.intercalate(a))

/**
* Partition this Reducible by a separating function `A => Either[B, C]`
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/data/Cokleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,14 @@ private[data] class CokleisliMonad[F[_], A] extends Monad[Cokleisli[F, A, *]] {
fa.map(f)

def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, Either[B, C]]): Cokleisli[F, A, C] =
Cokleisli({ (fa: F[A]) =>
Cokleisli { (fa: F[A]) =>
@tailrec
def loop(c: Cokleisli[F, A, Either[B, C]]): C = c.run(fa) match {
case Right(c) => c
case Left(bb) => loop(fn(bb))
}
loop(fn(b))
})
}

}

Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,12 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {
}
})

def semiflatTap[C](f: B => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
semiflatMap(b => F.as(f(b), b))

def leftSemiflatTap[C](f: A => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
leftSemiflatMap(a => F.as(f(a), a))

def compare(that: EitherT[F, A, B])(implicit o: Order[F[Either[A, B]]]): Int =
o.compare(value, that.value)

Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/data/Func.scala
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ sealed abstract class AppFunc[F[_], A, B] extends Func[F, A, B] { self =>

def compose[G[_], C](g: AppFunc[G, C, A]): AppFunc[Nested[G, F, *], C, B] = {
implicit val gfApplicative: Applicative[Nested[G, F, *]] = Nested.catsDataApplicativeForNested[G, F](g.F, F)
Func.appFunc[Nested[G, F, *], C, B]({ (c: C) =>
Func.appFunc[Nested[G, F, *], C, B] { (c: C) =>
Nested(g.F.map(g.run(c))(self.run))
})
}
}

def andThen[G[_], C](g: AppFunc[G, B, C]): AppFunc[Nested[F, G, *], A, C] =
Expand Down
12 changes: 6 additions & 6 deletions core/src/main/scala/cats/data/Ior.scala
Original file line number Diff line number Diff line change
Expand Up @@ -507,14 +507,14 @@ sealed abstract class Ior[+A, +B] extends Product with Serializable {
* scala> import cats.data.Ior
* scala> import cats.implicits._
*
* scala> "abc".leftIor[Int].bimap(_.length, _ * 2)
* res0: Ior[Int, Int] = Left(3)
* scala> "abc".leftIor[Int].map(_ * 2)
* res0: Ior[String, Int] = Left(abc)
*
* scala> 123.rightIor[String].bimap(_.length, _ * 2)
* res1: Ior[Int, Int] = Right(246)
* scala> 123.rightIor[String].map(_ * 2)
* res1: Ior[String, Int] = Right(246)
*
* scala> Ior.Both("abc", 123).bimap(_.length, _ * 2)
* res2: Ior[Int, Int] = Both(3,246)
* scala> Ior.Both("abc", 123).map(_ * 2)
* res2: Ior[String, Int] = Both(abc,246)
* }}}
*/
final def map[D](f: B => D): A Ior D = bimap(identity, f)
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -598,9 +598,9 @@ private[data] trait KleisliFlatMap[F[_], A] extends FlatMap[Kleisli[F, A, *]] wi
fa.flatMap(f)

def tailRecM[B, C](b: B)(f: B => Kleisli[F, A, Either[B, C]]): Kleisli[F, A, C] =
Kleisli[F, A, C]({ a =>
F.tailRecM(b) { f(_).run(a) }
})
Kleisli[F, A, C] { a =>
F.tailRecM(b)(f(_).run(a))
}
}

private[data] trait KleisliApplicative[F[_], A] extends Applicative[Kleisli[F, A, *]] with KleisliApply[F, A] {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/WriterT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) {
* }}}
*/
def mapBoth[M, U](f: (L, V) => (M, U))(implicit functorF: Functor[F]): WriterT[F, M, U] =
WriterT { functorF.map(run)(f.tupled) }
WriterT(functorF.map(run)(f.tupled))

/**
* Example:
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/instances/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ trait MapInstances extends cats.kernel.instances.MapInstances {
val gba: Eval[G[Map[K, B]]] = Always(G.pure(Map.empty))
val gbb = Foldable
.iterateRight(fa, gba) { (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (b, buf) =>
G.map2Eval(f(kv._2), lbuf) { (b, buf) =>
buf + (kv._1 -> b)
})
}
}
.value
G.map(gbb)(_.toMap)
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/scala/cats/instances/sortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ trait SortedMapInstances extends SortedMapInstances2 {
val gba: Eval[G[SortedMap[K, B]]] = Always(G.pure(SortedMap.empty(Order[K].toOrdering)))
Foldable
.iterateRight(fa, gba) { (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (b, buf) =>
G.map2Eval(f(kv._2), lbuf) { (b, buf) =>
buf + (kv._1 -> b)
})
}
}
.value
}
Expand Down Expand Up @@ -186,9 +186,9 @@ private[instances] trait SortedMapInstancesBinCompat0 {
val gba: Eval[G[SortedMap[K, B]]] = Always(G.pure(SortedMap.empty))
Foldable
.iterateRight(fa, gba) { (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (ob, buf) =>
G.map2Eval(f(kv._2), lbuf) { (ob, buf) =>
ob.fold(buf)(b => buf + (kv._1 -> b))
})
}
}
.value
}
Expand Down
30 changes: 29 additions & 1 deletion core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
import scala.annotation.tailrec

/**
* Symbolic aliases for various types are defined here.
* The `cats` root package contains all the trait signatures of most Scala type classes.
*
* Cats type classes are implemented using the approach from the
* [[https://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf Type classes as objects and implicits]] article.
*
* For each type class, `cats` provides three pieces:
* - Its '''signature''': a trait that is polymorphic on a type parameter.
* Type class traits inherit from other type classes to indicate that any implementation of the lower type class (e.g. `Applicative`)
* can also serve as an instance for the higuer type class (e.g. `Functor`).
* - Type class ''''instances''', which are classes and objects that implement one or more type class signatures for some specific types.
* Type class instances for several data types from the Java or Scala standard libraries are declared in the subpackage `cats.instances`.
* - '''Syntax extensions''', each of which provides the methods of the type class defines as extension methods
* (which in Scala 2 are encoded as implicit classes) for values of any type `F`; given that an instance of the type class
* for the receiver type (`this`) is in the implicit scope.
* Symtax extensions are declared in the `cats.syntax` package.
* - A set of '''laws''', that are also generic on the type of the class, and are only defined on the operations of the type class.
* The purpose of these laws is to declare some algebraic relations (equations) between Scala expressions involving the operations
* of the type class, and test (but not verify) that implemented instances satisfy those equations.
* Laws are defined in the `cats-laws` package.
*
* Although most of cats type classes are declared in this package, some are declared in other packages:
* - type classes that operate on base types (kind `*`), and their implementations for standard library types,
* are contained in `cats.kernel`, which is a different SBT project. However, they are re-exported from this package.
* - type classes of kind `F[_, _]`, such as [[cats.arrow.Profunctor]]" or [[cats.arrow.Arrow]], which are relevant for
* Functional Reactive Programming or optics, are declared in the `cats.arrow` package.
* - Also, those type classes that abstract over (pure or impure) functional runtime effects are declared
* in the [[https://typelevel.org/cats-effect/ cats-effect library]].
* - Some type classes for which no laws can be provided are left out of the main road, in a small and dirty alley.
* These are the `alleycats`.
*/
package object cats {

Expand Down
4 changes: 3 additions & 1 deletion core/src/main/scala/cats/syntax/monadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,7 @@ final class MonadErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal {

final class MonadErrorRethrowOps[F[_], E, A](private val fea: F[Either[E, A]]) extends AnyVal {
def rethrow(implicit F: MonadError[F, _ >: E]): F[A] =
F.flatMap(fea)(_.fold(F.raiseError, F.pure)) // dup from the type class impl, due to https://github.com/scala/bug/issues/11562. Once fixed should be able to replace with `F.rethrow(fea)`
F.flatMap(fea)(
_.fold(F.raiseError, F.pure)
) // dup from the type class impl, due to https://github.com/scala/bug/issues/11562. Once fixed should be able to replace with `F.rethrow(fea)`
}
13 changes: 13 additions & 0 deletions free/src/main/scala/cats/free/FreeT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ sealed abstract class FreeT[S[_], M[_], A] extends Product with Serializable {
Suspend(mn(m))
}

/**
* Modify the context `M` using the transformation `mn`, flattening the free
* suspensions into the outer.
*/
def flatMapK[N[_]](mn: M ~> FreeT[S, N, *])(implicit S: Functor[S], N: Monad[N]): FreeT[S, N, A] = {
def loop(ftft: FreeT[S, FreeT[S, N, *], A]): FreeT[S, N, A] =
ftft.resume.flatMap { e =>
e.fold(sft => FreeT.liftF[S, N, FreeT[S, FreeT[S, N, *], A]](sft).flatMap(loop(_)), a => FreeT.pure(a))
}

loop(mapK(mn))
}

/** Binds the given continuation to the result of this computation. */
final def flatMap[B](f: A => FreeT[S, M, B]): FreeT[S, M, B] =
FlatMapped(this, f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,41 @@ trait SemigroupLaws[A] {
def combineAllOption(xs: Vector[A]): IsEq[Option[A]] =
S.combineAllOption(xs) <-> xs.reduceOption(S.combine)

def reverseReverses(a: A, b: A): IsEq[A] =
S.combine(a, b) <-> S.reverse.combine(b, a)

def reverseRepeat1(a: A): IsEq[A] = {
val rev = S.reverse
rev.combineN(a, 1) <-> a
}

def reverseRepeat2(a: A): IsEq[A] = {
val rev = S.reverse
rev.combineN(a, 2) <-> rev.combine(a, a)
}

def reverseCombineAllOption(xs: Vector[A]): IsEq[Option[A]] = {
val rev = S.reverse
rev.combineAllOption(xs) <-> xs.reduceOption(rev.combine)
}

def intercalateIntercalates(a: A, m: A, b: A): IsEq[A] =
S.combine(a, S.combine(m, b)) <-> S.intercalate(m).combine(a, b)

def intercalateRepeat1(m: A, a: A): IsEq[A] = {
val withMiddle = S.intercalate(m)
withMiddle.combineN(a, 1) <-> a
}

def intercalateRepeat2(m: A, a: A): IsEq[A] = {
val withMiddle = S.intercalate(m)
withMiddle.combineN(a, 2) <-> withMiddle.combine(a, a)
}

def intercalateCombineAllOption(m: A, xs: Vector[A]): IsEq[Option[A]] = {
val withMiddle = S.intercalate(m)
withMiddle.combineAllOption(xs) <-> xs.reduceOption(withMiddle.combine)
}
}

object SemigroupLaws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ trait SemigroupTests[A] extends Laws {
"associative" -> forAll(laws.semigroupAssociative _),
"repeat1" -> forAll(laws.repeat1 _),
"repeat2" -> forAll(laws.repeat2 _),
"combineAllOption" -> forAll(laws.combineAllOption _)
"combineAllOption" -> forAll(laws.combineAllOption _),
"reverseReverses" -> forAll(laws.reverseReverses _),
"reverseRepeat1" -> forAll(laws.reverseRepeat1 _),
"reverseRepeat2" -> forAll(laws.reverseRepeat2 _),
"reverseCombineAllOption" -> forAll(laws.reverseCombineAllOption _),
"intercalateIntercalates" -> forAll(laws.intercalateIntercalates _),
"intercalateRepeat1" -> forAll(laws.intercalateRepeat1 _),
"intercalateRepeat2" -> forAll(laws.intercalateRepeat2 _),
"intercalateCombineAllOption" -> forAll(laws.intercalateCombineAllOption _)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ object KernelCheck {
implicit val arbitraryDuration: Arbitrary[Duration] = {
// max range is +/- 292 years, but we give ourselves some extra headroom
// to ensure that we can add these things up. they crash on overflow.
val n = (292L * 365) / 50
val n = (292L * 365) / 500
Arbitrary(
Gen.oneOf(
Gen.choose(-n, n).map(Duration(_, DAYS)),
Expand All @@ -51,7 +51,7 @@ object KernelCheck {
implicit val arbitraryFiniteDuration: Arbitrary[FiniteDuration] = {
// max range is +/- 292 years, but we give ourselves some extra headroom
// to ensure that we can add these things up. they crash on overflow.
val n = (292L * 365) / 50
val n = (292L * 365) / 500
Arbitrary(
Gen.oneOf(
Gen.choose(-n, n).map(FiniteDuration(_, DAYS)),
Expand Down Expand Up @@ -314,9 +314,6 @@ class Tests extends TestsConfig with AnyFunSuiteLike with FunSuiteDiscipline wit
checkAll("Hash[Queue[Int]", HashTests[Queue[Int]].hash)

{
// default Arbitrary[BigDecimal] is a bit too intense :/
implicit val arbBigDecimal: Arbitrary[BigDecimal] =
Arbitrary(arbitrary[Double].map(n => BigDecimal(n.toString)))
checkAll("Order[BigDecimal]", OrderTests[BigDecimal].order)
checkAll("CommutativeGroup[BigDecimal]", CommutativeGroupTests[BigDecimal].commutativeGroup)
checkAll("CommutativeGroup[BigDecimal]", SerializableTests.serializable(CommutativeGroup[BigDecimal]))
Expand Down
6 changes: 5 additions & 1 deletion kernel/src/main/scala/cats/kernel/Band.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import scala.{specialized => sp}
* Bands are semigroups whose operation
* (i.e. combine) is also idempotent.
*/
trait Band[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A]
trait Band[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] {
override def combineN(a: A, n: Int): A =
if (n <= 0) throw new IllegalArgumentException("Repeated combining for semigroups must have n > 0")
else a // combine(a, a) == a
}

object Band extends SemigroupFunctions[Band] {

Expand Down
Loading

0 comments on commit 2d87f95

Please sign in to comment.