2016-05-03 14 views
1

プロジェクト内で参照したいオープンソースプロジェクト用のSBTビルドを生成しようとしていましたが、コンパイラのバグです。scala 2.10コンパイラのscala.language.dynamicsバグの回避策

次のコードはコンパイルして実行します日食/ 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指定

:sの

[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 

:= 2.10.6を、私は以下のコンパイルエラーを取得calaVersion:= 2.11.8、問題はありませんが、クロスコンパイルする必要がありますが、これは回避策ではありません。これに

client.x = 1.0 

::私はscalac 2.10と直接コンパイルする場合

client.xx = 1.0 

私も問題を参照してください

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

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

大きなプロジェクトからこの問題を解決するまで数時間かかってしまいましたが、私はこのオープンソースライブラリのscala 2.10バージョンに制限をかけたくないです。このバグはscala 2.11で修正されているようですので、2.10に修正をバックポートしないことに決めました。

解決方法(長いフィールド名)の存在を反映するようにタイトルを変更しました。

+0

クリーンでコンパイルしてコンパイルした場合、エラーが発生しますか? – Martin

+0

はい、非常に安定しており、繰り返し可能です。また、重複するのはとても簡単、あなただけのsrc /メイン/スカラ座/ fooの/ Client.scalaにあるファイルに上記のソースコードを貼り付け、その後、SBT 0.13.11でコンパイルしようとする必要があります(それはScalaの2.10にデフォルト設定されます。 6、起こるように)。 – philwalk

+1

私はscalac 2.10.6を直接実行しようとしましたが、同じエラーが発生するので、これはsbtのエラーではないと思います。 sbtでは 'compile'を実行し、' last'を実行してscalacに与えられた正確なパラメータを取得します。編集:スカラREPLと同じですが、ライブラリの依存関係は必要ありません。 – Martin

答えて

2

これはScrab 2.10の欠陥であり、sbtのものではありません。

Predefには、すべてのScalaファイルにコンテンツがインポートされていますが、問題のあるクラスはEnsuringArrowAssocの2つです。これらの2つのクラスのメンバーは、の暗黙的な変換によって利用可能です。値はです。例えば、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コンパイラはそれらを使用しようとします。しかし、2つの同等の有効な変換があるため、それらはあいまいであり、コンパイルエラーが発生します。

  • すべての変換がDynamic治療に優先していることを
  • にメンバーxを提供する暗黙的な変換があります。要するに

    が、これは2つの事実の不幸な結果です。

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

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に委任され、selectDynamic/updateDynamicに委任されます。

+0

ありがとう、私は私のケーキを持って、それも食べることができるようです:) – philwalk

関連する問題