diff --git a/.travis.yml b/.travis.yml index 7965bb8dc0..1ca2bf0a42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ jdk: - openjdk8 -scala_version_212: &scala_version_212 2.12.9 +scala_version_212: &scala_version_212 2.12.10 scala_version_213: &scala_version_213 2.13.0 before_install: @@ -29,12 +29,15 @@ stages: jobs: include: # it can speed up the overall build to have the longer-running jobs at the top of this list. - - stage: test + - &js_tests + stage: test env: TEST="JS tests" # http://austinpray.com/ops/2015/09/20/change-travis-node-version.html install: rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION script: sbt ++$TRAVIS_SCALA_VERSION! validateJS && sbt ++$TRAVIS_SCALA_VERSION! validateKernelJS && sbt ++$TRAVIS_SCALA_VERSION! validateFreeJS scala: *scala_version_212 + - <<: *js_tests + scala: *scala_version_213 - &jvm_tests stage: test @@ -64,7 +67,7 @@ jobs: - stage: styling env: TEST="linting" - script: sbt fmtCheck + script: sbt +fmtCheck - stage: publish snapshot script: | diff --git a/build.sbt b/build.sbt index a4643e40ff..38f08cb4be 100644 --- a/build.sbt +++ b/build.sbt @@ -17,17 +17,15 @@ val isTravisBuild = settingKey[Boolean]("Flag indicating whether the current bui val crossScalaVersionsFromTravis = settingKey[Seq[String]]("Scala versions set in .travis.yml as scala_version_XXX") isTravisBuild in Global := sys.env.get("TRAVIS").isDefined -val scalatestVersion = "3.1.0-SNAP13" - -val scalatestplusScalaCheckVersion = "1.0.0-SNAP8" - val scalaCheckVersion = "1.14.2" -val disciplineVersion = "1.0.0" +val scalatestplusScalaCheckVersion = "3.1.0.0-RC2" -val disciplineScalatestVersion = "1.0.0-M1" +val disciplineVersion = "1.0.1" -val kindProjectorVersion = "0.10.3" +val disciplineScalatestVersion = "1.0.0-RC1" + +val kindProjectorVersion = "0.11.0" crossScalaVersionsFromTravis in Global := { val manifest = (baseDirectory in ThisBuild).value / ".travis.yml" @@ -85,7 +83,7 @@ def macroDependencies(scalaVersion: String) = lazy val catsSettings = Seq( incOptions := incOptions.value.withLogRecompileOnMacro(false), libraryDependencies ++= Seq( - compilerPlugin("org.typelevel" %% "kind-projector" % kindProjectorVersion) + compilerPlugin(("org.typelevel" %% "kind-projector" % kindProjectorVersion).cross(CrossVersion.full)) ) ++ macroDependencies(scalaVersion.value) ) ++ commonSettings ++ publishSettings ++ scoverageSettings ++ simulacrumSettings @@ -99,7 +97,7 @@ lazy val simulacrumSettings = Seq( 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 == "com.github.mpilquist") && + e.child.exists(child => child.label == "groupId" && child.text == "org.typelevel") && e.child.exists(child => child.label == "artifactId" && child.text.startsWith("simulacrum_")) => Nil case _ => Seq(node) @@ -157,9 +155,8 @@ lazy val disciplineDependencies = Seq( lazy val testingDependencies = Seq( libraryDependencies ++= Seq( - "org.scalatest" %%% "scalatest" % scalatestVersion % "test", - "org.scalatestplus" %%% "scalatestplus-scalacheck" % scalatestplusScalaCheckVersion % "test", - "org.typelevel" %%% "discipline-scalatest" % disciplineScalatestVersion % "test" + "org.typelevel" %%% "discipline-scalatest" % disciplineScalatestVersion % "test", + "org.scalatestplus" %%% "scalatestplus-scalacheck" % scalatestplusScalaCheckVersion % "test" ) ) @@ -560,12 +557,7 @@ lazy val tests = crossProject(JSPlatform, JVMPlatform) .settings(moduleName := "cats-tests") .settings(catsSettings) .settings(noPublishSettings) - .settings( - libraryDependencies ++= Seq( - "org.scalatestplus" %%% "scalatestplus-scalacheck" % scalatestplusScalaCheckVersion, - "org.typelevel" %%% "discipline-scalatest" % disciplineScalatestVersion - ) - ) + .settings(testingDependencies) .jsSettings(commonJsSettings) .jvmSettings(commonJvmSettings) .settings(scalacOptions in Test := (scalacOptions in Test).value.filter(_ != "-Xfatal-warnings")) @@ -578,11 +570,6 @@ lazy val testkit = crossProject(JSPlatform, JVMPlatform) .settings(moduleName := "cats-testkit") .settings(catsSettings) .settings(disciplineDependencies) - .settings( - libraryDependencies ++= Seq( - "org.scalacheck" %%% "scalacheck" % scalaCheckVersion - ) - ) .jsSettings(commonJsSettings) .jvmSettings(commonJvmSettings) .settings(scalacOptions := scalacOptions.value.filter(_ != "-Xfatal-warnings")) @@ -653,17 +640,17 @@ lazy val binCompatTest = project // see https://github.com/typelevel/cats/pull/3026#discussion_r321984342 useCoursier := false, commonScalaVersionSettings, - addCompilerPlugin("org.typelevel" %% "kind-projector" % kindProjectorVersion), + addCompilerPlugin(("org.typelevel" %% "kind-projector" % kindProjectorVersion).cross(CrossVersion.full)), libraryDependencies ++= List( { if (priorTo2_13(scalaVersion.value)) mimaPrevious("cats-core", scalaVersion.value, version.value).last % Provided else //We are not testing BC on Scala 2.13 yet. "org.typelevel" %% "cats-core" % "2.0.0-M4" % Provided - }, - "org.scalatest" %%% "scalatest" % scalatestVersion % Test + } ) ) + .settings(testingDependencies) .dependsOn(core.jvm % Test) // cats-js is JS-only @@ -846,7 +833,6 @@ def priorTo2_13(scalaVersion: String): Boolean = lazy val sharedPublishSettings = Seq( releaseTagName := tagName.value, releaseVcsSign := true, - useGpg := true, // bouncycastle has bugs with subkeys, so we use gpg instead publishMavenStyle := true, publishArtifact in Test := false, pomIncludeRepository := Function.const(false), diff --git a/core/src/main/scala-2.12/cats/instances/ScalaVersionSpecificParallelInstances.scala b/core/src/main/scala-2.12/cats/instances/ScalaVersionSpecificParallelInstances.scala index 55ab7e7de3..e43b11f7c1 100644 --- a/core/src/main/scala-2.12/cats/instances/ScalaVersionSpecificParallelInstances.scala +++ b/core/src/main/scala-2.12/cats/instances/ScalaVersionSpecificParallelInstances.scala @@ -3,70 +3,30 @@ package instances import cats.data._ import cats.kernel.Semigroup -import cats.syntax.either._ -import cats.{~>, Applicative, Apply, FlatMap, Monad, NonEmptyParallel, Parallel} +import cats.{NonEmptyParallel, Parallel} trait ParallelInstances extends ParallelInstances1 { - implicit def catsParallelForEitherValidated[E: Semigroup]: Parallel.Aux[Either[E, *], Validated[E, *]] = - new Parallel[Either[E, *]] { - type F[x] = Validated[E, x] - def applicative: Applicative[Validated[E, *]] = Validated.catsDataApplicativeErrorForValidated - def monad: Monad[Either[E, *]] = cats.instances.either.catsStdInstancesForEither - - def sequential: Validated[E, *] ~> Either[E, *] = - λ[Validated[E, *] ~> Either[E, *]](_.toEither) - - def parallel: Either[E, *] ~> Validated[E, *] = - λ[Either[E, *] ~> Validated[E, *]](_.toValidated) - } + @deprecated("Use cats.instances.either.catsParallelForEitherAndValidated", "2.1.0") + def catsParallelForEitherValidated[E: Semigroup]: Parallel.Aux[Either[E, *], Validated[E, *]] = + cats.instances.either.catsParallelForEitherAndValidated[E] @deprecated("Use OptionT.catsDataParallelForOptionT", "2.0.0") def catsParallelForOptionTNestedOption[M[_]]( implicit P: Parallel[M] ): Parallel.Aux[OptionT[M, *], Nested[P.F, Option, *]] = OptionT.catsDataParallelForOptionT[M] - implicit def catsStdNonEmptyParallelForZipList: NonEmptyParallel.Aux[List, ZipList] = - new NonEmptyParallel[List] { - type F[x] = ZipList[x] - - def flatMap: FlatMap[List] = cats.instances.list.catsStdInstancesForList - def apply: Apply[ZipList] = ZipList.catsDataCommutativeApplyForZipList - - def sequential: ZipList ~> List = - λ[ZipList ~> List](_.value) - - def parallel: List ~> ZipList = - λ[List ~> ZipList](v => new ZipList(v)) - } - - implicit def catsStdNonEmptyParallelForZipVector: NonEmptyParallel.Aux[Vector, ZipVector] = - new NonEmptyParallel[Vector] { - type F[x] = ZipVector[x] - - def flatMap: FlatMap[Vector] = cats.instances.vector.catsStdInstancesForVector - def apply: Apply[ZipVector] = ZipVector.catsDataCommutativeApplyForZipVector - - def sequential: ZipVector ~> Vector = - λ[ZipVector ~> Vector](_.value) - - def parallel: Vector ~> ZipVector = - λ[Vector ~> ZipVector](v => new ZipVector(v)) - } - - implicit def catsStdParallelForZipStream: Parallel.Aux[Stream, ZipStream] = - new Parallel[Stream] { - type F[x] = ZipStream[x] - - def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream - def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream + @deprecated("Use cats.instances.list.catsStdNonEmptyParallelForListZipList", "2.1.0") + def catsStdNonEmptyParallelForZipList: NonEmptyParallel.Aux[List, ZipList] = + cats.instances.list.catsStdNonEmptyParallelForListZipList - def sequential: ZipStream ~> Stream = - λ[ZipStream ~> Stream](_.value) + @deprecated("Use cats.instances.vector.catsStdNonEmptyParallelForVectorZipVector", "2.1.0") + def catsStdNonEmptyParallelForZipVector: NonEmptyParallel.Aux[Vector, ZipVector] = + cats.instances.vector.catsStdNonEmptyParallelForVectorZipVector - def parallel: Stream ~> ZipStream = - λ[Stream ~> ZipStream](v => new ZipStream(v)) - } + @deprecated("Use cats.instances.stream.catsStdParallelForStreamZipStream", "2.1.0") + def catsStdParallelForZipStream: Parallel.Aux[Stream, ZipStream] = + cats.instances.stream.catsStdParallelForStreamZipStream @deprecated("Use EitherT.catsDataParallelForEitherTWithParallelEffect", "2.0.0") def catsParallelForEitherTNestedParallelValidated[M[_], E: Semigroup]( diff --git a/core/src/main/scala-2.12/cats/instances/stream.scala b/core/src/main/scala-2.12/cats/instances/stream.scala index de0fcbbf84..67d4db6e19 100644 --- a/core/src/main/scala-2.12/cats/instances/stream.scala +++ b/core/src/main/scala-2.12/cats/instances/stream.scala @@ -1,6 +1,7 @@ package cats package instances +import cats.data.ZipStream import cats.syntax.show._ import scala.annotation.tailrec @@ -157,6 +158,19 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { def show(fa: Stream[A]): String = if (fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)" } + implicit def catsStdParallelForStreamZipStream: Parallel.Aux[Stream, ZipStream] = + new Parallel[Stream] { + type F[x] = ZipStream[x] + + def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream + def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream + + def sequential: ZipStream ~> Stream = + λ[ZipStream ~> Stream](_.value) + + def parallel: Stream ~> ZipStream = + λ[Stream ~> ZipStream](v => new ZipStream(v)) + } } private[instances] trait StreamInstancesBinCompat0 { diff --git a/core/src/main/scala-2.13+/cats/instances/ScalaVersionSpecificParallelInstances.scala b/core/src/main/scala-2.13+/cats/instances/ScalaVersionSpecificParallelInstances.scala index ae37f968fe..f3c16fcaa0 100644 --- a/core/src/main/scala-2.13+/cats/instances/ScalaVersionSpecificParallelInstances.scala +++ b/core/src/main/scala-2.13+/cats/instances/ScalaVersionSpecificParallelInstances.scala @@ -3,85 +3,35 @@ package instances import cats.data._ import cats.kernel.Semigroup -import cats.syntax.either._ -import cats.{~>, Applicative, Apply, FlatMap, Monad, NonEmptyParallel, Parallel} +import cats.data.ZipLazyList +import cats.{NonEmptyParallel, Parallel} trait ParallelInstances extends ParallelInstances1 { - implicit def catsParallelForEitherValidated[E: Semigroup]: Parallel.Aux[Either[E, *], Validated[E, *]] = - new Parallel[Either[E, *]] { - type F[x] = Validated[E, x] - def applicative: Applicative[Validated[E, *]] = Validated.catsDataApplicativeErrorForValidated - def monad: Monad[Either[E, *]] = cats.instances.either.catsStdInstancesForEither - - def sequential: Validated[E, *] ~> Either[E, *] = - λ[Validated[E, *] ~> Either[E, *]](_.toEither) - - def parallel: Either[E, *] ~> Validated[E, *] = - λ[Either[E, *] ~> Validated[E, *]](_.toValidated) - } + @deprecated("Use cats.instances.either.catsParallelForEitherAndValidated", "2.1.0") + def catsParallelForEitherValidated[E: Semigroup]: Parallel.Aux[Either[E, *], Validated[E, *]] = + cats.instances.either.catsParallelForEitherAndValidated[E] @deprecated("Use OptionT.catsDataParallelForOptionT", "2.0.0") def catsParallelForOptionTNestedOption[M[_]]( implicit P: Parallel[M] ): Parallel.Aux[OptionT[M, *], Nested[P.F, Option, *]] = OptionT.catsDataParallelForOptionT[M] - implicit def catsStdNonEmptyParallelForZipList: NonEmptyParallel.Aux[List, ZipList] = - new NonEmptyParallel[List] { - type F[x] = ZipList[x] - - def flatMap: FlatMap[List] = cats.instances.list.catsStdInstancesForList - def apply: Apply[ZipList] = ZipList.catsDataCommutativeApplyForZipList - - def sequential: ZipList ~> List = - λ[ZipList ~> List](_.value) - - def parallel: List ~> ZipList = - λ[List ~> ZipList](v => new ZipList(v)) - } - - implicit def catsStdNonEmptyParallelForZipVector: NonEmptyParallel.Aux[Vector, ZipVector] = - new NonEmptyParallel[Vector] { - type F[x] = ZipVector[x] - - def flatMap: FlatMap[Vector] = cats.instances.vector.catsStdInstancesForVector - def apply: Apply[ZipVector] = ZipVector.catsDataCommutativeApplyForZipVector + @deprecated("Use cats.instances.list.catsStdNonEmptyParallelForListZipList", "2.1.0") + def catsStdNonEmptyParallelForZipList: NonEmptyParallel.Aux[List, ZipList] = + cats.instances.list.catsStdNonEmptyParallelForListZipList - def sequential: ZipVector ~> Vector = - λ[ZipVector ~> Vector](_.value) - - def parallel: Vector ~> ZipVector = - λ[Vector ~> ZipVector](v => new ZipVector(v)) - } + @deprecated("Use cats.instances.vector.catsStdNonEmptyParallelForVectorZipVector", "2.1.0") + def catsStdNonEmptyParallelForZipVector: NonEmptyParallel.Aux[Vector, ZipVector] = + cats.instances.vector.catsStdNonEmptyParallelForVectorZipVector @deprecated("Use catsStdParallelForZipLazyList", "2.0.0-RC2") - implicit def catsStdParallelForZipStream: Parallel.Aux[Stream, ZipStream] = - new Parallel[Stream] { - type F[x] = ZipStream[x] - - def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream - def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream - - def sequential: ZipStream ~> Stream = - λ[ZipStream ~> Stream](_.value) - - def parallel: Stream ~> ZipStream = - λ[Stream ~> ZipStream](v => new ZipStream(v)) - } - - implicit def catsStdParallelForZipLazyList[A]: Parallel.Aux[LazyList, ZipLazyList] = - new Parallel[LazyList] { - type F[x] = ZipLazyList[x] - - def monad: Monad[LazyList] = cats.instances.lazyList.catsStdInstancesForLazyList - def applicative: Applicative[ZipLazyList] = ZipLazyList.catsDataAlternativeForZipLazyList - - def sequential: ZipLazyList ~> LazyList = - λ[ZipLazyList ~> LazyList](_.value) + def catsStdParallelForZipStream: Parallel.Aux[Stream, ZipStream] = + cats.instances.stream.catsStdParallelForStreamZipStream - def parallel: LazyList ~> ZipLazyList = - λ[LazyList ~> ZipLazyList](v => new ZipLazyList(v)) - } + @deprecated("Use cats.instances.lazyList.catsStdParallelForLazyListZipLazyList", "2.1.0") + def catsStdParallelForZipLazyList[A]: Parallel.Aux[LazyList, ZipLazyList] = + cats.instances.lazyList.catsStdParallelForLazyListZipLazyList[A] @deprecated("Use EitherT.catsDataParallelForEitherTWithParallelEffect", "2.0.0") def catsParallelForEitherTNestedParallelValidated[M[_], E: Semigroup]( diff --git a/core/src/main/scala-2.13+/cats/instances/lazyList.scala b/core/src/main/scala-2.13+/cats/instances/lazyList.scala index 1d55cb8c21..9e9c344a92 100644 --- a/core/src/main/scala-2.13+/cats/instances/lazyList.scala +++ b/core/src/main/scala-2.13+/cats/instances/lazyList.scala @@ -1,7 +1,9 @@ package cats package instances + import cats.kernel import cats.syntax.show._ +import cats.data.ZipLazyList import scala.annotation.tailrec @@ -155,4 +157,18 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { .value } + + implicit def catsStdParallelForLazyListZipLazyList[A]: Parallel.Aux[LazyList, ZipLazyList] = + new Parallel[LazyList] { + type F[x] = ZipLazyList[x] + + def monad: Monad[LazyList] = cats.instances.lazyList.catsStdInstancesForLazyList + def applicative: Applicative[ZipLazyList] = ZipLazyList.catsDataAlternativeForZipLazyList + + def sequential: ZipLazyList ~> LazyList = + λ[ZipLazyList ~> LazyList](_.value) + + def parallel: LazyList ~> ZipLazyList = + λ[LazyList ~> ZipLazyList](v => new ZipLazyList(v)) + } } diff --git a/core/src/main/scala-2.13+/cats/instances/stream.scala b/core/src/main/scala-2.13+/cats/instances/stream.scala index 68844aa794..fb313600ee 100644 --- a/core/src/main/scala-2.13+/cats/instances/stream.scala +++ b/core/src/main/scala-2.13+/cats/instances/stream.scala @@ -1,6 +1,7 @@ package cats package instances +import cats.data.ZipStream import cats.syntax.show._ import scala.annotation.tailrec @@ -159,6 +160,20 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { def show(fa: Stream[A]): String = if (fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)" } + @deprecated("Use catsStdParallelForZipLazyList", "2.0.0-RC2") + implicit val catsStdParallelForStreamZipStream: Parallel.Aux[Stream, ZipStream] = + new Parallel[Stream] { + type F[x] = ZipStream[x] + + def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream + def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream + + def sequential: ZipStream ~> Stream = + λ[ZipStream ~> Stream](_.value) + + def parallel: Stream ~> ZipStream = + λ[Stream ~> ZipStream](v => new ZipStream(v)) + } } private[instances] trait StreamInstancesBinCompat0 { diff --git a/core/src/main/scala/cats/data/Ior.scala b/core/src/main/scala/cats/data/Ior.scala index ec6c8d91fc..8f5f5cd3ad 100644 --- a/core/src/main/scala/cats/data/Ior.scala +++ b/core/src/main/scala/cats/data/Ior.scala @@ -25,57 +25,543 @@ import scala.annotation.tailrec */ sealed abstract class Ior[+A, +B] extends Product with Serializable { + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> val rightF: Int => String = _.show + * scala> val bothF: (String,Int) => String = (a,b) => a.combine(b.show) + * + * scala> val ior1 = "abc".leftIor[Int] + * scala> ior1.fold(identity, rightF, bothF) + * res0: String = abc + * + * scala> val ior2 = 123.rightIor[String] + * scala> ior2.fold(identity, rightF, bothF) + * res1: String = 123 + * + * scala> val ior3 = Ior.Both("abc", 123) + * scala> ior3.fold(identity, rightF, bothF) + * res2: String = abc123 + * }}} + */ final def fold[C](fa: A => C, fb: B => C, fab: (A, B) => C): C = this match { case Ior.Left(a) => fa(a) case Ior.Right(b) => fb(b) case Ior.Both(a, b) => fab(a, b) } + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> val ior1 = "abc".leftIor[Int] + * scala> ior1.putLeft(true) + * res0: Ior[Boolean, Int] = Left(true) + * + * scala> val ior2 = 123.rightIor[String] + * scala> ior2.putLeft(false) + * res1: Ior[Boolean, Int] = Both(false,123) + * + * scala> val ior3 = Ior.Both("abc",123) + * scala> ior3.putLeft(true) + * res2: Ior[Boolean, Int] = Both(true,123) + * }}} + */ final def putLeft[C](left: C): C Ior B = fold(_ => Ior.left(left), Ior.both(left, _), (_, b) => Ior.both(left, b)) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> val ior1 = "abc".leftIor[Int] + * scala> ior1.putRight(123L) + * res0: Ior[String, Long] = Both(abc,123) + * + * scala> val ior2 = 123.rightIor[String] + * scala> ior2.putRight(123L) + * res1: Ior[String, Long] = Right(123) + * + * scala> val ior3 = Ior.Both("abc",123) + * scala> ior3.putRight(123L) + * res2: Ior[String, Long] = Both(abc,123) + * }}} + */ final def putRight[C](right: C): A Ior C = fold(Ior.both(_, right), _ => Ior.right(right), (a, _) => Ior.both(a, right)) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].isLeft + * res0: Boolean = true + * + * scala> 123.rightIor[String].isLeft + * res1: Boolean = false + * + * scala> Ior.Both("abc", 123).isLeft + * res2: Boolean = false + * }}} + */ final def isLeft: Boolean = fold(_ => true, _ => false, (_, _) => false) final def isRight: Boolean = fold(_ => false, _ => true, (_, _) => false) final def isBoth: Boolean = fold(_ => false, _ => false, (_, _) => true) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].left + * res0: Option[String] = Some(abc) + * + * scala> 123.rightIor[String].left + * res1: Option[String] = None + * + * scala> Ior.Both("abc", 123).left + * res2: Option[String] = Some(abc) + * }}} + */ final def left: Option[A] = fold(a => Some(a), _ => None, (a, _) => Some(a)) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].right + * res0: Option[Int] = None + * + * scala> 123.rightIor[String].right + * res1: Option[Int] = Some(123) + * + * scala> Ior.Both("abc", 123).right + * res2: Option[Int] = Some(123) + * }}} + */ final def right: Option[B] = fold(_ => None, b => Some(b), (_, b) => Some(b)) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].onlyLeft + * res0: Option[String] = Some(abc) + * + * scala> 123.rightIor[String].onlyLeft + * res1: Option[String] = None + * + * scala> Ior.Both("abc", 123).onlyLeft + * res2: Option[String] = None + * }}} + */ final def onlyLeft: Option[A] = fold(a => Some(a), _ => None, (_, _) => None) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].onlyRight + * res0: Option[Int] = None + * + * scala> 123.rightIor[String].onlyRight + * res1: Option[Int] = Some(123) + * + * scala> Ior.Both("abc", 123).onlyRight + * res2: Option[Int] = None + * }}} + */ final def onlyRight: Option[B] = fold(_ => None, b => Some(b), (_, _) => None) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].onlyLeftOrRight + * res0: Option[Either[String, Int]] = Some(Left(abc)) + * + * scala> 123.rightIor[String].onlyLeftOrRight + * res1: Option[Either[String, Int]] = Some(Right(123)) + * + * scala> Ior.Both("abc", 123).onlyLeftOrRight + * res2: Option[Either[String, Int]] = None + * }}} + */ final def onlyLeftOrRight: Option[Either[A, B]] = fold(a => Some(Left(a)), b => Some(Right(b)), (_, _) => None) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].onlyBoth + * res0: Option[(String, Int)] = None + * + * scala> 123.rightIor[String].onlyBoth + * res1: Option[(String, Int)] = None + * + * scala> Ior.Both("abc", 123).onlyBoth + * res2: Option[(String, Int)] = Some((abc,123)) + * }}} + */ final def onlyBoth: Option[(A, B)] = fold(_ => None, _ => None, (a, b) => Some((a, b))) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].pad + * res0: (Option[String], Option[Int]) = (Some(abc),None) + * + * scala> 123.rightIor[String].pad + * res1: (Option[String], Option[Int]) = (None,Some(123)) + * + * scala> Ior.Both("abc", 123).pad + * res2: (Option[String], Option[Int]) = (Some(abc),Some(123)) + * }}} + */ final def pad: (Option[A], Option[B]) = fold(a => (Some(a), None), b => (None, Some(b)), (a, b) => (Some(a), Some(b))) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].unwrap + * res0: Either[Either[String, Int], (String, Int)] = Left(Left(abc)) + * + * scala> 123.rightIor[String].unwrap + * res1: Either[Either[String, Int], (String, Int)] = Left(Right(123)) + * + * scala> Ior.Both("abc", 123).unwrap + * res2: Either[Either[String, Int], (String, Int)] = Right((abc,123)) + * }}} + */ final def unwrap: Either[Either[A, B], (A, B)] = fold(a => Left(Left(a)), b => Left(Right(b)), (a, b) => Right((a, b))) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].toIorNes + * res0: IorNes[String, Int] = Left(TreeSet(abc)) + * + * scala> 123.rightIor[String].toIorNes + * res1: IorNes[String, Int]= Right(123) + * + * scala> Ior.Both("abc", 123).toIorNes + * res2: IorNes[String, Int] = Both(TreeSet(abc),123) + * }}} + */ final def toIorNes[AA >: A](implicit O: Order[AA]): IorNes[AA, B] = leftMap(NonEmptySet.one(_)) + final def toIorNec[AA >: A]: IorNec[AA, B] = leftMap(NonEmptyChain.one) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].toIorNel + * res0: IorNel[String, Int] = Left(NonEmptyList(abc)) + * + * scala> 123.rightIor[String].toIorNel + * res1: IorNel[String, Int]= Right(123) + * + * scala> Ior.Both("abc", 123).toIorNel + * res2: IorNel[String, Int] = Both(NonEmptyList(abc),123) + * }}} + */ final def toIorNel[AA >: A]: IorNel[AA, B] = leftMap(NonEmptyList.one) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].toEither + * res0: Either[String, Int] = Left(abc) + * + * scala> 123.rightIor[String].toEither + * res1: Either[String, Int]= Right(123) + * + * scala> Ior.Both("abc", 123).toEither + * res2: Either[String, Int] = Right(123) + * }}} + */ final def toEither: Either[A, B] = fold(Left(_), Right(_), (_, b) => Right(b)) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].toValidated + * res0: Validated[String, Int] = Invalid(abc) + * + * scala> 123.rightIor[String].toValidated + * res1: Validated[String, Int]= Valid(123) + * + * scala> Ior.Both("abc", 123).toValidated + * res2: Validated[String, Int] = Valid(123) + * }}} + */ final def toValidated: Validated[A, B] = fold(Invalid(_), Valid(_), (_, b) => Valid(b)) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].toOption + * res0: Option[Int] = None + * + * scala> 123.rightIor[String].toOption + * res1: Option[Int]= Some(123) + * + * scala> Ior.Both("abc", 123).toOption + * res2: Option[Int] = Some(123) + * }}} + */ final def toOption: Option[B] = right + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].toList + * res0: List[Int] = List() + * + * scala> 123.rightIor[String].toList + * res1: List[Int]= List(123) + * + * scala> Ior.Both("abc", 123).toList + * res2: List[Int] = List(123) + * }}} + */ final def toList: List[B] = right.toList + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].to[List, Int] + * res0: List[Int] = List() + * + * scala> 123.rightIor[String].to[List, Int] + * res1: List[Int]= List(123) + * + * scala> Ior.Both("abc", 123).to[List, Int] + * res2: List[Int] = List(123) + * }}} + */ final def to[F[_], BB >: B](implicit F: Alternative[F]): F[BB] = fold(_ => F.empty, F.pure, (_, b) => F.pure(b)) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].swap + * res0: Ior[Int, String] = Right(abc) + * + * scala> 123.rightIor[String].swap + * res1: Ior[Int, String] = Left(123) + * + * scala> Ior.Both("abc", 123).swap + * res2: Ior[Int, String] = Both(123,abc) + * }}} + */ final def swap: B Ior A = fold(Ior.right, Ior.left, (a, b) => Ior.both(b, a)) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].exists(_ > 100) + * res0: Boolean = false + * + * scala> 123.rightIor[String].exists(_ > 100) + * res1: Boolean = true + * + * scala> Ior.Both("abc", 123).exists(_ > 100) + * res2: Boolean = true + * }}} + */ final def exists(p: B => Boolean): Boolean = right.exists(p) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].forall(_ > 100) + * res0: Boolean = true + * + * scala> 123.rightIor[String].forall(_ > 150) + * res1: Boolean = false + * + * scala> Ior.Both("abc", 123).forall(_ > 100) + * res2: Boolean = true + * }}} + */ final def forall(p: B => Boolean): Boolean = right.forall(p) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].getOrElse(456) + * res0: Int = 456 + * + * scala> 123.rightIor[String].getOrElse(456) + * res1: Int = 123 + * + * scala> Ior.Both("abc", 123).getOrElse(456) + * res2: Int = 123 + * }}} + */ final def getOrElse[BB >: B](bb: => BB): BB = right.getOrElse(bb) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].valueOr(_.length) + * res0: Int = 3 + * + * scala> 123.rightIor[String].valueOr(_.length) + * res1: Int = 123 + * + * scala> Ior.Both("abc", 123).valueOr(_.length) + * res2: Int = 126 + * }}} + */ final def valueOr[BB >: B](f: A => BB)(implicit BB: Semigroup[BB]): BB = fold(f, identity, (a, b) => BB.combine(f(a), b)) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].bimap(_.length, identity) + * res0: Ior[Int, Int] = Left(3) + * + * scala> 123.rightIor[String].bimap(_.length, identity) + * res1: Ior[Int, Int] = Right(123) + * + * scala> Ior.Both("abc", 123).bimap(_.length, identity) + * res2: Ior[Int, Int] = Both(3,123) + * }}} + */ final def bimap[C, D](fa: A => C, fb: B => D): C Ior D = fold(a => Ior.left(fa(a)), b => Ior.right(fb(b)), (a, b) => Ior.both(fa(a), fb(b))) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].bimap(_.length, _ * 2) + * res0: Ior[Int, Int] = Left(3) + * + * scala> 123.rightIor[String].bimap(_.length, _ * 2) + * res1: Ior[Int, Int] = Right(246) + * + * scala> Ior.Both("abc", 123).bimap(_.length, _ * 2) + * res2: Ior[Int, Int] = Both(3,246) + * }}} + */ final def map[D](f: B => D): A Ior D = bimap(identity, f) + + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].leftMap(_.length) + * res0: Ior[Int, Int] = Left(3) + * + * scala> 123.rightIor[String].leftMap(_.length) + * res1: Ior[Int, Int] = Right(123) + * + * scala> Ior.Both("abc", 123).leftMap(_.length) + * res2: Ior[Int, Int] = Both(3,123) + * }}} + */ final def leftMap[C](f: A => C): C Ior B = bimap(f, identity) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].flatMap(i => 456.rightIor[String]) + * res0: Ior[String, Int] = Left(abc) + * + * scala> 123.rightIor[String].flatMap(i => (i * 2).rightIor[String]) + * res1: Ior[String, Int] = Right(246) + * + * scala> 123.rightIor[String].flatMap(_ => "error".leftIor[Int]) + * res2: Ior[String, Int] = Left(error) + * + * scala> Ior.Both("abc", 123).flatMap(_ => 456.rightIor[String]) + * res3: Ior[String, Int] = Both(abc,456) + * + * scala> Ior.Both("abc", 123).flatMap(_ => "error".leftIor[Int]) + * res4: Ior[String, Int] = Left(abcerror) + * + * scala> Ior.Both("abc", 123).flatMap(_ => Ior.Both("error",456)) + * res5: Ior[String, Int] = Both(abcerror,456) + * }}} + */ final def flatMap[AA >: A, D](f: B => AA Ior D)(implicit AA: Semigroup[AA]): AA Ior D = this match { case l @ Ior.Left(_) => l case Ior.Right(b) => f(b) @@ -98,6 +584,22 @@ sealed abstract class Ior[+A, +B] extends Product with Serializable { case Ior.Both(a, b) => F.map(g(b))(d => Ior.both(a, d)) } + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].foldLeft(List(456))((c,b) => b :: c) + * res0: List[Int] = List(456) + * + * scala> 123.rightIor[String].foldLeft(List(456))((c,b) => b :: c) + * res1: List[Int] = List(123, 456) + * + * scala> Ior.Both("abc", 123).foldLeft(List(456))((c,b) => b :: c) + * res2: List[Int] = List(123, 456) + * }}} + */ final def foldLeft[C](c: C)(f: (C, B) => C): C = fold(_ => c, f(c, _), (_, b) => f(c, b)) @@ -111,6 +613,31 @@ sealed abstract class Ior[+A, +B] extends Product with Serializable { final def mergeRight[AA >: A](implicit ev: B <:< AA): AA = fold(identity, ev, (_, b) => ev(b)) + /** + * Example: + * {{{ + * scala> import cats.data.Ior + * scala> import cats.implicits._ + * + * scala> "abc".leftIor[Int].combine("other".leftIor[Int]) + * res0: Ior[String, Int] = Left(abcother) + * + * scala> "abc".leftIor[Int].combine(456.rightIor[String]) + * res1: Ior[String, Int] = Both(abc,456) + * + * scala> 123.rightIor[String].combine("other".leftIor[Int]) + * res2: Ior[String, Int] = Both(other,123) + * + * scala> Ior.Both("abc", 123).combine(Ior.Both("other",456)) + * res3: Ior[String, Int] = Both(abcother,579) + * + * scala> Ior.Both("abc", 123).combine("other".leftIor[Int]) + * res3: Ior[String, Int] = Both(abcother,123) + * + * scala> Ior.Both("abc", 123).combine(456.rightIor[String]) + * res3: Ior[String, Int] = Both(abc,579) + * }}} + */ // scalastyle:off cyclomatic.complexity final def combine[AA >: A, BB >: B](that: AA Ior BB)(implicit AA: Semigroup[AA], BB: Semigroup[BB]): AA Ior BB = this match { diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index c61416fdd6..a6baf9dacc 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -7,25 +7,112 @@ import cats.kernel.CommutativeMonoid import cats.syntax.semigroup._ final case class WriterT[F[_], L, V](run: F[(L, V)]) { + + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, List[String], Int](Some(123)) + * scala> writer.tell(List("a","b","c")).tell(List("d","e","f")) + * res0: WriterT[Option, List[String], Int] = WriterT(Some((List(a, b, c, d, e, f),123))) + * }}} + */ def tell(l: L)(implicit functorF: Functor[F], semigroupL: Semigroup[L]): WriterT[F, L, V] = mapWritten(_ |+| l) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer: WriterT[Option, List[String], Int] = WriterT.liftF(Some(123)) + * scala> writer.tell(List("a","b","c")).written.getOrElse(Nil) + * res0: List[String] = List(a, b, c) + * }}} + */ def written(implicit functorF: Functor[F]): F[L] = functorF.map(run)(_._1) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer: WriterT[Option, List[String], Int] = WriterT.liftF(Some(123)) + * scala> val wt: WriterT[Option, List[String], Int] = writer.tell(List("error")) + * res0: WriterT[Option, List[String], Int] = WriterT(Some((List(error),123))) + * + * scala> wt.value + * res1: Option[Int] = Some(123) + * }}} + */ def value(implicit functorF: Functor[F]): F[V] = functorF.map(run)(_._2) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer: WriterT[Option, String, Int] = WriterT.liftF(Some(123)) + * scala> val wt: WriterT[Option, String, Int] = writer.tell("error").tell(" log") + * res0: WriterT[Option, String, Int] = WriterT(Some((error log,123))) + * + * scala> wt.listen + * res1: WriterT[Option, String, (Int,String)] = WriterT(Some((error log,(123,error log)))) + * }}} + */ def listen(implicit F: Functor[F]): WriterT[F, L, (V, L)] = WriterT(F.map(run) { case (l, v) => (l, (v, l)) }) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer: WriterT[Option, String, Int] = WriterT.liftF(Some(123)) + * scala> val wt: WriterT[Option, String, Int] = writer.tell("error") + * res0: WriterT[Option, String, Int] = WriterT(Some((error,123))) + * + * scala> val func = WriterT.liftF[Option, String, Int => List[Int]](Some(i => List(i))) + * scala> val func2 = func.tell("log") + * + * scala> wt.ap(func2) + * res1: WriterT[Option, String, List[Int]] = WriterT(Some((logerror,List(123)))) + * }}} + */ def ap[Z](f: WriterT[F, L, V => Z])(implicit F: Apply[F], L: Semigroup[L]): WriterT[F, L, Z] = WriterT(F.map2(f.run, run) { case ((l1, fvz), (l2, v)) => (L.combine(l1, l2), fvz(v)) }) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val wr1: WriterT[Option, String, Int] = WriterT.liftF(None) + * scala> val wr2 = wr1.tell("error") + * res0: WriterT[Option, String, Int] = WriterT(None) + * + * scala> wr2.map(_ * 2) + * res1: WriterT[Option, String, Int] = WriterT(None) + * + * scala> val wr3: WriterT[Option, String, Int] = WriterT.liftF(Some(456)) + * scala> val wr4 = wr3.tell("error") + * scala> wr4.map(_ * 2) + * res2: WriterT[Option, String, Int] = WriterT(Some((error,912))) + * }}} + */ def map[Z](fn: V => Z)(implicit functorF: Functor[F]): WriterT[F, L, Z] = WriterT { functorF.map(run) { z => @@ -33,6 +120,18 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { } } + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val wr1: WriterT[Option, String, Int] = WriterT.liftF(Some(123)) + * scala> val wr2 = wr1.tell("log...") + * scala> wr2.imap(_ * 2)(_ / 2) + * res0: WriterT[Option, String, Int] = WriterT(Some((log...,246))) + * }}} + */ def imap[Z](f: V => Z)(g: Z => V)(implicit F: Invariant[F]): WriterT[F, L, Z] = WriterT { F.imap(run)(z => (z._1, f(z._2)))(z => (z._1, g(z._2))) @@ -40,6 +139,20 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { /** * Modify the context `F` using transformation `f`. + * + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.arrow.FunctionK + * scala> import cats.implicits._ + * + * scala> val optionWriter = WriterT.liftF[Option, String, Int](Some(123)).tell("log") + * res0: WriterT[Option, String, Int](Some((log,123))) + * + * scala> def toList[A](option: Option[A]): List[A] = option.toList + * scala> val listWriter = optionWriter.mapK(FunctionK.lift(toList _)) + * res1: WriterT[List, String, Int](List((log,123))) + * }}} */ def mapK[G[_]](f: F ~> G): WriterT[G, L, V] = WriterT[G, L, V](f(run)) @@ -51,6 +164,20 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { } } + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val wr1 = WriterT.liftF[Option, String, Int](Some(123)).tell("error") + * res0: WriterT[Option, String, Int] = WriterT(Some(error,123)) + * scala> val func = (i:Int) => WriterT.liftF[Option, String, Int](Some(i * 2)).tell(i.show) + * + * scala> wr1.flatMap(func) + * res1: WriterT[Option, String, Int] = WriterT(Some((error123,246))) + * }}} + */ def flatMap[U](f: V => WriterT[F, L, U])(implicit flatMapF: FlatMap[F], semigroupL: Semigroup[L]): WriterT[F, L, U] = WriterT { flatMapF.flatMap(run) { lv => @@ -60,29 +187,137 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { } } + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val wr1 = WriterT.liftF[Option, String, Int](Some(123)).tell("quack") + * res0: WriterT[Option, String, Int] = WriterT(Some(quack,123)) + * + * scala> wr1.mapBoth((s,i) => (s + " " + s, i * 2)) + * res1: WriterT[Option, String, Int] = WriterT(Some((quack quack,246))) + * }}} + */ def mapBoth[M, U](f: (L, V) => (M, U))(implicit functorF: Functor[F]): WriterT[F, M, U] = WriterT { functorF.map(run)(f.tupled) } + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val wr1 = WriterT.liftF[Option, String, Int](Some(123)).tell("456") + * res0: WriterT[Option, String, Int] = WriterT(Some(456,123)) + * + * scala> wr1.bimap(_.toInt, _.show) + * res1: WriterT[Option, Int, String] = WriterT(Some((456,123))) + * }}} + */ def bimap[M, U](f: L => M, g: V => U)(implicit functorF: Functor[F]): WriterT[F, M, U] = mapBoth((l, v) => (f(l), g(v))) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, String, Int](Some(246)).tell("error") + * res0: WriterT[Option, String, Int] = WriterT(Some((error,246))) + * + * scala> writer.mapWritten(i => List(i)) + * res1: WriterT[Option, List[String], Int] = WriterT(Some((List(error),246))) + * }}} + */ def mapWritten[M](f: L => M)(implicit functorF: Functor[F]): WriterT[F, M, V] = mapBoth((l, v) => (f(l), v)) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, String, Int](Some(123)).tell("log") + * scala> writer.swap + * res0: WriterT[Option, Int, String] = WriterT(Some((123,log))) + * }}} + */ def swap(implicit functorF: Functor[F]): WriterT[F, V, L] = mapBoth((l, v) => (v, l)) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, String, Int](Some(123)).tell("error") + * scala> writer.reset + * res0: WriterT[Option, String, Int] = WriterT(Some((,123))) + * }}} + */ def reset(implicit monoidL: Monoid[L], functorF: Functor[F]): WriterT[F, L, V] = mapWritten(_ => monoidL.empty) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, String, Int](Some(456)).tell("log...") + * scala> writer.show + * res0: String = Some((log...,456)) + * }}} + */ def show(implicit F: Show[F[(L, V)]]): String = F.show(run) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, String, Int](Some(123)).tell("hi") + * scala> writer.foldLeft(456)((acc,v) => acc + v) + * res0: Int = 579 + * }}} + */ def foldLeft[C](c: C)(f: (C, V) => C)(implicit F: Foldable[F]): C = F.foldLeft(run, c)((z, lv) => f(z, lv._2)) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.Eval + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, String, Int](Some(123)).tell("hi") + * scala> writer + * | .foldRight(Eval.now(456))((v,c) => c.map(_ + v)) + * | .value + * res0: Int = 579 + * }}} + */ def foldRight[C](lc: Eval[C])(f: (V, Eval[C]) => Eval[C])(implicit F: Foldable[F]): Eval[C] = F.foldRight(run, lc)((lv, z) => f(lv._2, z)) + /** + * Example: + * {{{ + * scala> import cats.data.WriterT + * scala> import cats.implicits._ + * + * scala> val writer = WriterT.liftF[Option, String, Int](Some(123)).tell("hi") + * scala> writer.traverse[List,Int](i => List(i)) + * res0: List[WriterT[Option, String, Int]] = List(WriterT(Some((hi,123)))) + * }}} + */ def traverse[G[_], V1](f: V => G[V1])(implicit F: Traverse[F], G: Applicative[G]): G[WriterT[F, L, V1]] = G.map( F.traverse(run)(lv => G.tupleLeft(f(lv._2), lv._1)) diff --git a/core/src/main/scala/cats/instances/either.scala b/core/src/main/scala/cats/instances/either.scala index 572b5df3c8..ff2025a146 100644 --- a/core/src/main/scala/cats/instances/either.scala +++ b/core/src/main/scala/cats/instances/either.scala @@ -1,8 +1,11 @@ package cats package instances +import cats.data.Validated +import cats.kernel.Semigroup import cats.syntax.EitherUtil import cats.syntax.either._ + import scala.annotation.tailrec trait EitherInstances extends cats.kernel.instances.EitherInstances { @@ -158,4 +161,18 @@ trait EitherInstances extends cats.kernel.instances.EitherInstances { case Right(b) => "Right(" + B.show(b) + ")" } } + + implicit def catsParallelForEitherAndValidated[E: Semigroup]: Parallel.Aux[Either[E, *], Validated[E, *]] = + new Parallel[Either[E, *]] { + type F[x] = Validated[E, x] + + def applicative: Applicative[Validated[E, *]] = Validated.catsDataApplicativeErrorForValidated + def monad: Monad[Either[E, *]] = cats.instances.either.catsStdInstancesForEither + + def sequential: Validated[E, *] ~> Either[E, *] = + λ[Validated[E, *] ~> Either[E, *]](_.toEither) + + def parallel: Either[E, *] ~> Validated[E, *] = + λ[Either[E, *] ~> Validated[E, *]](_.toValidated) + } } diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index 975fa14519..d93cadfafe 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -1,6 +1,7 @@ package cats package instances +import cats.data.ZipList import cats.syntax.show._ import scala.annotation.tailrec @@ -150,6 +151,20 @@ trait ListInstances extends cats.kernel.instances.ListInstances { def show(fa: List[A]): String = fa.iterator.map(_.show).mkString("List(", ", ", ")") } + + implicit def catsStdNonEmptyParallelForListZipList: NonEmptyParallel.Aux[List, ZipList] = + new NonEmptyParallel[List] { + type F[x] = ZipList[x] + + def flatMap: FlatMap[List] = cats.instances.list.catsStdInstancesForList + def apply: Apply[ZipList] = ZipList.catsDataCommutativeApplyForZipList + + def sequential: ZipList ~> List = + λ[ZipList ~> List](_.value) + + def parallel: List ~> ZipList = + λ[List ~> ZipList](v => new ZipList(v)) + } } private[instances] trait ListInstancesBinCompat0 { diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 5c27e6f4b9..107efceca8 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -1,6 +1,7 @@ package cats package instances +import cats.data.ZipVector import cats.syntax.show._ import scala.annotation.tailrec @@ -120,6 +121,20 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { def show(fa: Vector[A]): String = fa.iterator.map(_.show).mkString("Vector(", ", ", ")") } + + implicit def catsStdNonEmptyParallelForVectorZipVector: NonEmptyParallel.Aux[Vector, ZipVector] = + new NonEmptyParallel[Vector] { + type F[x] = ZipVector[x] + + def flatMap: FlatMap[Vector] = cats.instances.vector.catsStdInstancesForVector + def apply: Apply[ZipVector] = ZipVector.catsDataCommutativeApplyForZipVector + + def sequential: ZipVector ~> Vector = + λ[ZipVector ~> Vector](_.value) + + def parallel: Vector ~> ZipVector = + λ[Vector ~> ZipVector](v => new ZipVector(v)) + } } private[instances] trait VectorInstancesBinCompat0 { diff --git a/project/plugins.sbt b/project/plugins.sbt index f280beadac..b827780ea3 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,8 +6,8 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.0") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.9.5") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.7") -addSbtPlugin("com.47deg" % "sbt-microsites" % "0.9.6") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.8") +addSbtPlugin("com.47deg" % "sbt-microsites" % "0.9.7") addSbtPlugin("org.lyranthe.sbt" % "partial-unification" % "1.1.2") addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.6.13") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.1") diff --git a/tests/src/test/scala-2.13+/cats/tests/LazyListSuite.scala b/tests/src/test/scala-2.13+/cats/tests/LazyListSuite.scala index 5392187a2c..b8d56f6f10 100644 --- a/tests/src/test/scala-2.13+/cats/tests/LazyListSuite.scala +++ b/tests/src/test/scala-2.13+/cats/tests/LazyListSuite.scala @@ -13,6 +13,7 @@ import cats.laws.discipline.{ } import cats.data.ZipLazyList import cats.laws.discipline.arbitrary._ +import org.scalatest.funsuite.AnyFunSuiteLike class LazyListSuite extends CatsSuite { checkAll("LazyList[Int]", SemigroupalTests[LazyList].semigroupal[Int, Int, Int]) @@ -59,3 +60,13 @@ class LazyListSuite extends CatsSuite { } } + +final class LazyListInstancesSuite extends AnyFunSuiteLike { + + test("parallel instance in cats.instances.lazyList") { + import cats.instances.lazyList._ + import cats.syntax.parallel._ + + (LazyList(1, 2, 3), LazyList("A", "B", "C")).parTupled + } +} diff --git a/tests/src/test/scala-2.13+/cats/tests/ScalaVersionSpecific.scala b/tests/src/test/scala-2.13+/cats/tests/ScalaVersionSpecific.scala index 43c3230dd9..76af75af33 100644 --- a/tests/src/test/scala-2.13+/cats/tests/ScalaVersionSpecific.scala +++ b/tests/src/test/scala-2.13+/cats/tests/ScalaVersionSpecific.scala @@ -123,11 +123,9 @@ trait ScalaVersionSpecificParallelSuite { self: ParallelSuite => } // Can't test Parallel here, as Applicative[ZipLazyList].pure doesn't terminate - checkAll("Parallel[LazyList]", - NonEmptyParallelTests[LazyList].nonEmptyParallel[Int, String]) + checkAll("Parallel[LazyList]", NonEmptyParallelTests[LazyList].nonEmptyParallel[Int, String]) - checkAll("Parallel[NonEmptyLazyList]", - ParallelTests[NonEmptyLazyList].parallel[Int, String]) + checkAll("Parallel[NonEmptyLazyList]", ParallelTests[NonEmptyLazyList].parallel[Int, String]) } trait ScalaVersionSpecificRegressionSuite { self: RegressionSuite => diff --git a/tests/src/test/scala/cats/tests/CatsSuite.scala b/tests/src/test/scala/cats/tests/CatsSuite.scala index 9467b92d6b..ff691f95b8 100644 --- a/tests/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/src/test/scala/cats/tests/CatsSuite.scala @@ -6,7 +6,7 @@ import cats.platform.Platform import cats.syntax._ import org.scalactic.anyvals.{PosInt, PosZDouble, PosZInt} import org.scalatest.funsuite.AnyFunSuiteLike -import org.scalatest.Matchers +import org.scalatest.matchers.should.Matchers import org.scalatest.prop.Configuration import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import org.typelevel.discipline.scalatest.Discipline diff --git a/tests/src/test/scala/cats/tests/EitherSuite.scala b/tests/src/test/scala/cats/tests/EitherSuite.scala index 7dff18ad7e..3544434b71 100644 --- a/tests/src/test/scala/cats/tests/EitherSuite.scala +++ b/tests/src/test/scala/cats/tests/EitherSuite.scala @@ -4,6 +4,8 @@ package tests import cats.data.{EitherT, NonEmptyChain, NonEmptyList, NonEmptySet, Validated} import cats.laws.discipline._ import cats.kernel.laws.discipline.{EqTests, MonoidTests, OrderTests, PartialOrderTests, SemigroupTests} +import org.scalatest.funsuite.AnyFunSuiteLike + import scala.util.Try class EitherSuite extends CatsSuite { @@ -361,7 +363,18 @@ class EitherSuite extends CatsSuite { either.leftFlatMap(f) should ===(either.swap.flatMap(a => f(a).swap).swap) } } +} + +final class EitherInstancesSuite extends AnyFunSuiteLike { + test("parallel instance in cats.instances.either") { + import cats.instances.either._ + import cats.instances.string._ + import cats.syntax.parallel._ + + def either: Either[String, Int] = Left("Test") + (either, either).parTupled + } } @deprecated("To test deprecated methods", "2.1.0") diff --git a/tests/src/test/scala/cats/tests/ListSuite.scala b/tests/src/test/scala/cats/tests/ListSuite.scala index c5301365f1..b5bb2061d9 100644 --- a/tests/src/test/scala/cats/tests/ListSuite.scala +++ b/tests/src/test/scala/cats/tests/ListSuite.scala @@ -13,6 +13,7 @@ import cats.laws.discipline.{ TraverseTests } import cats.laws.discipline.arbitrary._ +import org.scalatest.funsuite.AnyFunSuiteLike class ListSuite extends CatsSuite { @@ -60,3 +61,13 @@ class ListSuite extends CatsSuite { } } } + +final class ListInstancesSuite extends AnyFunSuiteLike { + + test("NonEmptyParallel instance in cats.instances.list") { + import cats.instances.list._ + import cats.syntax.parallel._ + + (List(1, 2, 3), List("A", "B", "C")).parTupled + } +} diff --git a/tests/src/test/scala/cats/tests/ParallelSuite.scala b/tests/src/test/scala/cats/tests/ParallelSuite.scala index d1b4168aac..8d052df629 100644 --- a/tests/src/test/scala/cats/tests/ParallelSuite.scala +++ b/tests/src/test/scala/cats/tests/ParallelSuite.scala @@ -493,7 +493,6 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest with Sc trait ApplicativeErrorForEitherTest extends AnyFunSuiteLike with Discipline { import cats.instances.either._ - import cats.instances.parallel._ import cats.instances.string._ import cats.instances.int._ import cats.instances.unit._ diff --git a/tests/src/test/scala/cats/tests/SemigroupSuite.scala b/tests/src/test/scala/cats/tests/SemigroupSuite.scala index 09bbffeedf..aff7fa3658 100644 --- a/tests/src/test/scala/cats/tests/SemigroupSuite.scala +++ b/tests/src/test/scala/cats/tests/SemigroupSuite.scala @@ -2,10 +2,11 @@ package cats package tests import org.scalatest._ +import org.scalatest.matchers.should.Matchers import org.scalatest.funsuite._ -import org.scalatest.prop.GeneratorDrivenPropertyChecks +import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks -class SemigroupSuite extends AnyFunSuiteLike with Matchers with GeneratorDrivenPropertyChecks { +class SemigroupSuite extends AnyFunSuiteLike with Matchers with ScalaCheckDrivenPropertyChecks { { import cats.implicits._ Invariant[Semigroup] diff --git a/tests/src/test/scala/cats/tests/StreamSuite.scala b/tests/src/test/scala/cats/tests/StreamSuite.scala index e97d92dbbb..3a22bc4b9a 100644 --- a/tests/src/test/scala/cats/tests/StreamSuite.scala +++ b/tests/src/test/scala/cats/tests/StreamSuite.scala @@ -13,6 +13,7 @@ import cats.laws.discipline.{ } import cats.data.ZipStream import cats.laws.discipline.arbitrary._ +import org.scalatest.funsuite.AnyFunSuiteLike class StreamSuite extends CatsSuite { checkAll("Stream[Int]", SemigroupalTests[Stream].semigroupal[Int, Int, Int]) @@ -59,3 +60,13 @@ class StreamSuite extends CatsSuite { } } + +final class StreamInstancesSuite extends AnyFunSuiteLike { + + test("parallel instance in cats.instances.stream") { + import cats.instances.stream._ + import cats.syntax.parallel._ + + (Stream(1, 2, 3), Stream("A", "B", "C")).parTupled + } +} diff --git a/tests/src/test/scala/cats/tests/VectorSuite.scala b/tests/src/test/scala/cats/tests/VectorSuite.scala index 0f89646539..515b1cf52d 100644 --- a/tests/src/test/scala/cats/tests/VectorSuite.scala +++ b/tests/src/test/scala/cats/tests/VectorSuite.scala @@ -13,6 +13,7 @@ import cats.laws.discipline.{ TraverseTests } import cats.laws.discipline.arbitrary._ +import org.scalatest.funsuite.AnyFunSuiteLike class VectorSuite extends CatsSuite { checkAll("Vector[Int]", SemigroupalTests[Vector].semigroupal[Int, Int, Int]) @@ -55,3 +56,13 @@ class VectorSuite extends CatsSuite { assert(Vector.empty[Int].toNev == None) } } + +final class VectorInstancesSuite extends AnyFunSuiteLike { + + test("NonEmptyParallel instance in cats.instances.vector") { + import cats.instances.vector._ + import cats.syntax.parallel._ + + (Vector(1, 2, 3), Vector("A", "B", "C")).parTupled + } +}