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 では、問題はありませんが、クロスコンパイルする必要があるため、回避策ではありません。

もう 1 つの手がかりは、次のコード行を変更することで問題を隠すことができるということです。

client.x = 1.0

これに:

client.xx = 1.0

scalac 2.10.6 で直接コンパイルした場合にも問題が発生します。

回避策として、プロジェクトをリファクタリングして 1 文字より長いフィールド名を使用することができますが、これは私のプロジェクトではないため、回避策として受け入れることができるものには多少の制約があります。また、これは Breeze.linalg プロジェクトであり、単一文字の行列名とベクトル名を許可しないことは深刻な制限となります。

問題を大規模なプロジェクトからこのコード フラグメントに要約するのに数時間かかりました。このオープン ソース ライブラリの scala 2.10 バージョンに制限を課したくありません。このバグは scala 2.11 で修正されたようですので、修正を 2.10 にバックポートしないことが決定されたと思います。

回避策の存在を反映するようにタイトルを変更しました (より長いフィールド名)。

4

1 に答える 1

2

これは sbt のせいではなく、Scala 2.10 のせいです。

問題は、Predefコンテンツがすべての Scala ファイルにインポートされる には、2 つの問題のあるクラスがあることです:EnsuringArrowAssoc. これら 2 つのクラスのメンバーは、任意の型の値に対する暗黙的な変換を通じて利用できます。ArrowAssoc、たとえば、1 -> 2タプルを構築するためにできる理由です(1, 2)

さて、これらのクラスには、2.10 で、x!という名前のメンバーを宣言するという非常に残念な特性がありました。2.10 で廃止されましたが、 の使用には深刻な問題が存在しscala.Dynamicます。

コードでは、最初に の型の val/getter として存在するclient.x = 1.0かどうかをテストします。実際にはそうではありませんが、暗黙的な変換を使用してorに変換すると利用可能になります。暗黙的な変換は処理よりも優先度が高いため、Scala コンパイラーはそれらを使用しようとします。しかし、同等に有効な変換が 2 つあるため、あいまいであり、コンパイル エラーが発生します。client.xclientPredefEnsuringArrowAssocselectDynamic

要約すると、これは 2 つの事実の残念な結果です。

  • すべてxにメンバーを提供する暗黙の変換があります
  • その変換はDynamic治療よりも優先されます。

あなたのインスタンスでこれを解決する方法は、 をとへのフォワーダーとして明示的xに宣言することです:ClientselectDynamicupdateDynamic

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_=し、 /Clientに委譲します。selectDynamicupdateDynamic

于 2016-05-03T18:22:07.680 に答える