你是 Scala 中 Any => 单位转换的受害者。您正在做的是在任务中计算一些超级复杂的东西,然后通过将其转换为 () 来忽略它,并让 scala 忽略它。
这里有一些代码使用较低级别的Initialize API 来折叠任务(我希望将来更直接地公开)
我的 build.sbt:
def dummyTaskGen(name: String): Def.Initialize[Task[Unit]] = Def.task {
System.err.println(s"Started ${name}")
Thread.sleep(1000L*6)
System.err.println(s"Finished ${name}")
}
lazy val validations = taskKey[Unit]("Run everything, one at a time.")
lazy val names = Seq("foo", "bar", "baz", "biz", "buzz", "bunk")
lazy val allRuns: Def.Initialize[Task[Unit]] = Def.settingDyn {
val zero: Def.Initialize[Seq[Task[Unit]]] = Def.setting { Seq(task(())) }
names.map(dummyTaskGen).foldLeft(zero) { (acc, current) =>
acc.zipWith(current) { case (taskSeq, task) =>
taskSeq :+ task.tag(Tags.CPU)
}
} apply { tasks: Seq[Task[Unit]] =>
tasks.join map { seq => () /* Ignore the sequence of unit returned */ }
}
}
validations := allRuns.value
concurrentRestrictions in Global ++= Seq(
Tags.limit(Tags.CPU, 2)
)
在命令行上:
> validations
Started bar
Started buzz
Finished buzz
Finished bar
Started biz
Started bunk
Finished biz
Finished bunk
Started baz
Started foo
Finished baz
Finished foo
[success] Total time: 18 s, completed Jul 7, 2014 2:11:43 PM
让我们深入了解我们在这里使用的方法:
首先,关键是您将zipWith-ing 一起完成任务的整个初始化。
例如,在您的项目中,您有一种机制可以生成名为validationTaskFor 的标记任务。这将返回一个Def.Intiailize[Task[Unit]],其中任务被适当标记。
所以,现在你有一个字符串序列,你可以把它变成一个初始化序列。从这里开始,我们需要合并所有的Initialization 外层以获得Task[_] 层中的多汁好处。
initialize 的 API 见:http://www.scala-sbt.org/0.13.5/api/index.html#sbt.Init$Initialize。
注意:
大多数情况下,初始化隐藏在宏后面(如Def.task,
Def.setting、Def.inputTask、:= 和朋友们)。然而,当
使用动态任务生成,暴露的 API 并不完全
足够了,并且您遇到了 scala 隐含的那些领域之一
推理 + 我们的宏导致编译的东西在
性质(即丢弃所有生成的 Task[_] 实例和
无缘无故返回 ()。
现在,进入函数的核心:
-
Def.settingDyn 需要Initialize[T]。在我们的例子中,T 是 Task[Unit]。所以我们知道我们需要将下面的表达式转换为Initialize[Task[Unit],所以让我们适当地键入allRuns 值以获得更好的编译器错误消息。
def allRuns: Def.Initialize[Task[Unit]] = Def.settingDyn { ... }
-
Def.Initialize 是一个应用程序,我们将有一个序列。这意味着折叠,所以我们应该为折叠创建一个zero。在这种情况下,当我们将所有任务合并在一起时,我们将返回一个Initialize[Seq[Task[Unit]],其中所有任务在单个Initialize 容器中,我们可以在其中与它们进行交互。 (这类似于在我们尝试使用它们之前确保所有任务都从 State 正确初始化/构造)。
val zero: Def.Initialize[Seq[Task[Unit]]] = Def.setting { Seq(task( () )) }
现在,我们将 Seq[String] 转换为 Seq[Initialize[Task[Unit]]
names.map(dummyTaskGen)`
-
我们折叠这个序列:
names.map(dummyTaskGen).foldLeft(zero) { (acc, current) => ... }
-
我们定义了如何使用`Initialize[Task[Unit]] 加入Initialize[Seq[Task[Unit]],即我们将新任务添加到现有任务列表中:
acc.zipWith(current) { case (taskSeq, task) =>
taskSeq :+ task.tag(Tags.CPU)
}
-
我们现在有一个Initialize[Seq[Task[Unit]],我们将所有这些联系在一起:
tasks.join /* Task[Seq[Unit]] */
-
我们忽略 Seq[Unit] 结果,将其转换为 Unit
tasks.join.map { ignore => () }
你应该做三件事:
- 开一张关于在
Def.task 或Def.taskDyn 宏中公开机制以直接公开向任务添加标签的工单,从而消除需要您进入此级别的两件事中的一个的 API。
- 打开一个关于直接公开加入任务的机制的工单。例如。
Initialize[Seq[Initialize[Task[T]]]] 应该可以在没有 hoopla 的情况下加入,因为每次有人想要这样做时,它都是相同的脏代码。
- 开一张关于自动任务宏和 Scala 的隐式 Any => 单元的不良交互导致非常严重的错误的票。