Resource
Effectfully allocates and releases a resource. Forms a MonadError
on the resource type when the effect type has a Bracket
instance.
The Acquiring and releasing Resource
s section of the tutorial provides some additional context and examples regarding Resource
.
import cats.effect.BracketThrow
abstract class Resource[F[_], A] {
def use[B](f: A => F[B])(implicit F: BracketThrow[F]): F[B]
}
Nested resources are released in reverse order of acquisition. Outer resources are released even if an inner acquisition, use or release fails.
You can lift any F[A]
with an Applicative
instance into a Resource[F, A]
with a no-op release via Resource.liftF
:
import cats.effect.{IO, Resource}
val greet: String => IO[Unit] = x => IO(println("Hello " + x))
// greet: String => IO[Unit] = <function1>
Resource.eval(IO.pure("World")).use(greet).unsafeRunSync()
// Hello World
Moreover it's possible to apply further effects to the wrapped resource without leaving the Resource
context via evalMap
:
import cats.effect.{IO, Resource}
val acquire: IO[String] = IO(println("Acquire cats...")) *> IO("cats")
// acquire: IO[String] = Bind(
// source = Delay(
// thunk = <function0>,
// trace = StackTrace(
// stackTrace = List(
// cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),
// cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),
// cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),
// cats.effect.IO$.delay(IO.scala:1176),
// cats.effect.IO$.apply(IO.scala:1144),
// repl.MdocSession$App2.<init>(resource.md:40),
// repl.MdocSession$App0.<init>(resource.md:31),
// repl.MdocSession$App.<init>(resource.md:16),
// repl.MdocSession$.app(resource.md:3),
// mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),
// scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),
// scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),
// scala.Console$.withErr(Console.scala:193),
// mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),
// scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),
// scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),
// scala.Console$.withOut(Console.scala:164),
// mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),
// mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),
// mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:104)
// )
// )
// ),
// f = cats.effect.IO$$Lambda$10759/0x0000000102eff040@19ad1f4f,
// trace = StackTrace(
// stackTrace = List(
// cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),
// cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),
// cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),
// cats.effect.IO.flatMap(IO.scala:133),
// cats.effect.IO.$times$greater(IO.scala:770),
// repl.MdocSession$App7.<init>(io.md:203),
// repl.MdocSession$App6.<init>(io.md:177),
// repl.MdocSession$App5.<init>(io.md:155),
// repl.MdocSession$App.<init>(io.md:137),
// repl.MdocSession$.app(io.md:3),
// mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),
// scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),
// ...
val release: String => IO[Unit] = _ => IO(println("...release everything"))
// release: String => IO[Unit] = <function1>
val addDogs: String => IO[String] = x =>
IO(println("...more animals...")) *> IO.pure(x + " and dogs")
// addDogs: String => IO[String] = <function1>
val report: String => IO[String] = x =>
IO(println("...produce weather report...")) *> IO("It's raining " + x)
// report: String => IO[String] = <function1>
Resource.make(acquire)(release).evalMap(addDogs).use(report).unsafeRunSync()
// Acquire cats...
// ...more animals...
// ...produce weather report...
// ...release everything
// res3: String = "It's raining cats and dogs"
Example
import cats.effect.{IO, Resource}
def mkResource(s: String) = {
val acquire = IO(println(s"Acquiring $s")) *> IO.pure(s)
def release(s: String) = IO(println(s"Releasing $s"))
Resource.make(acquire)(release)
}
val r = for {
outer <- mkResource("outer")
inner <- mkResource("inner")
} yield (outer, inner)
r.use { case (a, b) => IO(println(s"Using $a and $b")) }.unsafeRunSync()
If using an AutoCloseable create a resource without the need to specify how to close.
Examples
scala.io.Source
With import cats.effect._
val acquire = IO {
scala.io.Source.fromString("Hello world")
}
Resource.fromAutoCloseable(acquire).use(source => IO(println(source.mkString))).unsafeRunSync()
java.io
using IO
With import java.io._
import scala.jdk.CollectionConverters._
import cats.effect._
def readAllLines(bufferedReader: BufferedReader, blocker: Blocker)(implicit cs: ContextShift[IO]): IO[List[String]] =
blocker.delay[IO, List[String]] {
bufferedReader.lines().iterator().asScala.toList
}
def reader(file: File, blocker: Blocker)(implicit cs: ContextShift[IO]): Resource[IO, BufferedReader] =
Resource.fromAutoCloseableBlocking(blocker)(IO {
new BufferedReader(new FileReader(file))
}
)
def readLinesFromFile(file: File, blocker: Blocker)(implicit cs: ContextShift[IO]): IO[List[String]] = {
reader(file, blocker).use(br => readAllLines(br, blocker))
}
java.io
example agnostic of the effect type
A import java.io._
import cats.effect._
import cats.syntax.flatMap._
def reader[F[_]](file: File, blocker: Blocker)(implicit F: Sync[F], cs: ContextShift[F]): Resource[F, BufferedReader] =
Resource.fromAutoCloseableBlocking(blocker)(F.delay {
new BufferedReader(new FileReader(file))
})
def dumpResource[F[_]](res: Resource[F, BufferedReader], blocker: Blocker)(implicit F: Sync[F], cs: ContextShift[F]): F[Unit] = {
def loop(in: BufferedReader): F[Unit] =
F.defer {
blocker.delay(in.readLine()).flatMap { line =>
if (line != null) {
System.out.println(line)
loop(in)
} else {
F.unit
}
}
}
res.use(loop)
}
def dumpFile[F[_]](file: File, blocker: Blocker)(implicit F: Sync[F], cs: ContextShift[F]): F[Unit] =
dumpResource(reader(file, blocker), blocker)