1

我一直在尝试为我想从我的项目中引用的开源项目生成 SBT 构建,但我遇到了似乎是编译器错误的问题。

以下代码在 eclipse/scala-ide 中按预期编译和运行,但 scala 2.10.6 编译器无法消化它:

package foo

import scala.language.dynamics

object Caller extends App {
  val client = new Client() // initialise an R interpreter
  client.x = 1.0
}
class Client extends Dynamic {
  var map = Map.empty[String, Any]
  def selectDynamic(name: String) = map get name getOrElse sys.error("field not found")
  def updateDynamic(name: String)(value: Any) { map += name -> value }
}

这是我的 build.sbt:

scalaVersion := "2.10.6"

libraryDependencies++= Seq(
  "org.scalanlp" %% "breeze" % "0.12"
)

当我指定 scalaVersion := 2.10.6 时,我收到以下编译错误:

[error] /home/philwalk/dynsbt/src/main/scala/foo/Caller.scala:8: type mismatch;
[error]  found   : foo.Caller.client.type (with underlying type foo.Client)
[error]  required: ?{def x: ?}
[error] Note that implicit conversions are not applicable because they are ambiguous:
[error]  both method any2Ensuring in object Predef of type [A](x: A)Ensuring[A]
[error]  and method any2ArrowAssoc in object Predef of type [A](x: A)ArrowAssoc[A]
[error]  are possible conversion functions from foo.Caller.client.type to ?{def x: ?}
[error]   client.x = Seq("a","b","c")
[error]   ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 3 s, completed May 3, 2016 11:03:08 AM

使用 scalaVersion := 2.11.8,没有问题,尽管我需要交叉编译,所以这不是解决方法。

另一个线索是我可以通过更改这行代码来隐藏问题:

client.x = 1.0

对此:

client.xx = 1.0

当我直接用 scalac 2.10.6 编译时,我也看到了这个问题。

作为一种解决方法,我可以重构项目以使用比单个字符更长的字段名称,尽管因为它不是我的项目,我在某种程度上受限于我可以接受的解决方法。此外,它是一个breeze.linalg 项目,禁止使用单个字符矩阵和向量名称将是一个严重的限制。

花了几个小时将问题归结为来自一个更大项目的代码片段,我不希望对这个开源库的 scala 2.10 版本施加限制。由于此错误似乎已在 scala 2.11 中修复,我假设已决定不将该修复程序向后移植到 2.10。

我更改了标题以反映解决方法的存在(更长的字段名称)。

4

1 回答 1

2

这是 Scala 2.10 的错,不是 sbt 的错。

问题在于,Predef在每个 Scala 文件中都导入其内容的 中,有两个有问题的类:EnsuringArrowAssoc. 这两个类的成员可通过对任何类型值的隐式转换获得。ArrowAssoc,例如,是您可以1 -> 2构造元组的原因(1, 2)

现在,这些类在 2.10 中具有声明一个名为x!的成员的非常不幸的属性。尽管在 2.10 中已弃用,但scala.Dynamic.

在您的代码中,client.x = 1.0首先测试是否client.x作为 val/getter onclient的类型存在。实际上并没有,但如果我们使用隐式转换将其转换为or ,它将可用。由于隐式转换的优先级高于处理,Scala 编译器会尝试使用它们。但是由于有两个同样有效的转换,它们是模棱两可的,你会得到一个编译错误。PredefEnsuringArrowAssocselectDynamic

总之,这是两个事实的不幸结果:

  • 有隐式转换为x所有内容提供成员
  • 这种转换优先于Dynamic治疗。

在您的实例中,解决此问题的方法是显式声明xin Client,作为和的转发selectDynamicupdateDynamic

class Client extends Dynamic {
  var map = Map.empty[String, Any]
  def selectDynamic(name: String) = map get name getOrElse sys.error("field not found")
  def updateDynamic(name: String)(value: Any) { map += name -> value }

  // Work around the annoying implicits in Predef in Scala 2.10.
  def x: Any = selectDynamic("x")
  def x_=(value: Any): Unit = updateDynamic("x")(value)
}

现在,client.x当然会使用显式声明x及其 in 的设置器x_=Client它将委托给selectDynamic/ updateDynamic

于 2016-05-03T18:22:07.680 回答