我开始尝试 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 ()
}
Run Code Online (Sandbox Code Playgroud)
(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)
Run Code Online (Sandbox Code Playgroud)
我不明白以下内容:
该程序需要环境中的一个Console和一个System实例,但我之前不需要.provide它,当我没有记录时。为什么?为什么我突然需要它?
根据scaladoc,.provideSome 提供了运行此效果所需的*一些*环境,其余为R0。对我来说,这意味着我不必提供完整的环境类型,我可以一个一个添加所需的模块 - 但它似乎期望完整的 ZEnv 类型,就像.provide- 那么有什么意义呢?
我无法通过 实例化匿名类new Logger with Console.Live with system.System.Live,因为 Slf4jLogger 在伴随对象中有一个工厂方法。如何将此工厂方法与其他特征一起使用?
既然在 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)
Run Code Online (Sandbox Code Playgroud)
这将编译并运行,但覆盖混合特征的内部成员似乎不正确......
它的工作方式与 完全相同.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)
Run Code Online (Sandbox Code Playgroud)
到底是怎么回事?
恐怕您必须返回一个实现您想要提供的所有特征的实例。
如果您查看 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)
* }
* }
* )
* }}}
*/
Run Code Online (Sandbox Code Playgroud)
对我来说,这意味着我不必提供完整的环境类型,我可以一一添加所需的模块
不可以。您必须为要包装的效果提供完整环境类型的实例(因为它需要)。我认为这是因为我们希望在编译时进行检查,并且没有办法(没有宏)在不说明如何做到这一点的情况下生成“混搭”堆叠环境。未来可能会有一些宏。
就像 .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
Run Code Online (Sandbox Code Playgroud)
由于您正在使用provideSome[ZEnv],您可能不应该访问全局单例Console.Live,而是从env传入的文件中获取:
.provideSome[ZEnv](env =>
new Console with Logger with system.System {
override val console = env.console
Run Code Online (Sandbox Code Playgroud)
该方案要求控制台和环境的系统的实例,但我没有到
.provide之前,
那是因为ZEnv,默认环境已经提供了这两个。
既然在 Java 中创建一个记录器实例是有效的,那么 .provide 甚至是调用正确的函数吗?
就个人而言,我会忽略构建记录器是有效的。这似乎不值得追踪。我认为它是一些基本的设施,它几乎是类加载的一部分。
如果你确实想跟踪效果,你可以这样做
app
.provideSomeM(env => for {
logger <- UIO(Slf4jLogger.create)
} yield new MyEnvironment(env, logger)
)
Run Code Online (Sandbox Code Playgroud)