[FIXED] Warum Bootstrap überschreiben, nicht für die Run-Methode arbeiten?

Ausgabe

Ich lerne ZIO 2.x, bei der Konfiguration Runtimemit bootstrapLayer funktioniert es nicht.

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 ())
}

Erhalten Sie einen Fehler:

[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

Wenn ich durch ersetze provide, funktioniert es.

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)
}

Das Vollversionsprogramm ist hier

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)
}

Lösung

ZIOAppDefaultfür Apps gedacht ist, die keine zusätzlichen Dienste benötigen, wird davon ausgegangen, dass sie nur die integrierten Dienste “benötigen”, da Sie Ihre anderen Anforderungen mit der Methode bereitgestellt haben.provide

Wenn Sie die Methode verwenden möchten, bootstrapsollten Sie stattdessen die Felder und erweitern ZIOAppund überschreiben , damit der Mechanismus weiß, wie die endgültige Umgebung zu erstellen ist.environmentTagEnvironmentrun

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)
}

Bearbeiten

Um zu zeigen, dass dies funktioniert, verwenden Sie das ursprüngliche Beispiel:

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 ())
}

https://scastie.scala-lang.org/02MMaWS2S6Wwe55a24H7Sg


Beantwortet von –
paulpdaniels


Antwort geprüft von –
Terry (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like