Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ benchmarks/results
.metals
.bloop
.bundle
.semanticdb
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ language: scala

matrix:
include:
- jdk: openjdk9
scala: 0.22.0-bin-20200128-e7a0f80-NIGHTLY
env: COVERAGE=
- jdk: openjdk8
scala: 2.13.0
# We want the latest stable Scala last so sbt-travisci makes it the default
Expand Down
52 changes: 40 additions & 12 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import sbtcrossproject.crossProject
ThisBuild / organization := "org.typelevel"
ThisBuild / organizationName := "Typelevel"
ThisBuild / startYear := Some(2017)
ThisBuild / scalafixDependencies += "dev.travisbrown" %% "simulacrum-fix" % "0.1.0"

val CompileTime = config("CompileTime").hide
val SimulacrumVersion = "1.0.0"
val CatsVersion = "2.1.0"
val DisciplineScalatestVersion = "1.0.0"

Expand All @@ -35,9 +35,10 @@ addCommandAlias("release", ";project root ;reload ;+publish ;sonatypeReleaseAll
val commonSettings = Seq(
scalacOptions ++= PartialFunction
.condOpt(CrossVersion.partialVersion(scalaVersion.value)) {
case Some((2, n)) if n >= 13 =>
// Necessary for simulacrum
Seq("-Ymacro-annotations")
case Some((2, n)) if n < 13 =>
Seq("-Ypartial-unification")
case Some((0, _)) =>
Set("-Ykind-projector")
}
.toList
.flatten,
Expand All @@ -58,7 +59,13 @@ val commonSettings = Seq(
scalacOptions in (Compile, doc) ++=
Opts.doc.title("cats-effect"),
scalacOptions in Test += "-Yrangepos",
scalacOptions in Test += "-language:implicitConversions",
scalacOptions in Test ~= (_.filterNot(Set("-Wvalue-discard", "-Ywarn-value-discard"))),
libraryDependencies ++= (if (isDotty.value) Nil else Seq(compilerPlugin(scalafixSemanticdb))),
scalacOptions ++= (if (isDotty.value) Nil
else Seq(s"-P:semanticdb:targetroot:${baseDirectory.value}/.semanticdb", "-Yrangepos")),
libraryDependencies += ("dev.travisbrown" %% "simulacrum-annotation" % "0.1.0")
.withDottyCompat(scalaVersion.value),
// Disable parallel execution in tests; otherwise we cannot test System.err
parallelExecution in Test := false,
parallelExecution in IntegrationTest := false,
Expand Down Expand Up @@ -136,7 +143,11 @@ val commonSettings = Seq(
}
}).transform(node).head
},
addCompilerPlugin(("org.typelevel" %% "kind-projector" % "0.11.0").cross(CrossVersion.full)),
libraryDependencies ++= (if (isDotty.value) Nil
else
Seq(
compilerPlugin(("org.typelevel" %% "kind-projector" % "0.11.0").cross(CrossVersion.full))
)),
mimaFailOnNoPrevious := false
)

Expand Down Expand Up @@ -230,10 +241,19 @@ lazy val core = crossProject(JSPlatform, JVMPlatform)
.settings(
name := "cats-effect",
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-core" % CatsVersion,
"org.typelevel" %%% "simulacrum" % SimulacrumVersion % CompileTime,
"org.typelevel" %%% "cats-laws" % CatsVersion % Test,
"org.typelevel" %%% "discipline-scalatest" % DisciplineScalatestVersion % Test
"org.typelevel" %%% "cats-core" % CatsVersion
).map(_.withDottyCompat(scalaVersion.value)),
libraryDependencies += ("org.typelevel" %%% "cats-laws" % CatsVersion % Test).withDottyCompat(scalaVersion.value),
libraryDependencies ++= (
if (isDotty.value)
Seq(
"dev.travisbrown" %% "discipline-scalatest" % (DisciplineScalatestVersion + "-20200123-9982f0d-NIGHTLY") % Test
)
else
Seq(
"org.scalatestplus" %%% "scalacheck-1-14" % "3.1.0.1" % Test,
"org.typelevel" %%% "discipline-scalatest" % DisciplineScalatestVersion % Test
)
),
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
Expand Down Expand Up @@ -262,9 +282,17 @@ lazy val laws = crossProject(JSPlatform, JVMPlatform)
.settings(commonSettings: _*)
.settings(
name := "cats-effect-laws",
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-laws" % CatsVersion,
"org.typelevel" %%% "discipline-scalatest" % DisciplineScalatestVersion % Test
scalacOptions += "-language:implicitConversions",
libraryDependencies += ("org.typelevel" %%% "cats-laws" % CatsVersion).withDottyCompat(scalaVersion.value),
libraryDependencies ++= (
if (isDotty.value)
Seq(
"dev.travisbrown" %% "discipline-scalatest" % (DisciplineScalatestVersion + "-20200123-9982f0d-NIGHTLY") % Test
)
else
Seq(
"org.typelevel" %%% "discipline-scalatest" % DisciplineScalatestVersion % Test
)
)
)
.jvmConfigure(_.enablePlugins(AutomateHeaderPlugin))
Expand Down
42 changes: 38 additions & 4 deletions core/shared/src/main/scala/cats/effect/Async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import cats.effect.internals.TrampolineEC.immediate

