izumi.functional.lifecycle
Members list
Type members
Classlikes
Lifecycle is a class that describes the effectful allocation of a resource and its finalizer. This can be used to represent expensive resources.
Lifecycle is a class that describes the effectful allocation of a resource and its finalizer. This can be used to represent expensive resources.
Resources can be created using Lifecycle.make:
def open(file: File): Lifecycle[IO, BufferedReader] =
Lifecycle.make(
acquire = IO { new BufferedReader(new FileReader(file)) }
)(release = reader => IO { reader.close() })
Using inheritance from Lifecycle.Basic:
final class BufferedReaderResource(
file: File
) extends Lifecycle.Basic[IO, BufferedReader] {
def acquire: IO[BufferedReader] = IO { new BufferedReader(new FileReader(file)) }
def release(reader: BufferedReader): IO[BufferedReader] = IO { reader.close() }
}
Using constructor-based inheritance from Lifecycle.Make, Lifecycle.LiftF, etc:
final class BufferedReaderResource(
file: File
) extends Lifecycle.Make[IO, BufferedReader](
acquire = IO { new BufferedReader(new FileReader(file)) },
release = reader => IO { reader.close() },
)
Or by converting from an existing cats.effect.Resource, scoped zio.ZIO or a zio.managed.ZManaged:
- Use Lifecycle.fromCats, Lifecycle.SyntaxLifecycleCats#toCats to convert from and to a cats.effect.Resource
- Use Lifecycle.fromZIO, Lifecycle.SyntaxLifecycleZIO#toZIO to convert from and to a scoped zio.ZIO
- And Lifecycle.fromZManaged, Lifecycle.SyntaxLifecycleZManaged#toZManaged to convert from and to a zio.managed.ZManaged
Usage is done via use:
open(file1).use {
reader1 =>
open(file2).use {
reader2 =>
readFiles(reader1, reader2)
}
}
Lifecycles can be combined into larger Lifecycles via Lifecycle#flatMap (and the associated for-comprehension syntax):
val res: Lifecycle[IO, (BufferedReader, BufferedReader)] = {
for {
reader1 IO(println("Disconnecting DB")))
val mqResource = Lifecycle.make(IO { println("Connecting to Message Queue!"); new MessageQueueConnection })(_ => IO(println("Disconnecting Message Queue")))
class MyApp(db: DBConnection, mq: MessageQueueConnection) {
val run = IO(println("Hello World!"))
}
val module = new ModuleDef {
make[DBConnection].fromResource(dbResource)
make[MessageQueueConnection].fromResource(mqResource)
make[MyApp]
}
Injector[IO]()
.produceGet[MyApp](module)
.use(_.run())
.unsafeRunSync()
Will produce the following output:
Connecting to DB!
Connecting to Message Queue!
Hello World!
Disconnecting Message Queue
Disconnecting DB
The lifecycle of the entire object graph is itself expressed with Lifecycle, you can control it by controlling the scope of .use or by manually invoking Lifecycle#acquire and Lifecycle#release.
== Inheritance helpers ==
The following helpers allow defining Lifecycle sub-classes using expression-like syntax:
- Lifecycle.Of
- Lifecycle.OfInner
- Lifecycle.OfCats
- Lifecycle.OfZIO
- Lifecycle.OfZManaged
- Lifecycle.OfZLayer
- Lifecycle.LiftF
- Lifecycle.Make
- Lifecycle.Make_
- Lifecycle.MakePair
- Lifecycle.FromAutoCloseable
- Lifecycle.SelfOf
- Lifecycle.MutableOf
The main reason to employ them is to workaround a limitation in Scala 2's eta-expansion — when converting a method to a function value, Scala always tries to fulfill implicit parameters eagerly instead of making them parameters of the function value, this limitation makes it harder to inject implicits using distage.
However, when using distage's type-based syntax: make[A].fromResource[A.Resource[F]] — this limitation does not apply and implicits inject successfully.
So to workaround the limitation you can convert an expression based resource-constructor such as:
import distage.Lifecycle, cats.Monad
class A
object A {
def resource[F[_]](implicit F: Monad[F]): Lifecycle[F, A] = Lifecycle.pure(new A)
}
Into a class-based form:
import distage.Lifecycle, cats.Monad
class A
object A {
final class Resource[F[_]](implicit F: Monad[F])
extends Lifecycle.Of(
Lifecycle.pure(new A)
)
}
And inject successfully using make[A].fromResource[A.Resource[F]] syntax of izumi.distage.model.definition.dsl.ModuleDefDSL.
The following helpers ease defining Lifecycle subclasses using traditional inheritance where acquire/release parts are defined as methods:
Attributes
Attributes
- Companion
- object
- Supertypes
-
class Objecttrait Matchableclass Any
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
LifecycleAggregator.type