scala 为什么重写Bootstrap,而不是Run方法?

2ic8powd  于 2022-11-09  发布在  Scala
关注(0)|答案(2)|浏览(182)

我正在学习ZIO 2.x,当使用bootstrap层配置Runtime时,它不工作。

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

出现一个错误:

[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

如果我用provide替换,它可以工作。

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

完整版程序在此

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

ohfgkhjo1#

ZIOAppDefault是为不需要额外服务的应用程序设计的,假设它们将只“需要”内置服务,因为您已经用provide方法提供了其他要求。
如果要使用bootstrap方法,则应该扩展ZIOApp并覆盖environmentTagEnvironment字段,以便run机制知道如何构造最终环境。

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

编辑

使用原始示例来说明这是可行的:

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

liwlm1x9

liwlm1x92#

像这样的事情应该会奏效:

object YourApp extends ZIOAppDefault {

def run = {
    val app = for {
        _ <- ZIO.debug("Start...")
        _ <- EmailService.send("God", "Hi")
        _ <- ZIO.debug("End...")
    } yield ()

    app.provideLayer(bootstrap)
}

override val bootstrap = {
    //    val confLayer = ZLayer(...getArgs...) construct Layer from getArgs
    //    confLayer >>> EmailService.live
    EmailService.live
}
}

相关问题