4

我开始尝试使用 ZIO,并尝试运行以下高度复杂的程序:

import logger.{Logger, _}
import zio.console._
import zio.{system, _}

object MyApp extends App {

  override def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = {

    app
      .provideSome[Logger](_ => Slf4jLogger.create) //1 
      .fold(_ => 1, _ => 0)
  }

  val app: ZIO[Console with system.System with Logger, SecurityException, Unit] =
    for {
      _         <- info("This message from the logger") //2
      maybeUser <- system.env("USER")
      _         <- maybeUser match {
                      case Some(userName) => putStrLn(s"Hello ${userName}!")
                      case None => putStrLn("I don't know your name")
                    }
    } yield ()
}

(Slf4jLogger 是https://github.com/NeQuissimus/zio-slf4j

如果我注释行//1并且//2一切正常。但在上面的表格中,我得到一个类型不匹配的错误:

Error:(13, 45) type mismatch;
 found   : logger.Slf4jLogger
 required: zio.console.Console with zio.system.System with logger.Logger
      .provideSome[Logger](_ => Slf4jLogger.create)

我不明白以下内容:

  1. 该程序在环境中需要一个Console和一个System实例,但.provide在我没有记录时,我以前不需要它。为什么?为什么我突然需要它?

  2. 根据 scaladoc,.provideSome 提供运行此效果所需的*一些*环境,剩下的为 R0。对我来说,这意味着我不必提供完整的环境类型,我可以一个一个地添加所需的模块——但它似乎期望完整的 ZEnv 类型,就像.provide——那么有什么意义呢?

  3. 我无法通过 实例化匿名类new Logger with Console.Live with system.System.Live,因为 Slf4jLogger 在伴随对象中有一个工厂方法。如何将此工厂方法与其他特征一起使用?

  4. 由于在 Java 中创建一个记录器实例是一种有效的方法,.provide甚至是正确的函数调用吗?

PS:我能够以一种丑陋的方式让它工作:

    app
      .provideSome[ZEnv](_ =>
        new Console with Logger with system.System {
          override val console = Console.Live.console
          override val system = System.Live.system
          override val logger = Slf4jLogger.create.logger
        }
      ) //1
      .fold(_ => 1, _ => 0)

这编译并运行,但覆盖混合特征的内部成员似乎不正确......

它的工作方式与以下内容完全相同.provide

    app
      .provide(
        new Console with Logger with system.System {
          override val console = Console.Live.console
          override val system = System.Live.system
          override val logger = Slf4jLogger.create.logger
        }
      ) //1
      .fold(_ => 1, _ => 0)

到底是怎么回事?

4

3 回答 3

5

恐怕您必须返回一个实现您要提供的所有特征的实例。

如果您查看 ZIO 文档中的示例provideSome,您会发现他们正在做完全相同的事情:他们将 aConsole转换为Console with Logging.

/**
   * Provides some of the environment required to run this effect,
   * leaving the remainder `R0`.
   *
   * {{{
   * val effect: ZIO[Console with Logging, Nothing, Unit] = ???
   *
   * effect.provideSome[Console](console =>
   *   new Console with Logging {
   *     val console = console
   *     val logging = new Logging {
   *       def log(line: String) = console.putStrLn(line)
   *     }
   *   }
   * )
   * }}}
   */

对我来说这意味着我不必提供完整的环境类型,我可以一个一个地添加所需的模块

不,您必须提供一个完整环境类型的实例来实现您包装的效果(因为它需要它)。我认为这是因为我们希望在编译时对其进行检查,并且没有办法(没有宏)生成“混搭”堆叠环境而不详细说明如何做到这一点。未来可能会有一些宏。

就像 .provide - 那有什么意义呢?

不同之处在于,.provide您必须在没有任何输入的情况下从头开始构建所需环境的实例。因此,效果(用你的包装)不再需要任何东西。

.provideSome您自己可以请求一个环境来帮助您构建您提供的实例。该环境将被传递到您的函数中。在上面的示例中,他们需要 aConsole并将其扩充为Console with Logging(通过使用console给定的实例)。结果,effect.provideSome[Console](...)仍然需要一个Console(而用.provide它编写则需要Nothing)。

.provideSome[ZEnv](_ =>
        new Console with Logger with system.System {
          override val console = Console.Live.console

由于您正在使用provideSome[ZEnv],您可能不应该访问全局单例Console.Live,而是从env传入的内容中获取:

.provideSome[ZEnv](env =>
        new Console with Logger with system.System {
          override val console = env.console

该程序在环境中需要一个控制台和一个系统实例,但我以前不需要.provide

那是因为ZEnv,默认环境已经提供了这两个。

既然在 Java 中创建一个记录器实例是一种有效的方法,那么 .provide 甚至是正确的调用函数吗?

就个人而言,我会忽略构建记录器是有效的。这似乎不值得追踪。我认为它是一些基本功能,它几乎是类加载的一部分。

如果你确实想跟踪效果,你可以这样做

app
  .provideSomeM(env => for {
     logger <- UIO(Slf4jLogger.create)
   } yield new MyEnvironment(env, logger)
  )
于 2019-10-25T11:21:51.820 回答
1

让我试着解释一下。

如果你看看ZEnv它的别名是什么Clock with Console with System with Random with Blocking。因此,当您开始实施时,def run(args: List[String]): ZIO[ZEnv, Nothing, Int]zio 已经为您提供了这些功能。

您的app变量签名的返回类型为 zio with environment Console with System with Logger- 突出的一件事是一种Logger能力。它不是标准的,所以 ZIO 无法为您提供。您必须自己提供。

这就是您使用的方法.provideSome[Logger]- 您从环境要求中消除了一项功能,使其类型与标准兼容:

type ZEnv = Clock with Console with System with Random with Blocking
type YourEnv = Console with System
type Proof = ZEnv <:< YourEnv
于 2019-10-24T09:04:09.823 回答
0

这是我使用的一种方式:

import logger.{Logger, _}
import zio.console._
import zio.{system, _}

object MyApp extends App {

  override def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = {

    app
      .fold(_ => 1, _ => 0)
  }

  val app: ZIO[Environment, SecurityException, Unit] =
    for {
      _         <- info("This message from the logger").provide( Slf4jLogger.create) // 1
      maybeUser <- system.env("USER")
      _         <- maybeUser match {
                      case Some(userName) => putStrLn(s"Hello ${userName}!")
                      case None => putStrLn("I don't know your name")
                    }
    } yield ()
}
  1. 我直接在for-comprehension应用程序中提供它。您从zio.App.
于 2019-10-30T19:26:52.460 回答