首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ZIO环境建设

ZIO环境建设
EN

Stack Overflow用户
提问于 2019-10-24 08:04:36
回答 3查看 2.7K关注 0票数 5

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

代码语言:javascript
复制
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,那么一切都很好。但在上面的形式中,我得到了一个类型错配错误:

代码语言:javascript
复制
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。为什么?为什么我突然需要it?
  2. According来运行scaladoc,.provideSome提供了运行这个效果所需的一些环境,剩下的R0就剩下了。对我来说,这意味着我不需要提供完整的环境类型,我可以一个一个地添加所需的模块--但是它似乎期待完整的ZEnv类型,就像.provide一样--那么有什么意义呢?
  3. 我不能通过new Logger with Console.Live with system.System.Live实例化匿名类,因为Slf4jLogger在伴生对象中有一个工厂方法。如何与在Java中创建记录器实例的其他traits?
  4. Since一起使用此工厂方法是一种有效的方法,.provide甚至是调用的正确函数?

PS:我能用一种丑陋的方式来完成这个任务:

代码语言:javascript
复制
    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的工作方式完全相同。

代码语言:javascript
复制
    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)

怎么一回事?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-10-25 11:21:51

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

如果您查看ZIO的provideSome文档中的示例,您会发现他们正在做同样的事情:他们正在获取一个Console并将其转换为一个Console with Logging

代码语言:javascript
复制
/**
   * 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)
   *     }
   *   }
   * )
   * }}}
   */

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

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

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

不同的是,使用.provide时,您必须从头开始构建所需环境的实例,而不需要任何输入。因此,效果(与您的包装)不再需要任何东西。

然而,使用.provideSome,您可以自己请求一个环境来帮助您构建所提供的实例。该环境将传递到您的功能中。在上面的示例中,他们需要一个Console并将其扩展到Console with Logging (通过使用给出的console实例)。因此,effect.provideSome[Console](...)仍然需要一个Console (而使用.provide编写则需要Nothing)。

代码语言:javascript
复制
.provideSome[ZEnv](_ =>
        new Console with Logger with system.System {
          override val console = Console.Live.console

由于您使用的是provideSome[ZEnv],所以您可能不应该访问全局单子Console.Live,但是应该从传入的env中获取这一点:

代码语言:javascript
复制
.provideSome[ZEnv](env =>
        new Console with Logger with system.System {
          override val console = env.console

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

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

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

就我个人而言,我忽略了构建伐木器是有效的。这似乎不值得追踪。我认为这是一个基本的设施,它几乎是类加载的一部分。

如果你真的想跟踪效果,你可以

代码语言:javascript
复制
app
  .provideSomeM(env => for {
     logger <- UIO(Slf4jLogger.create)
   } yield new MyEnvironment(env, logger)
  )
票数 6
EN

Stack Overflow用户

发布于 2019-10-24 09:04:09

让我解释一下。

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

您的app变量签名有一个带有环境Console with System with Logger的返回类型zio --突出的一点是Logger功能。这是不标准的,所以ZIO不能为您提供它。你必须自己提供。

这就是您使用.provideSome[Logger]时所做的--您从环境需求中排除了一种功能,使其类型与标准兼容:

代码语言:javascript
复制
type ZEnv = Clock with Console with System with Random with Blocking
type YourEnv = Console with System
type Proof = ZEnv <:< YourEnv
票数 1
EN

Stack Overflow用户

发布于 2019-10-30 19:26:52

这是我使用的一种方法:

代码语言:javascript
复制
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.

那里得到的所有其他东西

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58536841

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档