From f33c083a36587ed5e96a6647d02beb362d30421d Mon Sep 17 00:00:00 2001 From: "Ross A. Baker" Date: Fri, 17 Feb 2023 14:49:34 -0500 Subject: [PATCH 1/7] cats.mtl.Local[IO, E] from IOLocal[E] --- build.sbt | 5 ++- .../src/main/scala/cats/effect/IOLocal.scala | 15 +++++++ .../scala/cats/effect/IOLocalLocalSpec.scala | 41 +++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala diff --git a/build.sbt b/build.sbt index 2e140de51b..badb7fa5da 100644 --- a/build.sbt +++ b/build.sbt @@ -292,6 +292,7 @@ ThisBuild / apiURL := Some(url("https://typelevel.org/cats-effect/api/3.x/")) ThisBuild / autoAPIMappings := true val CatsVersion = "2.9.0" +val CatsMtlVersion = "1.3.0" val Specs2Version = "4.19.2" val ScalaCheckVersion = "1.17.0" val DisciplineVersion = "1.4.0" @@ -445,6 +446,7 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform) .settings( name := "cats-effect", libraryDependencies ++= Seq( + "org.typelevel" %% "cats-mtl" % CatsMtlVersion, "org.typelevel" %% "scalac-compat-annotation" % ScalacCompatVersion % CompileTime ), mimaBinaryIssueFilters ++= Seq( @@ -833,7 +835,8 @@ lazy val tests: CrossProject = crossProject(JSPlatform, JVMPlatform, NativePlatf "org.scalacheck" %%% "scalacheck" % ScalaCheckVersion, "org.specs2" %%% "specs2-scalacheck" % Specs2Version % Test, "org.typelevel" %%% "discipline-specs2" % DisciplineVersion % Test, - "org.typelevel" %%% "cats-kernel-laws" % CatsVersion % Test + "org.typelevel" %%% "cats-kernel-laws" % CatsVersion % Test, + "org.typelevel" %% "cats-mtl-laws" % CatsMtlVersion % Test ), buildInfoPackage := "catseffect" ) diff --git a/core/shared/src/main/scala/cats/effect/IOLocal.scala b/core/shared/src/main/scala/cats/effect/IOLocal.scala index 0675fe042a..423012b6a4 100644 --- a/core/shared/src/main/scala/cats/effect/IOLocal.scala +++ b/core/shared/src/main/scala/cats/effect/IOLocal.scala @@ -16,7 +16,9 @@ package cats.effect +import cats.Applicative import cats.data.AndThen +import cats.mtl.Local /** * [[IOLocal]] provides a handy way of manipulating a context on different scopes. @@ -243,6 +245,17 @@ sealed trait IOLocal[A] { self => } } + final def toLocal: Local[IO, A] = + new Local[IO, A] { + def applicative: Applicative[IO] = + IO.asyncForIO + + def ask[A2 >: A] = + self.get + + def local[B](iob: IO[B])(f: A => A): IO[B] = + self.modify(e => f(e) -> e).bracket(Function.const(iob))(self.set) + } } object IOLocal { @@ -319,4 +332,6 @@ object IOLocal { underlying.get.flatMap(s => underlying.reset.as(getter(s))) } + def local[E](e: E): IO[Local[IO, E]] = + IOLocal(e).map(_.toLocal) } diff --git a/tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala b/tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala new file mode 100644 index 0000000000..05898119c9 --- /dev/null +++ b/tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala @@ -0,0 +1,41 @@ +/* + * Copyright 2020-2022 Typelevel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cats +package effect + +import cats.mtl.Local +import cats.mtl.laws.discipline._ + +import org.typelevel.discipline.specs2.mutable.Discipline + +import java.util.concurrent.CancellationException + +class IOLocalLocalSpec extends BaseSpec with Discipline { + sequential + + implicit val ticker = Ticker() + + implicit val local: Local[IO, Int] = + // Don't try this at home + unsafeRun(IOLocal.local(0)).fold( + throw new CancellationException("canceled"), + throw _, + _.get + ) + + checkAll("Local[IO, Int]", LocalTests[IO, Int].local[Int, Int]) +} From 0fb57548c391efe930dd23627da48f8c20bb1fed Mon Sep 17 00:00:00 2001 From: "Ross A. Baker" Date: Fri, 17 Feb 2023 16:21:59 -0500 Subject: [PATCH 2/7] Move IOLocal.local to IO.local --- core/shared/src/main/scala/cats/effect/IO.scala | 3 +++ core/shared/src/main/scala/cats/effect/IOLocal.scala | 3 --- .../effect/{IOLocalLocalSpec.scala => IOMtlLocalSpec.scala} | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) rename tests/shared/src/test/scala/cats/effect/{IOLocalLocalSpec.scala => IOMtlLocalSpec.scala} (91%) diff --git a/core/shared/src/main/scala/cats/effect/IO.scala b/core/shared/src/main/scala/cats/effect/IO.scala index c8ceeefcc8..93a0a18b21 100644 --- a/core/shared/src/main/scala/cats/effect/IO.scala +++ b/core/shared/src/main/scala/cats/effect/IO.scala @@ -1678,6 +1678,9 @@ object IO extends IOCompanionPlatform with IOLowPriorityImplicits { case Failure(err) => raiseError(err) } + def local[E](e: E): IO[cats.mtl.Local[IO, E]] = + IOLocal(e).map(_.toLocal) + // instances implicit def showForIO[A: Show]: Show[IO[A]] = diff --git a/core/shared/src/main/scala/cats/effect/IOLocal.scala b/core/shared/src/main/scala/cats/effect/IOLocal.scala index 423012b6a4..89a818b3f6 100644 --- a/core/shared/src/main/scala/cats/effect/IOLocal.scala +++ b/core/shared/src/main/scala/cats/effect/IOLocal.scala @@ -331,7 +331,4 @@ object IOLocal { def getAndReset: IO[A] = underlying.get.flatMap(s => underlying.reset.as(getter(s))) } - - def local[E](e: E): IO[Local[IO, E]] = - IOLocal(e).map(_.toLocal) } diff --git a/tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala b/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala similarity index 91% rename from tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala rename to tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala index 05898119c9..ea774ab787 100644 --- a/tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala +++ b/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala @@ -19,19 +19,18 @@ package effect import cats.mtl.Local import cats.mtl.laws.discipline._ - import org.typelevel.discipline.specs2.mutable.Discipline import java.util.concurrent.CancellationException -class IOLocalLocalSpec extends BaseSpec with Discipline { +class IOMtlLocalSpec extends BaseSpec with Discipline { sequential implicit val ticker = Ticker() implicit val local: Local[IO, Int] = // Don't try this at home - unsafeRun(IOLocal.local(0)).fold( + unsafeRun(IO.local(0)).fold( throw new CancellationException("canceled"), throw _, _.get From efc096d3cf1eb0d433a9259dd803625f97903294 Mon Sep 17 00:00:00 2001 From: "Ross A. Baker" Date: Fri, 17 Feb 2023 16:32:24 -0500 Subject: [PATCH 3/7] Scala 3 wants explicitly typed implicits --- tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala b/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala index ea774ab787..26394a9d69 100644 --- a/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala +++ b/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala @@ -26,7 +26,7 @@ import java.util.concurrent.CancellationException class IOMtlLocalSpec extends BaseSpec with Discipline { sequential - implicit val ticker = Ticker() + implicit val ticker: Ticker = Ticker() implicit val local: Local[IO, Int] = // Don't try this at home From 79180ac40f287bf090950a9ec730cf3e63d2b46c Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sun, 24 Nov 2024 22:01:20 +0000 Subject: [PATCH 4/7] Scalafix --- tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala b/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala index 26394a9d69..e5d9d43bcc 100644 --- a/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala +++ b/tests/shared/src/test/scala/cats/effect/IOMtlLocalSpec.scala @@ -19,6 +19,7 @@ package effect import cats.mtl.Local import cats.mtl.laws.discipline._ + import org.typelevel.discipline.specs2.mutable.Discipline import java.util.concurrent.CancellationException From 1d42c9c21aa0c8cd2865b7429ba534aa3696f1af Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sun, 24 Nov 2024 17:25:29 -0500 Subject: [PATCH 5/7] You forgot a percentage sign --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 7afbc0a6f7..be741d648f 100644 --- a/build.sbt +++ b/build.sbt @@ -465,7 +465,7 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform) .settings( name := "cats-effect", libraryDependencies ++= Seq( - "org.typelevel" %% "cats-mtl" % CatsMtlVersion + "org.typelevel" %%% "cats-mtl" % CatsMtlVersion ), mimaBinaryIssueFilters ++= Seq( // introduced by #1837, removal of package private class From 36d5b17c4c5a478c7b2f1030bf2f455605c26db8 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sun, 24 Nov 2024 17:26:07 -0500 Subject: [PATCH 6/7] `toLocal` -> `asLocal` --- core/shared/src/main/scala/cats/effect/IO.scala | 2 +- core/shared/src/main/scala/cats/effect/IOLocal.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/shared/src/main/scala/cats/effect/IO.scala b/core/shared/src/main/scala/cats/effect/IO.scala index 5fc440ac65..a4c597547c 100644 --- a/core/shared/src/main/scala/cats/effect/IO.scala +++ b/core/shared/src/main/scala/cats/effect/IO.scala @@ -1950,7 +1950,7 @@ object IO extends IOCompanionPlatform with IOLowPriorityImplicits with TuplePara } def local[E](e: E): IO[cats.mtl.Local[IO, E]] = - IOLocal(e).map(_.toLocal) + IOLocal(e).map(_.asLocal) // instances diff --git a/core/shared/src/main/scala/cats/effect/IOLocal.scala b/core/shared/src/main/scala/cats/effect/IOLocal.scala index e464240a69..e045cdd625 100644 --- a/core/shared/src/main/scala/cats/effect/IOLocal.scala +++ b/core/shared/src/main/scala/cats/effect/IOLocal.scala @@ -240,7 +240,7 @@ sealed trait IOLocal[A] extends IOLocalPlatform[A] { self => */ def lens[B](get: A => B)(set: A => B => A): IOLocal[B] - final def toLocal: Local[IO, A] = + final def asLocal: Local[IO, A] = new Local[IO, A] { def applicative: Applicative[IO] = IO.asyncForIO From 94e126e7d97ae24def614b13f686b8fbb82d5de6 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sun, 24 Nov 2024 17:35:14 -0500 Subject: [PATCH 7/7] %%%%%%%%%%%%%%%%%%% --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index be741d648f..2ff7fcc61b 100644 --- a/build.sbt +++ b/build.sbt @@ -907,7 +907,7 @@ lazy val tests: CrossProject = crossProject(JSPlatform, JVMPlatform, NativePlatf "org.specs2" %%% "specs2-scalacheck" % Specs2Version % Test, "org.typelevel" %%% "discipline-specs2" % DisciplineVersion % Test, "org.typelevel" %%% "cats-kernel-laws" % CatsVersion % Test, - "org.typelevel" %% "cats-mtl-laws" % CatsMtlVersion % Test + "org.typelevel" %%% "cats-mtl-laws" % CatsMtlVersion % Test ), githubWorkflowArtifactUpload := false )