为什么值方法不能在宏外使用?

Why value method cannot be used outside macros?

错误信息

`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.
val x = version.value
                ^

明确指出如何解决问题,例如,使用 :=

val x = settingKey[String]("")
x := version.value

sbt uses macros heavily中的解释说明

The value method itself is in fact a macro, one that if you invoke it outside of the context of another macro, will result in a compile time error, the exact error message being... And you can see why, since sbt settings are entirely declarative, you can’t access the value of a task from the key, it doesn’t make sense to do that.

但是我很困惑声明性 sbt 的性质是什么意思。例如,直觉上我会认为以下 vanilla Scala 片段在语义上类似于 sbt 的

def version: String = ???
lazy val x = s"Hello $version"  // ok

trait Foo {
  def version: String
  val x = version               // ok
}

因为这是合法的,显然 Scala 片段在语义上与 sbt 片段不等同。我想知道是否有人可以详细说明为什么 value 不能在宏之外使用?是纯粹与宏语法相关的句法原因还是我遗漏了一些关于 sbt 本质的基本知识?

另外一句话说

Defining sbt’s task engine is done by giving sbt a series of settings, each setting declaring a task implementation. sbt then executes those settings in order. Tasks can be declared multiple times by multiple settings, the last one to execute wins.

所以在

行的那一刻
val x = version.value

将被执行(如果允许的话!),整个程序仍在设置中,SBT 不知道 version.

的最终定义

In what sense is the program "still being set up"?

SBT 的行动顺序基本上是(可能遗漏了一些东西):

  1. 您所有的 Scala 构建代码都是 运行。
  2. 它包含一些设置和任务定义,SBT 在遇到时收集这些(以及来自核心、插件等的)
  3. 它们按拓扑排序到任务图中,去重("the last one to execute wins")等
  4. 设置已评估。
  5. 现在您实际上可以 运行 执行任务(例如从 SBT 控制台)。

version.value 仅在步骤 4 后可用,但 val x = version.value 运行 在步骤 1 后可用。

Would not lazy evaluation take care of that?

嗯,当你写 val x = ... 时,就没有惰性求值。但是 lazy val x = ... 运行 也在第 1 步。