ZIO环境建设



我开始试验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 ()
}

(Slf4j记录器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. 程序需要环境中的ConsoleSystem实例,但我以前不登录时不必.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)

发生了什么事?

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

如果您查看ZIO文档中关于provideSome的示例,您会发现他们正在做完全相同的事情:他们将Console转换为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)
*     }
*   }
* )
* }}}
*/

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

否。您必须提供一个完整环境类型的实例,以达到您正在包装的效果(因为它需要这样(。我认为这是因为我们希望在编译时对此进行检查,如果没有宏,就无法在不详细说明如何生成"混搭"堆叠环境。未来可能会有一些宏。

就像.advere一样-那有什么意义呢?

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

而使用.provideSome,您自己可以请求一个环境来帮助您构建所提供的实例。该环境将传递到您的函数中。在上面的示例中,它们需要Console并将其扩充为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],您可能不应该访问全局singletonConsole.Live,而是从传入的env中获取:

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

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

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

既然在Java中创建记录器实例有点有效,那么.是否提供了正确的调用函数?

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

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

app
.provideSomeM(env => for {
logger <- UIO(Slf4jLogger.create)
} yield new MyEnvironment(env, logger)
)

让我试着解释一下。

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

您的app变量签名的返回类型为zio,环境为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

这是我使用的一种方式:

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得到的所有剩余部分

相关内容

  • 没有找到相关文章

最新更新