import scala.annotation.implicitNotFound
import scala.concurrent.{ExecutionContext, Future}
import scala.language.implicitConversions
import scala.util.{Either, Failure, Success}

/**
Expand Down Expand Up @@ -96,10 +97,7 @@ import scala.util.{Either, Failure, Success}
* See the [[Concurrent]] alternative for that.
*/
@typeclass
@implicitNotFound("""Cannot find implicit value for Async[${F}].
Building this implicit value might depend on having an implicit
s.c.ExecutionContext in scope, a Scheduler, a ContextShift[${F}]
or some equivalent type.""")
@implicitNotFound("Could not find an instance of Async for ${F}")
trait Async[F[_]] extends Sync[F] with LiftIO[F] {
/**
* Creates a simple, non-cancelable `F[A]` instance that
Expand Down Expand Up @@ -509,4 +507,40 @@ object Async {
override def async[A](k: (Either[Throwable, A] => Unit) => Unit): ReaderWriterStateT[F, E, L, S, A] =
ReaderWriterStateT.liftF(F.async(k))
}

/****************************************************************************
* THE REST OF THIS OBJECT IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! *
****************************************************************************/
/**
* Summon an instance of [[Async]] for `F`.
*/
@inline def apply[F[_]](implicit instance: Async[F]): Async[F] = instance

trait Ops[F[_], A] {
type TypeClassType <: Async[F]
def self: F[A]
val typeClassInstance: TypeClassType
}
trait AllOps[F[_], A] extends Ops[F, A] with Sync.AllOps[F, A] with LiftIO.AllOps[F, A] {
type TypeClassType <: Async[F]
}
trait ToAsyncOps {
implicit def toAsyncOps[F[_], A](target: F[A])(implicit tc: Async[F]): Ops[F, A] {
type TypeClassType = Async[F]
} = new Ops[F, A] {
type TypeClassType = Async[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
object nonInheritedOps extends ToAsyncOps
object ops {
implicit def toAllAsyncOps[F[_], A](target: F[A])(implicit tc: Async[F]): AllOps[F, A] {
type TypeClassType = Async[F]
} = new AllOps[F, A] {
type TypeClassType = Async[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
}
4 changes: 3 additions & 1 deletion core/shared/src/main/scala/cats/effect/Blocker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ final class Blocker private (val blockingContext: ExecutionContext) extends AnyV
* `blockOn` as a natural transformation.
*/
def blockOnK[F[_]](implicit cs: ContextShift[F]): F ~> F =
λ[F ~> F](blockOn(_))
new (F ~> F) {
def apply[A](fa: F[A]): F[A] = blockOn(fa)
}
}

object Blocker extends BlockerPlatform {
Expand Down
66 changes: 53 additions & 13 deletions core/shared/src/main/scala/cats/effect/Concurrent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import cats.syntax.all._
import scala.annotation.implicitNotFound
import scala.concurrent.{Promise, TimeoutException}
import scala.concurrent.duration.FiniteDuration
import scala.language.implicitConversions
import scala.util.Either
import simulacrum.typeclass

Expand Down Expand Up @@ -192,10 +193,7 @@ import simulacrum.typeclass
* `acquire` and `release` operations are guaranteed to be uncancelable as well.
*/
@typeclass
@implicitNotFound("""Cannot find implicit value for Concurrent[${F}].
Building this implicit value might depend on having an implicit
s.c.ExecutionContext in scope, a Scheduler, a ContextShift[${F}]
or some equivalent type.""")
@implicitNotFound("Could not find an instance of Concurrent for ${F}")
trait Concurrent[F[_]] extends Async[F] {
/**
* Start concurrent execution of the source suspended in
Expand Down Expand Up @@ -245,7 +243,7 @@ trait Concurrent[F[_]] extends Async[F] {
* The same result can be achieved by using `anotherProcess &> longProcess` with the Parallel type class syntax.
*/
def background[A](fa: F[A]): Resource[F, F[A]] =
Resource.make(start(fa))(_.cancel)(this).map(_.join)(this)
Resource.make[F, Fiber[F, A]](start(fa))(_.cancel)(this).map[F, F[A]](_.join)(this)

/**
* Run two tasks concurrently, creating a race between them and returns a
Expand Down Expand Up @@ -328,7 +326,7 @@ trait Concurrent[F[_]] extends Async[F] {
* }}}
*/
def cancelable[A](k: (Either[Throwable, A] => Unit) => CancelToken[F]): F[A] =
Concurrent.defaultCancelable(k)(this)
Concurrent.defaultCancelable[F, A](k)(this)

/**
* Inherited from [[LiftIO]], defines a conversion from [[IO]]
Expand Down Expand Up @@ -614,7 +612,7 @@ object Concurrent {
case ExitCase.Completed | ExitCase.Error(_) =>
fiber.join.attempt.flatMap(f).attempt.flatMap(r.complete)
case _ =>
fiber.cancel >>
(fiber.cancel: F[Unit]) >>
r.complete(Left(new Exception("Continual fiber cancelled") with NoStackTrace))
}.attempt
}(_ => r.get.void)
Expand Down Expand Up @@ -698,14 +696,14 @@ object Concurrent {
case Left((value, fiberB)) =>
value match {
case Left(_) =>
fiberB.cancel.map(_ => value.asInstanceOf[Left[L, Nothing]])
F.map(fiberB.cancel)(_ => value.asInstanceOf[Left[L, Nothing]])
case Right(r) =>
F.pure(Right(Left((r, fiberT[B](fiberB)))))
}
case Right((fiberA, value)) =>
value match {
case Left(_) =>
fiberA.cancel.map(_ => value.asInstanceOf[Left[L, Nothing]])
F.map(fiberA.cancel)(_ => value.asInstanceOf[Left[L, Nothing]])
case Right(r) =>
F.pure(Right(Right((fiberT[A](fiberA), r))))
}
Expand Down Expand Up @@ -735,14 +733,14 @@ object Concurrent {
case Left((value, fiberB)) =>
value match {
case None =>
fiberB.cancel.map(_ => None)
F.map(fiberB.cancel)(_ => None)
case Some(r) =>
F.pure(Some(Left((r, fiberT[B](fiberB)))))
}
case Right((fiberA, value)) =>
value match {
case None =>
fiberA.cancel.map(_ => None)
F.map(fiberA.cancel)(_ => None)
case Some(r) =>
F.pure(Some(Right((fiberT[A](fiberA), r))))
}
Expand Down Expand Up @@ -827,7 +825,7 @@ object Concurrent {
case Left((value, fiberB)) =>
value match {
case l @ Ior.Left(_) =>
fiberB.cancel.map(_ => l)
F.map(fiberB.cancel)(_ => l)
case Ior.Right(r) =>
F.pure(Ior.Right(Left((r, fiberT[B](fiberB)))))
case Ior.Both(l, r) =>
Expand All @@ -836,7 +834,7 @@ object Concurrent {
case Right((fiberA, value)) =>
value match {
case l @ Ior.Left(_) =>
fiberA.cancel.map(_ => l)
F.map(fiberA.cancel)(_ => l)
case Ior.Right(r) =>
F.pure(Ior.Right(Right((fiberT[A](fiberA), r))))
case Ior.Both(l, r) =>
Expand Down Expand Up @@ -870,4 +868,46 @@ object Concurrent {
case _ => F.unit
}
}

/****************************************************************************
* THE REST OF THIS OBJECT IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! *
****************************************************************************/
/**
* Summon an instance of [[Concurrent]] for `F`.
*/
@inline def apply[F[_]](implicit instance: Concurrent[F]): Concurrent[F] = instance

trait Ops[F[_], A] {
type TypeClassType <: Concurrent[F]
def self: F[A]
val typeClassInstance: TypeClassType
def start: F[Fiber[F, A]] = typeClassInstance.start[A](self)
def background: Resource[F, F[A]] = typeClassInstance.background[A](self)
def racePair[B](fb: F[B]): F[Either[(A, Fiber[F, B]), (Fiber[F, A], B)]] =
typeClassInstance.racePair[A, B](self, fb)
def race[B](fb: F[B]): F[Either[A, B]] = typeClassInstance.race[A, B](self, fb)
def continual[B](f: Either[Throwable, A] => F[B]): F[B] = typeClassInstance.continual[A, B](self)(f)
}
trait AllOps[F[_], A] extends Ops[F, A] with Async.AllOps[F, A] {
type TypeClassType <: Concurrent[F]
}
trait ToConcurrentOps {
implicit def toConcurrentOps[F[_], A](target: F[A])(implicit tc: Concurrent[F]): Ops[F, A] {
type TypeClassType = Concurrent[F]
} = new Ops[F, A] {
type TypeClassType = Concurrent[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
object nonInheritedOps extends ToConcurrentOps
object ops {
implicit def toAllConcurrentOps[F[_], A](target: F[A])(implicit tc: Concurrent[F]): AllOps[F, A] {
type TypeClassType = Concurrent[F]
} = new AllOps[F, A] {
type TypeClassType = Concurrent[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
}
Loading