scala 2.10 编译器中 scala.language.dynamics 错误的解决方法
Workaround for a scala.language.dynamics bug in scala 2.10 compiler
我一直在尝试为一个开源项目生成一个 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,没问题,虽然我需要 cross-compile,所以那不是 work-around。
另一个线索是我可以通过更改这行代码来隐藏问题:
client.x = 1.0
对此:
client.xx = 1.0
我直接用scalac 2.10.6编译也看到了这个问题
作为解决方法,我可以重构项目以使用比单个字符长的字段名称,尽管因为这不是我的项目,所以我在某种程度上限制了我可以接受的解决方法。此外,这是一个 breeze.linalg 项目,如果不允许使用单个字符矩阵和向量名称将是一个严重的限制。
花了几个小时才将问题归结为来自一个较大项目的这段代码片段,我不想对这个开源库的 scala 2.10 版本强加限制。由于此错误似乎已在 scala 2.11 中修复,我假设已决定不将修复程序反向移植到 2.10。
我更改了标题以反映存在解决方法(更长的字段名称)。
这是 Scala 2.10 的错,不是 sbt 的错。
问题在于,在Predef
, whose content is imported in every single Scala file, there are two problematic classes: Ensuring
and ArrowAssoc
。这两个 类 的成员可通过对 any 类型值的隐式转换获得。例如,ArrowAssoc
是您可以 1 -> 2
构造元组 (1, 2)
.
的原因
现在,这些 类 在 2.10 中非常不幸地 属性 声明了一个名为 x
的成员!即使在 2.10 中已弃用,scala.Dynamic
.
的用法仍然存在严重问题
在您的代码中,client.x = 1.0
首先测试 client.x
是否作为 val/getter 存在于 client
的类型中。它确实不是,但是如果我们使用 Predef
中的隐式转换将其转换为 Ensuring
或 ArrowAssoc
,它 将 可用。由于隐式转换比 selectDynamic
处理具有更高的优先级,因此 Scala 编译器会尝试使用它们。但是由于有两个同样有效的转换,它们是不明确的,你会得到一个编译错误。
总而言之,这是两个事实的不幸后果:
- 存在隐式转换,在 everything
上提供成员 x
- 该转换优先于
Dynamic
处理。
在您的实例中,解决此问题的方法是在 Client
中显式声明 x
,作为 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
及其在Client
中的setterx_=
,这将委托给selectDynamic
/updateDynamic
.
我一直在尝试为一个开源项目生成一个 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,没问题,虽然我需要 cross-compile,所以那不是 work-around。
另一个线索是我可以通过更改这行代码来隐藏问题:
client.x = 1.0
对此:
client.xx = 1.0
我直接用scalac 2.10.6编译也看到了这个问题
作为解决方法,我可以重构项目以使用比单个字符长的字段名称,尽管因为这不是我的项目,所以我在某种程度上限制了我可以接受的解决方法。此外,这是一个 breeze.linalg 项目,如果不允许使用单个字符矩阵和向量名称将是一个严重的限制。
花了几个小时才将问题归结为来自一个较大项目的这段代码片段,我不想对这个开源库的 scala 2.10 版本强加限制。由于此错误似乎已在 scala 2.11 中修复,我假设已决定不将修复程序反向移植到 2.10。
我更改了标题以反映存在解决方法(更长的字段名称)。
这是 Scala 2.10 的错,不是 sbt 的错。
问题在于,在Predef
, whose content is imported in every single Scala file, there are two problematic classes: Ensuring
and ArrowAssoc
。这两个 类 的成员可通过对 any 类型值的隐式转换获得。例如,ArrowAssoc
是您可以 1 -> 2
构造元组 (1, 2)
.
现在,这些 类 在 2.10 中非常不幸地 属性 声明了一个名为 x
的成员!即使在 2.10 中已弃用,scala.Dynamic
.
在您的代码中,client.x = 1.0
首先测试 client.x
是否作为 val/getter 存在于 client
的类型中。它确实不是,但是如果我们使用 Predef
中的隐式转换将其转换为 Ensuring
或 ArrowAssoc
,它 将 可用。由于隐式转换比 selectDynamic
处理具有更高的优先级,因此 Scala 编译器会尝试使用它们。但是由于有两个同样有效的转换,它们是不明确的,你会得到一个编译错误。
总而言之,这是两个事实的不幸后果:
- 存在隐式转换,在 everything 上提供成员
- 该转换优先于
Dynamic
处理。
x
在您的实例中,解决此问题的方法是在 Client
中显式声明 x
,作为 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
及其在Client
中的setterx_=
,这将委托给selectDynamic
/updateDynamic
.