I'm learning ZIO 2.x, when configuring Runtime
using bootstrap
layer, it don't works.
object RuntimeCustom extends ZIOAppDefault {
// It's not work, And I don't know why?
override val bootstrap = EmailService.live
def run = (for {
_ <- ZIO.debug("Start...")
_ <- EmailService.send("God", "Hi")
_ <- ZIO.debug("End...")
} yield ())
}
Get one error:
[error] /Users/changzhi/github-repo/zio-start/src/main/scala/zio/reference/experiment/core/RuntimeCustom.scala:35:7:
[error]
[error] ──── ZIO APP ERROR ───────────────────────────────────────────────────
[error]
[error] Your effect requires a service that is not in the environment.
[error] Please provide a layer for the following type:
[error]
[error] 1. example.EmailService
[error]
[error] Call your effect's provide method with the layers you need.
[error] You can read more about layers and providing services here:
[error]
[error] https://zio.dev/next/datatypes/contextual/
[error]
[error] ──────────────────────────────────────────────────────────────────────
[error]
[error] _ <- EmailService.send("God", "Hi")
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
If I replace with provide
, it works.
object RuntimeCustom extends ZIOAppDefault {
def run = (for {
_ <- ZIO.debug("Start...")
_ <- EmailService.send("God", "Hi")
_ <- ZIO.debug("End...")
} yield ())
// This can works, have no doubt
.provide(EmailService.live)
}
Full version program is here
package example
import zio._
trait EmailService {
def send(user: String, content: String): Task[Unit]
}
object EmailService {
def send(user: String, content: String): ZIO[EmailService, Throwable, Unit] =
ZIO.serviceWithZIO[EmailService](_.send(user, content))
val live: ZLayer[Any, Nothing, EmailService] =
ZLayer.fromZIO( ZIO.succeed(EmailServiceFake()) <* Console.printLine("Init EmailService") ).orDie
}
case class EmailServiceFake() extends EmailService {
override def send(user: String, content: String): Task[Unit] =
Console.printLine(s"sending email to $user")
}
object RuntimeCustom extends ZIOAppDefault {
// It's not work, And I don't know why?
//override val bootstrap = EmailService.live
def run = (for {
_ <- ZIO.debug("Start...")
_ <- EmailService.send("God", "Hi")
_ <- ZIO.debug("End...")
} yield ())
// This can works, have no doubt
.provide(EmailService.live)
}
CodePudding user response:
ZIOAppDefault
is meant for apps that will not require additional services, the assumption is that they will only "require" the built in services because you have provided away your other requirements with the provide
method.
If you want to use the bootstrap
method you should instead extend ZIOApp
and override both the environmentTag
and Environment
fields so that the run
mechanism knows how to construct the final environment.
import zio._
object App extends ZIOApp {
// Tell ZIO how the environment is constructed
override val environmentTag: EnvironmentTag[Environment] = EnvironmentTag[Environment]
// Tell the app which layers will be leftover from the `run`
override type Environment = FooService
// The app how to construct those remaining layers
override val bootstrap = FooService.layer
val run = FooService.doFoo
}
class FooService {
def doFoo: UIO[Unit] = ZIO.unit
}
object FooService {
val layer = ZLayer.succeed(new FooService)
def doFoo = ZIO.serviceWithZIO[FooService](_.doFoo)
}
Edit
Using the original example to show that this works:
import zio._
trait EmailService {
def send(user: String, content: String): Task[Unit]
}
object EmailService {
def send(user: String, content: String): ZIO[EmailService, Throwable, Unit] =
ZIO.serviceWithZIO[EmailService](_.send(user, content))
val live: ZLayer[Any, Nothing, EmailService] =
ZLayer.fromZIO( ZIO.succeed(EmailServiceFake()) <* Console.printLine("Init EmailService") ).orDie
}
case class EmailServiceFake() extends EmailService {
override def send(user: String, content: String): Task[Unit] =
Console.printLine(s"sending email to $user")
}
object RuntimeCustom extends ZIOApp {
// It's not work, And I don't know why?
override val bootstrap = EmailService.live
override type Environment = EmailService
override val environmentTag: EnvironmentTag[Environment] = EnvironmentTag[Environment]
def run = (for {
_ <- ZIO.debug("Start...")
_ <- EmailService.send("God", "Hi")
_ <- ZIO.debug("End...")
} yield ())
}