Skip to content

Commit

Permalink
cats.mtl.Local[IO, E] from IOLocal[E]
Browse files Browse the repository at this point in the history
  • Loading branch information
rossabaker committed Feb 17, 2023
1 parent 204fca2 commit f33c083
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 1 deletion.
5 changes: 4 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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"
)
Expand Down
15 changes: 15 additions & 0 deletions core/shared/src/main/scala/cats/effect/IOLocal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
41 changes: 41 additions & 0 deletions tests/shared/src/test/scala/cats/effect/IOLocalLocalSpec.scala
Original file line number Diff line number Diff line change
@@ -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])
}

0 comments on commit f33c083

Please sign in to comment.