我开始尝试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)
我不理解以下内容:
该程序在环境中需要一个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)
这可以编译并运行,但是覆盖特质内部成员中的混合对象似乎不正确...
它与.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)
这是怎么回事?
答案 0 :(得分:2)
恐怕您必须返回一个实例,该实例实现您要提供的所有特征。
如果您查看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)
* }
* }
* )
* }}}
*/
对我来说,这意味着我不必提供完整的环境类型,我可以一个一个地添加所需的模块
不。您必须提供完整环境类型的实例来实现包装效果(因为它需要这样做)。我认为这是因为我们希望在编译时对此进行检查,并且没有办法(没有宏)来生成“混搭”堆栈式环境,而无需详细说明如何做到这一点。将来可能会有一些宏。
就像.provide一样-有什么意义?
区别在于,使用.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]
,因此您可能不应该访问全局单例Console.Live
,而应从传入的env
中获取:
.provideSome[ZEnv](env =>
new Console with Logger with system.System {
override val console = env.console
该程序在环境中需要一个Console和一个System实例,但是我不必
.provide
之前
这是因为ZEnv
的默认环境已经提供了这两个。
由于使用Java创建记录器实例是一种有效的方法,因此.provide甚至提供了正确的调用函数吗?
就个人而言,我会忽略构造记录器是有效的。它似乎不值得跟踪。我认为这几乎是类加载的一部分。
如果您确实想跟踪效果,可以这样做
app
.provideSomeM(env => for {
logger <- UIO(Slf4jLogger.create)
} yield new MyEnvironment(env, logger)
)
答案 1 :(得分:1)
让我尝试解释。
如果您查看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
答案 2 :(得分: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 ()
}
for-comprehension
中提供了它。 zio.App
中的所有剩余内容。