diff --git a/.travis.yml b/.travis.yml index b879341e82..0b91a0411a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,16 @@ git: depth: 9999 scala: -- 2.10.6 -- 2.11.8 + - 2.10.6 + - 2.11.8 + +jdk: + - oraclejdk7 + +matrix: + include: + - scala: 2.12.0-RC2 + jdk: oraclejdk8 before_install: - export PATH=${PATH}:./vendor/bundle diff --git a/build.sbt b/build.sbt index 7d125cacf1..85af356214 100644 --- a/build.sbt +++ b/build.sbt @@ -1,37 +1,20 @@ import com.typesafe.sbt.SbtGhPages.GhPagesKeys._ import sbtunidoc.Plugin.UnidocKeys._ import ReleaseTransformations._ -import ScoverageSbtPlugin._ import scala.xml.transform.{RewriteRule, RuleTransformer} +import org.scalajs.sbtplugin.cross.CrossProject lazy val botBuild = settingKey[Boolean]("Build by TravisCI instead of local dev environment") lazy val scoverageSettings = Seq( - ScoverageKeys.coverageMinimum := 60, - ScoverageKeys.coverageFailOnMinimum := false, - ScoverageKeys.coverageHighlighting := scalaBinaryVersion.value != "2.10", - ScoverageKeys.coverageExcludedPackages := "cats\\.bench\\..*", - // don't include scoverage as a dependency in the pom - // see issue #980 - // this code was copied from https://github.com/mongodb/mongo-spark - pomPostProcess := { (node: xml.Node) => - new RuleTransformer( - new RewriteRule { - override def transform(node: xml.Node): Seq[xml.Node] = node match { - case e: xml.Elem - if e.label == "dependency" && e.child.exists(child => child.label == "groupId" && child.text == "org.scoverage") => Nil - case _ => Seq(node) - - } - - }).transform(node).head - } + coverageMinimum := 60, + coverageFailOnMinimum := false ) lazy val buildSettings = Seq( organization := "org.typelevel", scalaVersion := "2.11.8", - crossScalaVersions := Seq("2.10.6", "2.11.8") + crossScalaVersions := Seq("2.10.6", "2.11.8", "2.12.0-RC2") ) lazy val catsDoctestSettings = Seq( @@ -46,7 +29,7 @@ lazy val kernelSettings = Seq( Resolver.sonatypeRepo("snapshots")), parallelExecution in Test := false, scalacOptions in (Compile, doc) := (scalacOptions in (Compile, doc)).value.filter(_ != "-Xfatal-warnings") -) ++ warnUnusedImport +) ++ warnUnusedImport ++ update2_12 lazy val commonSettings = Seq( incOptions := incOptions.value.withLogRecompileOnMacro(false), @@ -57,17 +40,17 @@ lazy val commonSettings = Seq( Resolver.sonatypeRepo("snapshots") ), libraryDependencies ++= Seq( - "com.github.mpilquist" %%% "simulacrum" % "0.8.0", - "org.typelevel" %%% "machinist" % "0.4.1", + "com.github.mpilquist" %%% "simulacrum" % "0.10.0", + "org.typelevel" %%% "machinist" % "0.6.0", compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0" cross CrossVersion.full), - compilerPlugin("org.spire-math" %% "kind-projector" % "0.9.0") + compilerPlugin("org.spire-math" %% "kind-projector" % "0.9.2") ), fork in test := true, parallelExecution in Test := false, scalacOptions in (Compile, doc) := (scalacOptions in (Compile, doc)).value.filter(_ != "-Xfatal-warnings"), // workaround for https://github.com/scalastyle/scalastyle-sbt-plugin/issues/47 - (scalastyleSources in Compile) <++= unmanagedSourceDirectories in Compile -) ++ warnUnusedImport + scalastyleSources in Compile ++= (unmanagedSourceDirectories in Compile).value +) ++ warnUnusedImport ++ update2_12 lazy val tagName = Def.setting{ s"v${if (releaseUseGlobalVersion.value) (version in ThisBuild).value else version.value}" @@ -84,8 +67,6 @@ lazy val commonJsSettings = Seq( }, scalaJSStage in Global := FastOptStage, parallelExecution := false, - // Using Rhino as jsEnv to build scala.js code can lead to OOM, switch to PhantomJS by default - scalaJSUseRhino := false, requiresDOM := false, jsEnv := NodeJSEnv().value, // Only used for scala.js for now @@ -115,15 +96,15 @@ lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ sc lazy val scalaCheckVersion = "1.13.2" lazy val scalaTestVersion = "3.0.0" -lazy val disciplineVersion = "0.6" +lazy val disciplineVersion = "0.7.1" lazy val disciplineDependencies = Seq( libraryDependencies += "org.scalacheck" %%% "scalacheck" % scalaCheckVersion, libraryDependencies += "org.typelevel" %%% "discipline" % disciplineVersion) lazy val testingDependencies = Seq( - libraryDependencies += "org.typelevel" %%% "catalysts-platform" % "0.0.2", - libraryDependencies += "org.typelevel" %%% "catalysts-macros" % "0.0.2" % "test", + libraryDependencies += "org.typelevel" %%% "catalysts-platform" % "0.0.4", + libraryDependencies += "org.typelevel" %%% "catalysts-macros" % "0.0.4" % "test", libraryDependencies += "org.scalatest" %%% "scalatest" % scalaTestVersion % "test") @@ -224,6 +205,7 @@ lazy val macros = crossProject.crossType(CrossType.Pure) .settings(catsSettings:_*) .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) + .jsSettings(coverageEnabled := false) .settings(scalacOptions := scalacOptions.value.filter(_ != "-Xfatal-warnings")) lazy val macrosJVM = macros.jvm @@ -236,7 +218,7 @@ lazy val kernel = crossProject.crossType(CrossType.Pure) .settings(buildSettings: _*) .settings(publishSettings: _*) .settings(scoverageSettings: _*) - .settings(sourceGenerators in Compile <+= (sourceManaged in Compile).map(KernelBoiler.gen)) + .settings(sourceGenerators in Compile += (sourceManaged in Compile).map(KernelBoiler.gen).taskValue) .settings(includeGeneratedSrc) .jsSettings(commonJsSettings:_*) .jvmSettings((commonJvmSettings ++ (mimaPreviousArtifacts := Set("org.typelevel" %% "cats-kernel" % "0.7.0"))):_*) @@ -255,6 +237,7 @@ lazy val kernelLaws = crossProject.crossType(CrossType.Pure) .settings(testingDependencies: _*) .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) + .jsSettings(coverageEnabled := false) .dependsOn(kernel) lazy val kernelLawsJVM = kernelLaws.jvm @@ -264,8 +247,10 @@ lazy val core = crossProject.crossType(CrossType.Pure) .dependsOn(macros, kernel) .settings(moduleName := "cats-core") .settings(catsSettings:_*) - .settings(sourceGenerators in Compile <+= (sourceManaged in Compile).map(Boilerplate.gen)) + .settings(sourceGenerators in Compile += (sourceManaged in Compile).map(Boilerplate.gen).taskValue) .settings(includeGeneratedSrc) + .configureCross(disableScoverage210Jvm) + .configureCross(disableScoverage210Js) .settings(libraryDependencies += "org.scalacheck" %%% "scalacheck" % scalaCheckVersion % "test") .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) @@ -278,9 +263,11 @@ lazy val laws = crossProject.crossType(CrossType.Pure) .settings(moduleName := "cats-laws") .settings(catsSettings:_*) .settings(disciplineDependencies:_*) - .settings(libraryDependencies ++= Seq("org.typelevel" %%% "catalysts-platform" % "0.0.2")) + .configureCross(disableScoverage210Jvm) + .settings(libraryDependencies ++= Seq("org.typelevel" %%% "catalysts-platform" % "0.0.4")) .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) + .jsSettings(coverageEnabled := false) lazy val lawsJVM = laws.jvm lazy val lawsJS = laws.js @@ -309,13 +296,15 @@ lazy val testsJVM = tests.jvm lazy val testsJS = tests.js // bench is currently JVM-only + lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM) .settings(moduleName := "cats-bench") .settings(catsSettings) .settings(noPublishSettings) .settings(commonJvmSettings) + .settings(coverageEnabled := false) .settings(libraryDependencies ++= Seq( - "org.scalaz" %% "scalaz-core" % "7.2.5")) + "org.scalaz" %% "scalaz-core" % "7.2.6")) .enablePlugins(JmhPlugin) // cats-js is JS-only @@ -324,6 +313,7 @@ lazy val js = project .settings(moduleName := "cats-js") .settings(catsSettings:_*) .settings(commonJsSettings:_*) + .configure(disableScoverage210Js) .enablePlugins(ScalaJSPlugin) @@ -461,8 +451,8 @@ lazy val scalaMacroDependencies: Seq[Setting[_]] = Seq( // in Scala 2.10, quasiquotes are provided by macro paradise case Some((2, 10)) => Seq( - compilerPlugin("org.scalamacros" %% "paradise" % "2.0.1" cross CrossVersion.full), - "org.scalamacros" %% "quasiquotes" % "2.0.1" cross CrossVersion.binary + compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0" cross CrossVersion.full), + "org.scalamacros" %% "quasiquotes" % "2.1.0" cross CrossVersion.binary ) } } @@ -479,7 +469,6 @@ lazy val commonScalacOptions = Seq( "-unchecked", "-Xfatal-warnings", "-Xlint", - "-Yinline-warnings", "-Yno-adapted-args", "-Ywarn-dead-code", "-Ywarn-numeric-widen", @@ -529,7 +518,7 @@ lazy val warnUnusedImport = Seq( } }, scalacOptions in (Compile, console) ~= {_.filterNot("-Ywarn-unused-import" == _)}, - scalacOptions in (Test, console) <<= (scalacOptions in (Compile, console)) + scalacOptions in (Test, console) := (scalacOptions in (Compile, console)).value ) lazy val credentialSettings = Seq( @@ -539,3 +528,45 @@ lazy val credentialSettings = Seq( password <- Option(System.getenv().get("SONATYPE_PASSWORD")) } yield Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", username, password)).toSeq ) + +def disableScoverage210Js(crossProject: CrossProject) = + crossProject + .jsSettings( + coverageEnabled := { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 10)) => false + case _ => coverageEnabled.value + } + } + ) + +def disableScoverage210Js: Project ⇒ Project = p => + p.settings( + coverageEnabled := { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 10)) => false + case _ => coverageEnabled.value + } + } + ) + +def disableScoverage210Jvm(crossProject: CrossProject) = + crossProject + .jvmSettings( + coverageEnabled := { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 10)) => false + case _ => coverageEnabled.value + } + } + ) + +lazy val update2_12 = Seq( + scalacOptions -= { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 12)) => "-Yinline-warnings" + case _ => "" + } + } +) + diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index 4779eb2023..487c17d3f4 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -74,8 +74,11 @@ sealed abstract class Eval[+A] extends Serializable { self => case c: Eval.Compute[A] => new Eval.Compute[B] { type Start = c.Start - val start = c.start - val run = (s: c.Start) => + // See https://issues.scala-lang.org/browse/SI-9931 for an explanation + // of why the type annotations are necessary in these two lines on + // Scala 2.12.0. + val start: () => Eval[Start] = c.start + val run: Start => Eval[B] = (s: c.Start) => new Eval.Compute[B] { type Start = A val start = () => c.run(s) diff --git a/docs/src/main/tut/datatypes/either.md b/docs/src/main/tut/datatypes/either.md index fa0dc9655b..971ff31b41 100644 --- a/docs/src/main/tut/datatypes/either.md +++ b/docs/src/main/tut/datatypes/either.md @@ -235,16 +235,30 @@ object Service { Let's say we have an application that wants to do database things, and then take database values and do service things. Glancing at the types, it looks like `flatMap` will do it. -```tut:fail:silent +```scala def doApp = Database.databaseThings().flatMap(Service.serviceThings) ``` -This doesn't work! The reason this occurs is because the first type parameter in the two `Either`s are different - -`databaseThings()` can give us a `DatabaseError` whereas `serviceThings()` can give us a -`ServiceError`: two completely unrelated types. While `Either`'s type parameters are covariant, -the aforementioned syntax enrichment is invariant to avoid nasty variance issues like inferring -`Object`. Therefore, when the compiler sees `Either[E1, A1]` and an `Either[E2, A2]`, it -will simply reject the `flatMap` call. +If you're on Scala 2.12, this line will compile and work as expected, but if you're on an earlier +version of Scala it won't! This difference is related to the right-biasing of `Either` in Scala 2.12 +that was mentioned above. In Scala 2.12 the `flatMap` we get here is a method on `Either` with this +signature: + +```scala +def flatMap[AA >: A, Y](f: (B) => Either[AA, Y]): Either[AA, Y] +``` + +This `flatMap` is different from the ones you'll find on `List` or `Option`, for example, in that it +has two type parameters, with the extra `AA` parameter allowing us to `flatMap` into an `Either` +with a different type on the left side. + +This behavior is consistent with the covariance of `Either`, and in some cases it can be convenient, +but it also makes it easy to run into nasty variance issues (such as `Object` being inferred as the +type of the left side, as it is in this case). + +For this reason the `flatMap` provided by Cats's `Either` syntax (which is the one you'll get for +Scala 2.10 and 2.11) does not include this extra type parameter. Instead the left sides have to +match, which means our `doApp` definition above will not compile on versions of Scala before 2.12. ### Solution 1: Application-wide errors So clearly in order for us to easily compose `Either` values, the left type parameter must be the same. diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala index 9b8b014cb3..f6b959cf54 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -10,6 +10,7 @@ import org.typelevel.discipline.{ Laws } import org.typelevel.discipline.scalatest.Discipline import org.scalacheck.{ Arbitrary, Cogen, Gen } import Arbitrary.arbitrary +import org.scalactic.anyvals.{ PosInt, PosZInt } import org.scalatest.FunSuite import scala.util.Random @@ -18,11 +19,11 @@ import scala.collection.immutable.BitSet class LawTests extends FunSuite with Discipline { // The scalacheck defaults (100,100) are too high for scala-js. - final val PropMaxSize = if (Platform.isJs) 10 else 100 - final val PropMinSuccessful = if (Platform.isJs) 10 else 100 + final val PropMaxSize: PosZInt = if (Platform.isJs) 10 else 100 + final val PropMinSuccessful: PosInt = if (Platform.isJs) 10 else 100 implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = PropMaxSize, minSuccessful = PropMinSuccessful) + PropertyCheckConfiguration(minSuccessful = PropMinSuccessful, sizeRange = PropMaxSize) implicit def orderLaws[A: Cogen: Eq: Arbitrary]: OrderLaws[A] = OrderLaws[A] implicit def groupLaws[A: Cogen: Eq: Arbitrary]: GroupLaws[A] = GroupLaws[A] diff --git a/project/build.properties b/project/build.properties index 35c88bab7d..e0d25e8c63 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.12 +sbt.version=0.13.13-RC3 diff --git a/project/plugins.sbt b/project/plugins.sbt index aeb8b9e881..a5f76070cf 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,13 +1,13 @@ addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.3.3") addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.3") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.9") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.11") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.11") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.16") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.0-RC2") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.11") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.13") addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.4.1") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1") -addSbtPlugin("com.fortysevendeg" % "sbt-microsites" % "0.2.6") +addSbtPlugin("com.fortysevendeg" % "sbt-microsites" % "0.2.7") diff --git a/tests/src/test/scala/cats/tests/CatsSuite.scala b/tests/src/test/scala/cats/tests/CatsSuite.scala index 2909a55b86..750273bb6b 100644 --- a/tests/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/src/test/scala/cats/tests/CatsSuite.scala @@ -23,7 +23,7 @@ trait TestSettings extends Configuration with Matchers { lazy val slowCheckConfiguration: PropertyCheckConfiguration = if (Platform.isJvm) checkConfiguration - else PropertyCheckConfig(maxSize = 1, minSuccessful = 1) + else PropertyCheckConfiguration(minSuccessful = 1, sizeRange = 1) } /** diff --git a/tests/src/test/scala/cats/tests/NestedTests.scala b/tests/src/test/scala/cats/tests/NestedTests.scala index 9063647450..0032ea502a 100644 --- a/tests/src/test/scala/cats/tests/NestedTests.scala +++ b/tests/src/test/scala/cats/tests/NestedTests.scala @@ -13,7 +13,7 @@ class NestedTests extends CatsSuite { // Scalacheck to calm down a bit so we don't hit memory and test duration // issues. implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) implicit val iso = { implicit val instance = ListWrapper.functor diff --git a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala index 024c58b626..c913a8f8db 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala @@ -10,7 +10,7 @@ import cats.laws.discipline.arbitrary._ class NonEmptyListTests extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) checkAll("NonEmptyList[Int]", OrderLaws[NonEmptyList[Int]].order) diff --git a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala index 53c4feacb9..51d95b7197 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala @@ -14,7 +14,7 @@ import scala.util.Properties class NonEmptyVectorTests extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) checkAll("NonEmptyVector[Int]", OrderLaws[NonEmptyVector[Int]].eqv) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index f3a7b26f61..a3ec809c2e 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -10,7 +10,7 @@ import cats.laws.discipline.arbitrary._ class OneAndTests extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) checkAll("OneAnd[Stream, Int]", OrderLaws[OneAnd[Stream, Int]].eqv) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index b41c680b23..69a261c1d9 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -17,7 +17,7 @@ class WriterTTests extends CatsSuite { // Scalacheck to calm down a bit so we don't hit memory and test duration // issues. implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + PropertyCheckConfiguration(minSuccessful = 20, sizeRange = 5) checkAll("WriterT[List, Int, Int]", OrderLaws[WriterT[List, Int, Int]].eqv) checkAll("Eq[WriterT[List, Int, Int]]", SerializableTests.serializable(Eq[WriterT[List, Int, Int]]))