我对Scala和ZIO还是陌生的,遇到了一个奇怪的难题。
我想设置一个包含ZIO队列和更高版本的ZIO环境
此共享队列中有不同的ZIO任务offer
和take
。
我试图这样定义我的环境
trait MainEnv extends Console with Clock
{
val mainQueue = Queue.unbounded[String]
}
并从像这样的单独任务中访问队列
for {
env <- ZIO.environment[MainEnv]
queue <- env.mainQueue
...
但是在测试中,我观察到每个单独的任务都有一个单独的Queue实例。
查看unbounded
def unbounded[A]: UIO[Queue[A]]
我观察到它不会立即返回一个队列,而是返回一个效果,该效果返回 排队,所以虽然观察到的行为是有道理的,但这根本不是我想要的, 我没有清晰的方法来获得我想要的行为。
对于如何实现我设置不同目标的建议,我们将不胜感激 任务通过存储在环境中的共享队列进行通信。
作为参考,这是我的代码和输出。
bash-3.2$ sbt run
[info] Loading project definition from /private/tmp/env-q/project
[info] Loading settings for project root from build.sbt ...
[info] Set current project to example (in build file:/private/tmp/env-q/)
[info] Compiling 1 Scala source to /private/tmp/env-q/target/scala-2.12/classes ...
[info] Done compiling.
[info] Packaging /private/tmp/env-q/target/scala-2.12/example_2.12-0.0.1-SNAPSHOT.jar ...
[info] Done packaging.
[info] Running example.Main
env example.Main$$anon$1@36fbcafd queue zio.Queue$$anon$1@65b9a444
env example.Main$$anon$1@36fbcafd queue zio.Queue$$anon$1@7c050764
(挂在这里-注意环境对象是相同的,但队列对象是不同的,因此第二个任务被卡住了)
这是我完整的测试,它基于https://www.slideshare.net/jdegoes/zio-queue的幻灯片37中的示例
package example
import zio.{App, Queue, ZIO}
import zio.blocking.Blocking
import zio.clock.Clock
import zio.console._
trait MainEnv extends Console with Clock // environment with queue
{
val mainQueue = Queue.unbounded[String]
}
object Main extends App // main test
{
val task1 = for { // task to add something to the queue
env <- ZIO.environment[MainEnv]
queue <- env.mainQueue
_ <- putStrLn(s"env $env queue $queue")
_ <- queue.offer("Give me Coffee!")
} yield ()
val task2 = for { // task to remove+print stuff from queue
env <- ZIO.environment[MainEnv]
queue <- env.mainQueue
_ <- putStrLn(s"env $env queue $queue")
_ <- queue.take.flatMap(putStrLn)
} yield ()
val program = ZIO.runtime[MainEnv] // top level to run both tasks
.flatMap {
implicit rts =>
for {
_ <- task1.fork
_ <- task2
} yield ()
}
val runEnv = new MainEnv with Console.Live with Clock.Live
def run(args: List[String]) =
program.provide(runEnv).fold(_ => 1, _ => 0)
}
这是我使用的build.sbt
val ZioVersion = "1.0.0-RC13"
lazy val root = (project in file("."))
.settings(
organization := "example",
name := "example",
version := "0.0.1-SNAPSHOT",
scalaVersion := "2.12.8",
scalacOptions ++= Seq("-Ypartial-unification"),
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % ZioVersion,
),
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.6"),
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.2.4")
)
scalacOptions ++= Seq(
"-deprecation", // Emit warning and location for usages of deprecated APIs.
"-encoding", "UTF-8", // Specify character encoding used by source files.
"-language:higherKinds", // Allow higher-kinded types
"-language:postfixOps", // Allows operator syntax in postfix position (deprecated since Scala 2.10)
"-feature", // Emit warning and location for usages of features that should be imported explicitly.
"-Ypartial-unification", // Enable partial unification in type constructor inference
"-Xfatal-warnings", // Fail the compilation if there are any warnings
)
答案 0 :(得分:6)
在Official Gitter Channel for ZIO Core中,亚当·弗雷泽(Adam Fraser)提出了建议
您想让您的环境只有一个
Queue[String]
,然后您想要使用provideM
之类的与Queue.unbounded
之类的方法来创建一个队列并将其提供给整个应用程序。这就是provideM
与provide
相对的地方。它通过提供A
来满足需要ZIO[A]
的环境。
稍微深入研究ZIO来源,可以发现DefaultTestReporterSpec.scala中的一个有用示例。
通过将环境定义为
trait MainEnv extends Console with Clock // environment with queue
{
val mainQueue: Queue[String]
}
将我的任务更改为使用env.mainQueue
而不是=
访问<-
(因为mainQueue现在是Queue[String]
而不是UIO[Queue[String]]
,因此删除了{{1 }},并在测试中将runEnv
方法更改为使用run
provideSomeM
我能够获得预期的结果:
def run(args: List[String]) =
program.provideSomeM(
for {
q <- Queue.unbounded[String]
} yield new MainEnv with Console.Live with Clock.Live {
override val mainQueue = q
}
).fold(_ => 1, _ => 0)