diff --git a/.gitignore b/.gitignore index bfb8f4612a..3eae41bf36 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ benchmarks/results .metals .bloop .bundle +.semanticdb diff --git a/.travis.yml b/.travis.yml index d45326164b..27dc6473a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/build.sbt b/build.sbt index 88b256ddcc..5081db04af 100644 --- a/build.sbt +++ b/build.sbt @@ -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" @@ -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, @@ -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, @@ -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 ) @@ -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 { @@ -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)) diff --git a/core/shared/src/main/scala/cats/effect/Async.scala b/core/shared/src/main/scala/cats/effect/Async.scala index 5d5df0d050..24873ca93f 100644 --- a/core/shared/src/main/scala/cats/effect/Async.scala +++ b/core/shared/src/main/scala/cats/effect/Async.scala @@ -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} /** @@ -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 @@ -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 + } + } } diff --git a/core/shared/src/main/scala/cats/effect/Blocker.scala b/core/shared/src/main/scala/cats/effect/Blocker.scala index 52aaede4e7..0c49c485b6 100644 --- a/core/shared/src/main/scala/cats/effect/Blocker.scala +++ b/core/shared/src/main/scala/cats/effect/Blocker.scala @@ -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 { diff --git a/core/shared/src/main/scala/cats/effect/Concurrent.scala b/core/shared/src/main/scala/cats/effect/Concurrent.scala index 35a433ec6f..90da5f4b26 100644 --- a/core/shared/src/main/scala/cats/effect/Concurrent.scala +++ b/core/shared/src/main/scala/cats/effect/Concurrent.scala @@ -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 @@ -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 @@ -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 @@ -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]] @@ -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) @@ -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)))) } @@ -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)))) } @@ -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) => @@ -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) => @@ -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 + } + } } diff --git a/core/shared/src/main/scala/cats/effect/ConcurrentEffect.scala b/core/shared/src/main/scala/cats/effect/ConcurrentEffect.scala index c5a61ca07c..7ff973bcdc 100644 --- a/core/shared/src/main/scala/cats/effect/ConcurrentEffect.scala +++ b/core/shared/src/main/scala/cats/effect/ConcurrentEffect.scala @@ -20,6 +20,7 @@ package effect import simulacrum._ import cats.data.{EitherT, WriterT} import scala.annotation.implicitNotFound +import scala.language.implicitConversions import scala.util.Either /** @@ -35,10 +36,7 @@ import scala.util.Either * Note this is the safe and generic version of [[IO.unsafeRunCancelable]]. */ @typeclass -@implicitNotFound("""Cannot find implicit value for ConcurrentEffect[${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 ConcurrentEffect for ${F}") trait ConcurrentEffect[F[_]] extends Concurrent[F] with Effect[F] { /** * Evaluates `F[_]` with the ability to cancel it. @@ -93,7 +91,7 @@ object ConcurrentEffect { override def runCancelable[A]( fa: EitherT[F, Throwable, A] )(cb: Either[Throwable, A] => IO[Unit]): SyncIO[CancelToken[EitherT[F, Throwable, *]]] = - F.runCancelable(fa.value)(cb.compose(_.flatMap(x => x))).map(EitherT.liftF(_)(F)) + F.runCancelable(fa.value)(cb.compose(_.flatMap(x => x))).map(EitherT.liftF[F, Throwable, Unit](_)(F)) } private[effect] trait WriterTConcurrentEffect[F[_], L] @@ -106,6 +104,44 @@ object ConcurrentEffect { override def runCancelable[A]( fa: WriterT[F, L, A] )(cb: Either[Throwable, A] => IO[Unit]): SyncIO[CancelToken[WriterT[F, L, *]]] = - F.runCancelable(fa.run)(cb.compose(_.map(_._2))).map(WriterT.liftF(_)(L, F)) + F.runCancelable(fa.run)(cb.compose(_.map(_._2))).map(WriterT.liftF[F, L, Unit](_)(L, F)) + } + + /**************************************************************************** + * THE REST OF THIS OBJECT IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! * + ****************************************************************************/ + /** + * Summon an instance of [[ConcurrentEffect]] for `F`. + */ + @inline def apply[F[_]](implicit instance: ConcurrentEffect[F]): ConcurrentEffect[F] = instance + + trait Ops[F[_], A] { + type TypeClassType <: ConcurrentEffect[F] + def self: F[A] + val typeClassInstance: TypeClassType + def runCancelable(cb: Either[Throwable, A] => IO[Unit]): SyncIO[CancelToken[F]] = + typeClassInstance.runCancelable[A](self)(cb) + } + trait AllOps[F[_], A] extends Ops[F, A] with Concurrent.AllOps[F, A] with Effect.AllOps[F, A] { + type TypeClassType <: ConcurrentEffect[F] + } + trait ToConcurrentEffectOps { + implicit def toConcurrentEffectOps[F[_], A](target: F[A])(implicit tc: ConcurrentEffect[F]): Ops[F, A] { + type TypeClassType = ConcurrentEffect[F] + } = new Ops[F, A] { + type TypeClassType = ConcurrentEffect[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } + object nonInheritedOps extends ToConcurrentEffectOps + object ops { + implicit def toAllConcurrentEffectOps[F[_], A](target: F[A])(implicit tc: ConcurrentEffect[F]): AllOps[F, A] { + type TypeClassType = ConcurrentEffect[F] + } = new AllOps[F, A] { + type TypeClassType = ConcurrentEffect[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } } } diff --git a/core/shared/src/main/scala/cats/effect/ContextShift.scala b/core/shared/src/main/scala/cats/effect/ContextShift.scala index e35b7e57ba..200f858398 100644 --- a/core/shared/src/main/scala/cats/effect/ContextShift.scala +++ b/core/shared/src/main/scala/cats/effect/ContextShift.scala @@ -84,7 +84,10 @@ object ContextShift { /** * `evalOn` as a natural transformation. */ - def evalOnK[F[_]](ec: ExecutionContext)(implicit cs: ContextShift[F]): F ~> F = λ[F ~> F](cs.evalOn(ec)(_)) + def evalOnK[F[_]](ec: ExecutionContext)(implicit cs: ContextShift[F]): F ~> F = + new (F ~> F) { + def apply[A](fa: F[A]): F[A] = cs.evalOn(ec)(fa) + } /** * Derives a [[ContextShift]] instance for `cats.data.EitherT`, diff --git a/core/shared/src/main/scala/cats/effect/Effect.scala b/core/shared/src/main/scala/cats/effect/Effect.scala index a4668f9bd8..d438bbfdf5 100644 --- a/core/shared/src/main/scala/cats/effect/Effect.scala +++ b/core/shared/src/main/scala/cats/effect/Effect.scala @@ -20,6 +20,7 @@ package effect import simulacrum._ import cats.data.{EitherT, WriterT} import scala.annotation.implicitNotFound +import scala.language.implicitConversions import scala.util.Either /** @@ -36,10 +37,7 @@ import scala.util.Either * (aka Haskell's `unsafePerformIO`). */ @typeclass -@implicitNotFound("""Cannot find implicit value for Effect[${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 Effect for ${F}") trait Effect[F[_]] extends Async[F] { /** * Evaluates `F[_]`, with the effect of starting the run-loop @@ -74,7 +72,9 @@ object Effect { /** * [[Effect.toIO]] as a natural transformation. */ - def toIOK[F[_]](implicit F: Effect[F]): F ~> IO = λ[F ~> IO](F.toIO(_)) + def toIOK[F[_]](implicit F: Effect[F]): F ~> IO = new (F ~> IO) { + def apply[A](fa: F[A]): IO[A] = F.toIO(fa) + } /** * [[Effect]] instance built for `cats.data.EitherT` values initialized @@ -112,4 +112,42 @@ object Effect { override def toIO[A](fa: WriterT[F, L, A]): IO[A] = F.toIO(fa.value(F)) } + + /**************************************************************************** + * THE REST OF THIS OBJECT IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! * + ****************************************************************************/ + /** + * Summon an instance of [[Effect]] for `F`. + */ + @inline def apply[F[_]](implicit instance: Effect[F]): Effect[F] = instance + + trait Ops[F[_], A] { + type TypeClassType <: Effect[F] + def self: F[A] + val typeClassInstance: TypeClassType + def runAsync(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit] = typeClassInstance.runAsync[A](self)(cb) + def toIO: IO[A] = typeClassInstance.toIO[A](self) + } + trait AllOps[F[_], A] extends Ops[F, A] with Async.AllOps[F, A] { + type TypeClassType <: Effect[F] + } + trait ToEffectOps { + implicit def toEffectOps[F[_], A](target: F[A])(implicit tc: Effect[F]): Ops[F, A] { + type TypeClassType = Effect[F] + } = new Ops[F, A] { + type TypeClassType = Effect[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } + object nonInheritedOps extends ToEffectOps + object ops { + implicit def toAllEffectOps[F[_], A](target: F[A])(implicit tc: Effect[F]): AllOps[F, A] { + type TypeClassType = Effect[F] + } = new AllOps[F, A] { + type TypeClassType = Effect[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } } diff --git a/core/shared/src/main/scala/cats/effect/Fiber.scala b/core/shared/src/main/scala/cats/effect/Fiber.scala index 0f024d95cc..d5a525ec37 100644 --- a/core/shared/src/main/scala/cats/effect/Fiber.scala +++ b/core/shared/src/main/scala/cats/effect/Fiber.scala @@ -21,6 +21,8 @@ import cats.syntax.applicative._ import cats.syntax.flatMap._ import cats.{~>, Applicative, Apply, Monoid, Semigroup} +import scala.language.implicitConversions + /** * `Fiber` represents the (pure) result of an [[Async]] data type (e.g. [[IO]]) * being started concurrently and that can be either joined or canceled. diff --git a/core/shared/src/main/scala/cats/effect/IO.scala b/core/shared/src/main/scala/cats/effect/IO.scala index dbdfef9efc..073d21e6ed 100644 --- a/core/shared/src/main/scala/cats/effect/IO.scala +++ b/core/shared/src/main/scala/cats/effect/IO.scala @@ -386,7 +386,7 @@ sealed abstract class IO[+A] extends internals.IOBinaryCompat[A] { * @see [[cats.effect.Concurrent#background]] for the generic version. */ final def background(implicit cs: ContextShift[IO]): Resource[IO, IO[A @uncheckedVariance]] = - Resource.make(start)(_.cancel).map(_.join) + Resource.make[IO, Fiber[IO, A]](start)(_.cancel).map(_.join) /** * Makes the source `IO` uninterruptible such that a [[Fiber.cancel]] @@ -886,9 +886,13 @@ abstract private[effect] class IOInstances extends IOLowPriorityInstances { final override val monad: Monad[IO] = ioConcurrentEffect(cs) - final override val sequential: IO.Par ~> IO = λ[IO.Par ~> IO](IO.Par.unwrap(_)) + final override val sequential: IO.Par ~> IO = new (IO.Par ~> IO) { + def apply[A](a: IO.Par[A]): IO[A] = IO.Par.unwrap(a) + } - final override val parallel: IO ~> IO.Par = λ[IO ~> IO.Par](IO.Par(_)) + final override val parallel: IO ~> IO.Par = new (IO ~> IO.Par) { + def apply[A](a: IO[A]): IO.Par[A] = IO.Par(a) + } } implicit def ioMonoid[A: Monoid]: Monoid[IO[A]] = new IOSemigroup[A] with Monoid[IO[A]] { diff --git a/core/shared/src/main/scala/cats/effect/LiftIO.scala b/core/shared/src/main/scala/cats/effect/LiftIO.scala index f2adb0badf..43d921a423 100644 --- a/core/shared/src/main/scala/cats/effect/LiftIO.scala +++ b/core/shared/src/main/scala/cats/effect/LiftIO.scala @@ -22,12 +22,11 @@ import simulacrum._ import cats.data.{EitherT, IorT, Kleisli, OptionT, ReaderWriterStateT, StateT, WriterT} import scala.annotation.implicitNotFound +import scala.language.implicitConversions @typeclass -@implicitNotFound("""Cannot find implicit value for LiftIO[${F}]. -Building this implicit value might depend on having an implicit -s.c.ExecutionContext in scope, a Scheduler or some equivalent type.""") -trait LiftIO[F[_]] { +@implicitNotFound("Could not find an instance of LiftIO for ${F}") +trait LiftIO[F[_]] extends Serializable { def liftIO[A](ioa: IO[A]): F[A] } @@ -35,7 +34,9 @@ object LiftIO { /** * [[LiftIO.liftIO]] as a natural transformation. */ - def liftK[F[_]: LiftIO]: IO ~> F = λ[IO ~> F](_.to[F]) + def liftK[F[_]: LiftIO]: IO ~> F = new (IO ~> F) { + def apply[A](a: IO[A]): F[A] = a.to[F] + } /** * [[LiftIO]] instance built for `cats.data.EitherT` values initialized @@ -143,4 +144,38 @@ object LiftIO { override def liftIO[A](ioa: IO[A]): ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.liftF(F.liftIO(ioa))(FA, L) } + + /**************************************************************************** + * THE REST OF THIS OBJECT IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! * + ****************************************************************************/ + /** + * Summon an instance of [[LiftIO]] for `F`. + */ + @inline def apply[F[_]](implicit instance: LiftIO[F]): LiftIO[F] = instance + + trait Ops[F[_], A] { + type TypeClassType <: LiftIO[F] + def self: F[A] + val typeClassInstance: TypeClassType + } + trait AllOps[F[_], A] extends Ops[F, A] + trait ToLiftIOOps { + implicit def toLiftIOOps[F[_], A](target: F[A])(implicit tc: LiftIO[F]): Ops[F, A] { + type TypeClassType = LiftIO[F] + } = new Ops[F, A] { + type TypeClassType = LiftIO[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } + object nonInheritedOps extends ToLiftIOOps + object ops { + implicit def toAllLiftIOOps[F[_], A](target: F[A])(implicit tc: LiftIO[F]): AllOps[F, A] { + type TypeClassType = LiftIO[F] + } = new AllOps[F, A] { + type TypeClassType = LiftIO[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } } diff --git a/core/shared/src/main/scala/cats/effect/Resource.scala b/core/shared/src/main/scala/cats/effect/Resource.scala index 6744dccffc..656f2e4a0a 100644 --- a/core/shared/src/main/scala/cats/effect/Resource.scala +++ b/core/shared/src/main/scala/cats/effect/Resource.scala @@ -25,6 +25,7 @@ import cats.implicits._ import cats.effect.implicits._ import scala.annotation.tailrec +import scala.language.implicitConversions /** * The `Resource` is a data structure that captures the effectful @@ -181,8 +182,8 @@ sealed abstract class Resource[+F[_], +A] { Resource .make(bothFinalizers)(_.get.flatMap(_.parTupled).void) .evalMap { store => - val leftStore: Update = f => store.update(_.leftMap(f)) - val rightStore: Update = f => store.update(_.map(f)) + val leftStore: Update = f => store.update { case (a, b) => (f(a), b) } + val rightStore: Update = f => store.update { case (a, b) => (a, f(b)) } (allocate(this, leftStore), allocate(that, rightStore)).parTupled } @@ -385,7 +386,9 @@ object Resource extends ResourceInstances with ResourcePlatform { * Lifts an applicative into a resource as a `FunctionK`. The resource has a no-op release. */ def liftK[F[_]](implicit F: Applicative[F]): F ~> Resource[F, *] = - λ[F ~> Resource[F, *]](Resource.liftF(_)) + new (F ~> Resource[F, *]) { + def apply[A](fa: F[A]): Resource[F, A] = Resource.liftF(fa) + } /** * Creates a [[Resource]] by wrapping a Java @@ -557,7 +560,7 @@ abstract private[effect] class ResourceMonadError[F[_], E] extends ResourceMonad }) }) case Suspend(resource) => - Suspend(resource.attempt.map { + Suspend(F.attempt(resource).map { case Left(error) => Resource.pure[F, Either[E, A]](Left(error)) case Right(fa: Resource[F, A]) => attempt(fa) }) @@ -656,8 +659,12 @@ abstract private[effect] class ResourceParallel[F0[_]] extends Parallel[Resource final override val monad: Monad[Resource[F0, *]] = F1 final override val sequential: Resource.Par[F0, *] ~> Resource[F0, *] = - λ[Resource.Par[F0, *] ~> Resource[F0, *]](Resource.Par.unwrap(_)) + new (Resource.Par[F0, *] ~> Resource[F0, *]) { + def apply[A](rpa: Resource.Par[F0, A]): Resource[F0, A] = Resource.Par.unwrap(rpa) + } final override val parallel: Resource[F0, *] ~> Resource.Par[F0, *] = - λ[Resource[F0, *] ~> Resource.Par[F0, *]](Resource.Par(_)) + new (Resource[F0, *] ~> Resource.Par[F0, *]) { + def apply[A](ra: Resource[F0, A]): Resource.Par[F0, A] = Resource.Par(ra) + } } diff --git a/core/shared/src/main/scala/cats/effect/Sync.scala b/core/shared/src/main/scala/cats/effect/Sync.scala index fa13d1956e..6b59b598f6 100644 --- a/core/shared/src/main/scala/cats/effect/Sync.scala +++ b/core/shared/src/main/scala/cats/effect/Sync.scala @@ -22,11 +22,14 @@ import cats.data._ import cats.effect.concurrent.Ref import cats.syntax.all._ import cats.instances.tuple._ +import scala.annotation.implicitNotFound +import scala.language.implicitConversions /** * A monad that can suspend the execution of side effects * in the `F[_]` context. */ +@implicitNotFound("Could not find an instance of Sync for ${F}") @typeclass(excludeParents = List("Defer")) trait Sync[F[_]] extends Bracket[F, Throwable] with Defer[F] { /** @@ -423,4 +426,40 @@ object Sync { override def uncancelable[A](fa: ReaderWriterStateT[F, E, L, S, A]): ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT((e, s) => F.uncancelable(fa.run(e, s))) } + + /**************************************************************************** + * THE REST OF THIS OBJECT IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! * + ****************************************************************************/ + /** + * Summon an instance of [[Sync]] for `F`. + */ + @inline def apply[F[_]](implicit instance: Sync[F]): Sync[F] = instance + + trait Ops[F[_], A] { + type TypeClassType <: Sync[F] + def self: F[A] + val typeClassInstance: TypeClassType + } + trait AllOps[F[_], A] extends Ops[F, A] { + type TypeClassType <: Sync[F] + } + trait ToSyncOps { + implicit def toSyncOps[F[_], A](target: F[A])(implicit tc: Sync[F]): Ops[F, A] { + type TypeClassType = Sync[F] + } = new Ops[F, A] { + type TypeClassType = Sync[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } + object nonInheritedOps extends ToSyncOps + object ops { + implicit def toAllSyncOps[F[_], A](target: F[A])(implicit tc: Sync[F]): AllOps[F, A] { + type TypeClassType = Sync[F] + } = new AllOps[F, A] { + type TypeClassType = Sync[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } } diff --git a/core/shared/src/main/scala/cats/effect/SyncEffect.scala b/core/shared/src/main/scala/cats/effect/SyncEffect.scala index d0dfb0748a..bfed82c3b8 100644 --- a/core/shared/src/main/scala/cats/effect/SyncEffect.scala +++ b/core/shared/src/main/scala/cats/effect/SyncEffect.scala @@ -19,11 +19,14 @@ package effect import simulacrum._ import cats.data.{EitherT, WriterT} +import scala.annotation.implicitNotFound +import scala.language.implicitConversions /** * A monad that can suspend side effects into the `F` context and * that supports only synchronous lazy evaluation of these effects. */ +@implicitNotFound("Could not find an instance of SyncEffect for ${F}") @typeclass trait SyncEffect[F[_]] extends Sync[F] { /** @@ -70,4 +73,41 @@ object SyncEffect { def runSync[G[_], A](fa: WriterT[F, L, A])(implicit G: Sync[G]): G[A] = F.runSync(F.map(fa.run)(_._2)) } + + /**************************************************************************** + * THE REST OF THIS OBJECT IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! * + ****************************************************************************/ + /** + * Summon an instance of [[SyncEffect]] for `F`. + */ + @inline def apply[F[_]](implicit instance: SyncEffect[F]): SyncEffect[F] = instance + + trait Ops[F[_], A] { + type TypeClassType <: SyncEffect[F] + def self: F[A] + val typeClassInstance: TypeClassType + def runSync[G[_]](implicit G: Sync[G]): G[A] = typeClassInstance.runSync[G, A](self)(G) + } + trait AllOps[F[_], A] extends Ops[F, A] with Sync.AllOps[F, A] { + type TypeClassType <: SyncEffect[F] + } + trait ToSyncEffectOps { + implicit def toSyncEffectOps[F[_], A](target: F[A])(implicit tc: SyncEffect[F]): Ops[F, A] { + type TypeClassType = SyncEffect[F] + } = new Ops[F, A] { + type TypeClassType = SyncEffect[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } + object nonInheritedOps extends ToSyncEffectOps + object ops { + implicit def toAllSyncEffectOps[F[_], A](target: F[A])(implicit tc: SyncEffect[F]): AllOps[F, A] { + type TypeClassType = SyncEffect[F] + } = new AllOps[F, A] { + type TypeClassType = SyncEffect[F] + val self: F[A] = target + val typeClassInstance: TypeClassType = tc + } + } } diff --git a/core/shared/src/main/scala/cats/effect/concurrent/Ref.scala b/core/shared/src/main/scala/cats/effect/concurrent/Ref.scala index 0b6fa712bb..0bc8854447 100644 --- a/core/shared/src/main/scala/cats/effect/concurrent/Ref.scala +++ b/core/shared/src/main/scala/cats/effect/concurrent/Ref.scala @@ -28,6 +28,7 @@ import cats.syntax.functor._ import cats.syntax.bifunctor._ import scala.annotation.tailrec +import scala.language.implicitConversions /** * An asynchronous, concurrent mutable reference. diff --git a/core/shared/src/main/scala/cats/effect/concurrent/Semaphore.scala b/core/shared/src/main/scala/cats/effect/concurrent/Semaphore.scala index ef548588e5..04d55821ef 100644 --- a/core/shared/src/main/scala/cats/effect/concurrent/Semaphore.scala +++ b/core/shared/src/main/scala/cats/effect/concurrent/Semaphore.scala @@ -22,6 +22,7 @@ import cats.effect.concurrent.Semaphore.TransformedSemaphore import cats.implicits._ import scala.collection.immutable.Queue +import scala.language.implicitConversions /** * A purely functional semaphore. diff --git a/core/shared/src/main/scala/cats/effect/internals/IORunLoop.scala b/core/shared/src/main/scala/cats/effect/internals/IORunLoop.scala index eb8aa4ab63..b00acb5e3e 100644 --- a/core/shared/src/main/scala/cats/effect/internals/IORunLoop.scala +++ b/core/shared/src/main/scala/cats/effect/internals/IORunLoop.scala @@ -129,7 +129,11 @@ private[effect] object IORunLoop { if (conn ne old) { if (rcb ne null) rcb.contextSwitch(conn) if (restore ne null) - currentIO = Bind(next, new RestoreContext(old, restore)) + currentIO = Bind( + next, + new RestoreContext(old, + restore.asInstanceOf[(Any, Throwable, IOConnection, IOConnection) => IOConnection]) + ) } } diff --git a/core/shared/src/main/scala/cats/effect/internals/IOStart.scala b/core/shared/src/main/scala/cats/effect/internals/IOStart.scala index a8831bcf0d..a3a6de890e 100644 --- a/core/shared/src/main/scala/cats/effect/internals/IOStart.scala +++ b/core/shared/src/main/scala/cats/effect/internals/IOStart.scala @@ -19,6 +19,7 @@ package cats.effect.internals import cats.effect.{ContextShift, Fiber, IO} import cats.implicits._ import scala.concurrent.Promise +import scala.language.implicitConversions private[effect] object IOStart { /** diff --git a/core/shared/src/main/scala/cats/effect/syntax/AsyncSyntax.scala b/core/shared/src/main/scala/cats/effect/syntax/AsyncSyntax.scala index 6c91cf0881..e317e9d68b 100644 --- a/core/shared/src/main/scala/cats/effect/syntax/AsyncSyntax.scala +++ b/core/shared/src/main/scala/cats/effect/syntax/AsyncSyntax.scala @@ -18,6 +18,7 @@ package cats.effect.syntax import cats.effect.Async import cats.{Parallel, Traverse} +import scala.language.implicitConversions trait AsyncSyntax extends Async.ToAsyncOps { implicit def catsEffectSyntaxAsyncObj[F[_]](F: Async[F]): AsyncObjOps[F] = diff --git a/core/shared/src/main/scala/cats/effect/syntax/BracketSyntax.scala b/core/shared/src/main/scala/cats/effect/syntax/BracketSyntax.scala index 40028e75cd..f6d3e95c99 100644 --- a/core/shared/src/main/scala/cats/effect/syntax/BracketSyntax.scala +++ b/core/shared/src/main/scala/cats/effect/syntax/BracketSyntax.scala @@ -18,6 +18,8 @@ package cats.effect.syntax import cats.effect.{Bracket, ExitCase} +import scala.language.implicitConversions + trait BracketSyntax { implicit def catsEffectSyntaxBracket[F[_], A, E](fa: F[A])(implicit bracket: Bracket[F, E]): BracketOps[F, E, A] = { // Bracket instance here is required to ensure correct inference for E diff --git a/core/shared/src/main/scala/cats/effect/syntax/ConcurrentSyntax.scala b/core/shared/src/main/scala/cats/effect/syntax/ConcurrentSyntax.scala index 945d497c6c..fa4f9bac8a 100644 --- a/core/shared/src/main/scala/cats/effect/syntax/ConcurrentSyntax.scala +++ b/core/shared/src/main/scala/cats/effect/syntax/ConcurrentSyntax.scala @@ -17,9 +17,10 @@ package cats.effect.syntax import cats.{Parallel, Traverse} +import cats.effect.{Concurrent, Timer} import scala.concurrent.duration.FiniteDuration -import cats.effect.{Concurrent, Timer} +import scala.language.implicitConversions trait ConcurrentSyntax extends Concurrent.ToConcurrentOps { implicit def catsEffectSyntaxConcurrent[F[_], A](fa: F[A]): ConcurrentOps[F, A] = diff --git a/core/shared/src/main/scala/cats/effect/syntax/ParallelNSyntax.scala b/core/shared/src/main/scala/cats/effect/syntax/ParallelNSyntax.scala index a929d0baaf..1bafc0251d 100644 --- a/core/shared/src/main/scala/cats/effect/syntax/ParallelNSyntax.scala +++ b/core/shared/src/main/scala/cats/effect/syntax/ParallelNSyntax.scala @@ -20,6 +20,8 @@ import cats.effect.Concurrent import cats.effect.implicits._ import cats.{Monad, Parallel, Traverse} +import scala.language.implicitConversions + trait ParallelNSyntax { implicit final def catsSyntaxParallelTraverseNConcurrent[T[_]: Traverse, A]( ta: T[A] diff --git a/laws/shared/src/main/scala/cats/effect/laws/AsyncLaws.scala b/laws/shared/src/main/scala/cats/effect/laws/AsyncLaws.scala index e91f166719..cbbeac4197 100644 --- a/laws/shared/src/main/scala/cats/effect/laws/AsyncLaws.scala +++ b/laws/shared/src/main/scala/cats/effect/laws/AsyncLaws.scala @@ -84,7 +84,7 @@ trait AsyncLaws[F[_]] extends SyncLaws[F] { def neverIsDerivedFromAsync[A] = F.never[A] <-> F.async[A](_ => ()) - def asyncCanBeDerivedFromAsyncF[A](k: (Either[Throwable, A] => Unit) => Unit) = + def asyncCanBeDerivedFromAsyncF[A](k: (Either[Throwable, A] => Unit) => Unit): IsEq[F[A]] = F.async(k) <-> F.asyncF(cb => F.delay(k(cb))) def bracketReleaseIsCalledOnCompletedOrError[A, B](fa: F[A], b: B) = { diff --git a/laws/shared/src/main/scala/cats/effect/laws/ConcurrentLaws.scala b/laws/shared/src/main/scala/cats/effect/laws/ConcurrentLaws.scala index fbbe57601d..94c9bbd7ad 100644 --- a/laws/shared/src/main/scala/cats/effect/laws/ConcurrentLaws.scala +++ b/laws/shared/src/main/scala/cats/effect/laws/ConcurrentLaws.scala @@ -190,7 +190,7 @@ trait ConcurrentLaws[F[_]] extends AsyncLaws[F] { val received = F.racePair(fa, never).flatMap { case Left((a, fiberB)) => - fiberB.cancel.map(_ => a) + F.map(fiberB.cancel)(_ => a) case Right(_) => F.raiseError[A](new IllegalStateException("right")) } @@ -202,7 +202,7 @@ trait ConcurrentLaws[F[_]] extends AsyncLaws[F] { val received = F.racePair(never, fb).flatMap { case Right((fiberA, b)) => - fiberA.cancel.map(_ => b) + F.map(fiberA.cancel)(_ => b) case Left(_) => F.raiseError[B](new IllegalStateException("left")) } diff --git a/laws/shared/src/main/scala/cats/effect/laws/EffectLaws.scala b/laws/shared/src/main/scala/cats/effect/laws/EffectLaws.scala index ad9b11e29c..468ae67162 100644 --- a/laws/shared/src/main/scala/cats/effect/laws/EffectLaws.scala +++ b/laws/shared/src/main/scala/cats/effect/laws/EffectLaws.scala @@ -33,7 +33,7 @@ trait EffectLaws[F[_]] extends AsyncLaws[F] { def runAsyncRaiseErrorProducesLeftIO[A](e: Throwable) = { val lh = IO.async[Either[Throwable, A]] { cb => - F.runAsync(F.raiseError(e))(r => IO(cb(Right(r)))) + F.runAsync(F.raiseError[A](e))(r => IO(cb(Right(r)))) .unsafeRunSync() } lh <-> IO.pure(Left(e)) diff --git a/laws/shared/src/test/scala/cats/effect/IOTests.scala b/laws/shared/src/test/scala/cats/effect/IOTests.scala index 798e03c534..0198542531 100644 --- a/laws/shared/src/test/scala/cats/effect/IOTests.scala +++ b/laws/shared/src/test/scala/cats/effect/IOTests.scala @@ -29,12 +29,13 @@ import cats.laws._ import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ import org.scalacheck._ +import org.scalatest.Inside import scala.concurrent.{ExecutionContext, Future, Promise, TimeoutException} import scala.util.{Failure, Success, Try} import scala.concurrent.duration._ -class IOTests extends BaseTestsSuite { +class IOTests extends BaseTestsSuite with Inside { checkAllAsync("IO", implicit ec => { implicit val cs: ContextShift[IO] = ec.ioContextShift ConcurrentEffectTests[IO].concurrentEffect[Int, Int, Int] @@ -122,7 +123,7 @@ class IOTests extends BaseTestsSuite { val ioa = IO { throw Foo } - ioa.attempt.unsafeRunSync() should matchPattern { + inside(ioa.attempt.unsafeRunSync()) { case Left(Foo) => () } } @@ -142,7 +143,7 @@ class IOTests extends BaseTestsSuite { case object Foo extends Exception val e: Either[Throwable, Nothing] = Left(Foo) - IO.fromEither(e).attempt.unsafeRunSync() should matchPattern { + inside(IO.fromEither(e).attempt.unsafeRunSync()) { case Left(Foo) => () } } @@ -151,7 +152,7 @@ class IOTests extends BaseTestsSuite { case class Foo(x: Int) val e: Either[Throwable, Foo] = Right(Foo(1)) - IO.fromEither(e).attempt.unsafeRunSync() should matchPattern { + inside(IO.fromEither(e).attempt.unsafeRunSync()) { case Right(Foo(_)) => () } } @@ -160,7 +161,7 @@ class IOTests extends BaseTestsSuite { case object Foo extends Exception val t: Try[Nothing] = Failure(Foo) - IO.fromTry(t).attempt.unsafeRunSync() should matchPattern { + inside(IO.fromTry(t).attempt.unsafeRunSync()) { case Left(Foo) => () } } @@ -169,7 +170,7 @@ class IOTests extends BaseTestsSuite { case class Foo(x: Int) val t: Try[Foo] = Success(Foo(1)) - IO.fromTry(t).attempt.unsafeRunSync() should matchPattern { + inside(IO.fromTry(t).attempt.unsafeRunSync()) { case Right(Foo(_)) => () } } diff --git a/laws/shared/src/test/scala/cats/effect/InstancesTests.scala b/laws/shared/src/test/scala/cats/effect/InstancesTests.scala index 78435e1f0f..c0b2ae2854 100644 --- a/laws/shared/src/test/scala/cats/effect/InstancesTests.scala +++ b/laws/shared/src/test/scala/cats/effect/InstancesTests.scala @@ -30,7 +30,9 @@ class InstancesTests extends BaseTestsSuite { checkAllAsync( "StateT[IO, S, *]", implicit ec => { - val fromState = λ[State[Int, *] ~> StateT[IO, Int, *]](st => StateT(s => IO.pure(st.run(s).value))) + val fromState = new (State[Int, *] ~> StateT[IO, Int, *]) { + def apply[A](st: State[Int, A]): StateT[IO, Int, A] = StateT(s => IO.pure(st.run(s).value)) + } BracketTests[StateT[IO, Int, *], Throwable].bracketTrans[State[Int, *], Int, Int](fromState) } ) @@ -43,7 +45,10 @@ class InstancesTests extends BaseTestsSuite { checkAllAsync( "OptionT[IO, *]", implicit ec => { - val fromOption = λ[Option ~> OptionT[IO, *]](OptionT.fromOption(_)) + val fromOption = new (Option ~> OptionT[IO, *]) { + def apply[A](oa: Option[A]): OptionT[IO, A] = OptionT.fromOption(oa) + } + BracketTests[OptionT[IO, *], Throwable].bracketTrans[Option, Int, Int](fromOption) } ) @@ -66,7 +71,9 @@ class InstancesTests extends BaseTestsSuite { checkAllAsync( "EitherT[IO, Throwable, *]", implicit ec => { - val fromEither = λ[Either[Throwable, *] ~> EitherT[IO, Throwable, *]](EitherT.fromEither(_)) + val fromEither = new (Either[Throwable, *] ~> EitherT[IO, Throwable, *]) { + def apply[A](ea: Either[Throwable, A]): EitherT[IO, Throwable, A] = EitherT.fromEither(ea) + } BracketTests[EitherT[IO, Throwable, *], Throwable].bracketTrans[Either[Throwable, *], Int, Int](fromEither) } ) @@ -82,7 +89,9 @@ class InstancesTests extends BaseTestsSuite { checkAllAsync( "WriterT[IO, Int, *]", implicit ec => { - val fromWriter = λ[Writer[Int, *] ~> WriterT[IO, Int, *]](w => WriterT(IO.pure(w.run))) + val fromWriter = new (Writer[Int, *] ~> WriterT[IO, Int, *]) { + def apply[A](w: Writer[Int, A]): WriterT[IO, Int, A] = WriterT(IO.pure(w.run)) + } BracketTests[WriterT[IO, Int, *], Throwable].bracketTrans[Writer[Int, *], Int, Int](fromWriter) } ) @@ -95,7 +104,9 @@ class InstancesTests extends BaseTestsSuite { checkAllAsync( "IorT[IO, Int, *]", implicit ec => { - val fromIor = λ[Ior[Int, *] ~> IorT[IO, Int, *]](IorT.fromIor(_)) + val fromIor = new (Ior[Int, *] ~> IorT[IO, Int, *]) { + def apply[A](ia: Ior[Int, A]): IorT[IO, Int, A] = IorT.fromIor(ia) + } BracketTests[IorT[IO, Int, *], Throwable].bracketTrans[Ior[Int, *], Int, Int](fromIor) } ) @@ -106,9 +117,11 @@ class InstancesTests extends BaseTestsSuite { checkAllAsync( "ReaderWriterStateT[IO, S, *]", implicit ec => { - val fromReaderWriterState = λ[ReaderWriterState[Int, Int, Int, *] ~> ReaderWriterStateT[IO, Int, Int, Int, *]]( - st => ReaderWriterStateT((e, s) => IO.pure(st.run(e, s).value)) - ) + val fromReaderWriterState = + new (ReaderWriterState[Int, Int, Int, *] ~> ReaderWriterStateT[IO, Int, Int, Int, *]) { + def apply[A](st: ReaderWriterState[Int, Int, Int, A]): ReaderWriterStateT[IO, Int, Int, Int, A] = + ReaderWriterStateT((e, s) => IO.pure(st.run(e, s).value)) + } BracketTests[ReaderWriterStateT[IO, Int, Int, Int, *], Throwable] .bracketTrans[ReaderWriterState[Int, Int, Int, *], Int, Int](fromReaderWriterState) } diff --git a/laws/shared/src/test/scala/cats/effect/ResourceTests.scala b/laws/shared/src/test/scala/cats/effect/ResourceTests.scala index 12719a438c..8f0b4a15cb 100644 --- a/laws/shared/src/test/scala/cats/effect/ResourceTests.scala +++ b/laws/shared/src/test/scala/cats/effect/ResourceTests.scala @@ -36,14 +36,14 @@ class ResourceTests extends BaseTestsSuite { checkAllAsync( "Resource.Par[IO, *]", implicit ec => { - implicit val cs = ec.contextShift[IO] + implicit val cs: ContextShift[IO] = ec.ioContextShift CommutativeApplicativeTests[Resource.Par[IO, *]].commutativeApplicative[Int, Int, Int] } ) checkAllAsync( "Resource[IO, *]", implicit ec => { - implicit val cs = ec.contextShift[IO] + implicit val cs: ContextShift[IO] = ec.ioContextShift // do NOT inline this val; it causes the 2.13.0 compiler to crash for... reasons (see: scala/bug#11732) val module = ParallelTests[IO] @@ -250,7 +250,7 @@ class ResourceTests extends BaseTestsSuite { val resource = Resource.liftF(IO.unit) val prog = for { - res <- (release *> resource).allocated + res <- (release *> resource).allocated[IO, Unit] (_, close) = res _ <- IO(released.get() shouldBe false) _ <- close @@ -278,7 +278,7 @@ class ResourceTests extends BaseTestsSuite { val resource = Resource.liftF(IO.unit) val prog = for { - res <- ((release *> resource).mapK(takeAnInteger) *> plusOneResource).mapK(runWithTwo).allocated + res <- ((release *> resource).mapK(takeAnInteger) *> plusOneResource).mapK(runWithTwo).allocated[IO, Int] (_, close) = res _ <- IO(released.get() shouldBe false) _ <- close @@ -295,7 +295,7 @@ class ResourceTests extends BaseTestsSuite { } testAsync("parZip - releases resources in reverse order of acquisition") { implicit ec => - implicit val ctx = ec.contextShift[IO] + implicit val ctx: ContextShift[IO] = ec.ioContextShift // conceptually asserts that: // forAll (r: Resource[F, A]) then r <-> r.parZip(Resource.unit) <-> Resource.unit.parZip(r) @@ -309,15 +309,15 @@ class ResourceTests extends BaseTestsSuite { val unit = ().pure[Resource[IO, *]] val p = if (rhs) r.parZip(unit) else unit.parZip(r) - p.use(IO.pure).attempt.unsafeToFuture + p.use(IO.pure).attempt.unsafeToFuture() ec.tick() released <-> as.map(_._1) } } testAsync("parZip - parallel acquisition and release") { implicit ec => - implicit val timer = ec.timer[IO] - implicit val ctx = ec.contextShift[IO] + implicit val timer: Timer[IO] = ec.ioTimer + implicit val ctx: ContextShift[IO] = ec.ioContextShift var leftAllocated = false var rightAllocated = false @@ -361,8 +361,8 @@ class ResourceTests extends BaseTestsSuite { } testAsync("parZip - safety: lhs error during rhs interruptible region") { implicit ec => - implicit val timer = ec.timer[IO] - implicit val ctx = ec.contextShift[IO] + implicit val timer: Timer[IO] = ec.ioTimer + implicit val ctx: ContextShift[IO] = ec.ioContextShift var leftAllocated = false var rightAllocated = false @@ -416,8 +416,8 @@ class ResourceTests extends BaseTestsSuite { } testAsync("parZip - safety: rhs error during lhs uninterruptible region") { implicit ec => - implicit val timer = ec.timer[IO] - implicit val ctx = ec.contextShift[IO] + implicit val timer: Timer[IO] = ec.ioTimer + implicit val ctx: ContextShift[IO] = ec.ioContextShift var leftAllocated = false var rightAllocated = false diff --git a/laws/shared/src/test/scala/cats/effect/SyncIOTests.scala b/laws/shared/src/test/scala/cats/effect/SyncIOTests.scala index 984687ac2d..1ec9517c4a 100644 --- a/laws/shared/src/test/scala/cats/effect/SyncIOTests.scala +++ b/laws/shared/src/test/scala/cats/effect/SyncIOTests.scala @@ -25,8 +25,9 @@ import cats.kernel.laws.discipline.MonoidTests import cats.laws._ import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ +import org.scalatest.Inside -class SyncIOTests extends BaseTestsSuite { +class SyncIOTests extends BaseTestsSuite with Inside { checkAllAsync("SyncIO", _ => SyncEffectTests[SyncIO].syncEffect[Int, Int, Int]) checkAllAsync("SyncIO", _ => MonoidTests[SyncIO[Int]].monoid) checkAllAsync("SyncIO", _ => SemigroupKTests[SyncIO].semigroupK[Int]) @@ -45,7 +46,7 @@ class SyncIOTests extends BaseTestsSuite { val ioa = SyncIO { throw Foo } - ioa.attempt.unsafeRunSync() should matchPattern { + inside(ioa.attempt.unsafeRunSync()) { case Left(Foo) => () } } @@ -54,7 +55,7 @@ class SyncIOTests extends BaseTestsSuite { case object Foo extends Exception val e: Either[Throwable, Nothing] = Left(Foo) - SyncIO.fromEither(e).attempt.unsafeRunSync() should matchPattern { + inside(SyncIO.fromEither(e).attempt.unsafeRunSync()) { case Left(Foo) => () } } @@ -63,7 +64,7 @@ class SyncIOTests extends BaseTestsSuite { case class Foo(x: Int) val e: Either[Throwable, Foo] = Right(Foo(1)) - SyncIO.fromEither(e).attempt.unsafeRunSync() should matchPattern { + inside(SyncIO.fromEither(e).attempt.unsafeRunSync()) { case Right(Foo(_)) => () } } diff --git a/project/plugins.sbt b/project/plugins.sbt index caeebf0fb2..9f09e7244c 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,9 +1,9 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.4.0") addSbtPlugin("com.dwijnand" % "sbt-travisci" % "1.2.0") addSbtPlugin("io.crashbox" % "sbt-gpg" % "0.2.1") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.6.1") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.4.0") -addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.10") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.32") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.8.1") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7") @@ -11,3 +11,4 @@ addSbtPlugin("com.47deg" % "sbt-microsites" % "1.1.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.1") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.1.1") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.11")