我一直在尝试为我想在项目中引用的开源项目生成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中似乎修复了这个bug,我假设决定不将修复程序反向移植到2.10。
我更改了标题以反映变通方法的存在(更长的字段名称)。
答案 0 :(得分:2)
这是Scala 2.10的错,不是sbt的。
问题是,在Predef
中,其内容是在每个单独的Scala文件中导入的,有两个有问题的类:Ensuring
和ArrowAssoc
。这两个类的成员可通过任何类型的值的隐式转换获得。例如,ArrowAssoc
是您1 -> 2
构建元组(1, 2)
的原因。
现在,这些类在2.10中声明了一个名为x
的成员的非常不幸的属性!即使在2.10中被弃用,但scala.Dynamic
的使用存在严重问题。
在您的代码中,client.x = 1.0
首先测试client.x
是否作为client
类型的val / getter存在。它实际上并非如此,但如果我们使用Predef
中的隐式转换将其转换为Ensuring
或ArrowAssoc
,那么将可用。由于隐式转换的优先级高于selectDynamic
处理,因此Scala编译器会尝试使用它们。但是由于有两个同样有效的转换,它们是模糊的,并且您得到编译错误。
总之,这是两个事实的不幸后果:
x
Dynamic
处理。在您的实例中,解决此问题的方法是在x
中明确声明Client
,作为selectDynamic
和updateDynamic
的转发器:
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
中使用明确声明的x_=
及其setter Client
,它将委托给selectDynamic
/ {{1 }}