2009-08-31 3 views
28

私は最近、Scalaの学習を開始しましたが、ジェネリックもタイプ消去によって実装されていることに失望しました(しかし驚くことではありません)。ScalaがJVMを変更せずにジェネリックを再現することは可能ですか?

私の質問は、Scalaがジェネリックを再現することは可能でしょうか、あるいはJVMを何らかの方法で変更する必要がありますか? JVMを変更する必要がある場合は、正確に何を変更する必要がありますか?

答えて

22

いいえ - バイトコードが標準化されたジェネリックをサポートしていない場合、ScalaはJava相当のバイトコードとして実行できません。

「何を変更する必要がありますか?の場合、答えはバイトコード指定です。現在、バイトコードでは、パラメータ化された型の変数を定義することはできません。正規化されたジェネリックをサポートするためのバイトコードを変更するとbreak backwards compatibility、そのgenerics would have to be implemented via type erasureとなることが判明しました。

これを回避するために、Scalaは、実行時に型情報を発見するために、任意の範囲にインポートすることができるManifestを定義するために、そのimplicit機構の動力を用いました。マニフェストは実験的で、大部分は文書化されていませんが、are coming as part of the library in 2.8です。ここに別の良いリソースがありますScala reified generics/Manifests

+0

私がこれを読むことができるリンクはありますか? – cdmckay

+0

話すときにリンクが追加されました:-) –

+0

Scalaの人たちがこれをやっていることはわかりませんでした。これはとても涼しいです。 – cdmckay

3

「暗黙のマニフェスト」はScalaコンパイラのトリックであり、Scalaでのジェネリック化は不可能です。 Scalaコンパイラは、 "暗黙のm:Manifest [A]"パラメータとの関数を見ると、コールサイトで汎用タイプAを知っていれば、Aのクラスとその汎用型パラメータをマニフェストにラップします関数内で使用できるようにします。しかし、真のタイプのAを理解できなかった場合、マニフェストを作成する方法はありません。言い換えれば、内部関数がそれを必要とする場合、マニフェストは関数呼び出しチェーンに沿って渡されなければならない。

scala> def typeName[A](a: A)(implicit m: reflect.Manifest[A]) = m.toString 
typeName: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String 

scala> typeName(List(1)) 
res6: java.lang.String = scala.collection.immutable.List[int] 

scala> def foo[A](a: A) = typeName(a) 
<console>:5: error: could not find implicit value for parameter m:scala.reflect.Manifest[A]. 
     def foo[A](a: A) = typeName(a) 
           ^

scala> def foo[A](a: A)(implicit m: reflect.Manifest[A]) = typeName(a) 
foo: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String 

scala> foo(Set("hello")) 
res8: java.lang.String = scala.collection.immutable.Set[java.lang.String] 
+0

ジェネリックスを持つすべてのメソッド呼び出しに目に見えない形でマニフェストを追加してみませんか?マニフェストはパフォーマンスペナルティを被るか? – cdmckay

+0

私は、ここにいる誰かがマニフェストによってジェネリック医薬品が真正であると言っているかどうかはわかりません(あなたの含意と思われる)。私はマニフェストをインポートして実行時に型情報を得ることができますが、reifiedジェネリックスではバイトコードの仕様を変更する必要があると言いました。 –

+0

@oxbow_lakes私の答えがそのように出てきたら、すみません。私は、「マニフェストがジェネリック医薬品を再確認する」ということについて誰かが何か言っていることを暗示するつもりはありませんでした。私が指摘したいのは、マニフェストを使用する際のいくつかの制限です。 –

0

scalac一度はそれがデータ構造が具体化ジェネリックを実装するために必要なものは何でもして生成されたコードを飾ることができるという可能性を秘めている、コンパイラです。私は何を意味

scalacが...

// definition 
class Klass[T] { 
    value : T 
} 

//calls 
floats = Klass[float] 
doubles = Klass[double] 

を見る能力を持っている...そして、このような何かに "拡大" ということである。

// definition 
class Klass_float { 
    value : float 
} 
class Klass_double { 
    value : double 
} 

// calls 
floats = Klass_float 
doubles = Klass_double 

編集

要点は次のとおりです。コンパイラは、必要なすべてのデータ構造を作成する能力を備えています実行時に追加の型情報を提供するために必要である。この型情報が利用可能になると、Scalaランタイムはそれを利用して、想像できるすべての型認識動作を実行できます。 JVMがジェネリック化のためにバイトコードを提供するかどうかは関係ありません。この作業は、JVMではなくScalaライブラリによって行われます。

すでにシンボリックデバッガを作成している場合(私がしました!)、コンパイル時にコンパイラが持っているすべての情報を生成されたバイナリに基本的に 'ダンプ'できることを知っています。これはまったく同じ考えです:Scalaコンパイラが持つすべての型情報を 'ダンプ'します。

簡潔に言えば、JVMが汎用ジェネリックのネイティブ操作を提供するかどうかは問題ではありません。

別の編集

IBM X10は、私が話しているの能力を示しています。それは、Javaプラットフォーム上に具体化ジェネリックを活用し、JavaコードにX10コードをコンパイルします。前にも触れましたが、これは実行可能ですが、コンパイラの動作を知っている人と、コンパイル時に生成されたコードにコンパイラが "ダンプする"情報をランタイムライブラリがどのように活用できるかだけを知っている人でなければなりません。さらに詳しい情報:http://x10.sourceforge.net/documentation/papers/X10Workshop2012/slides/Takeuchi.pdf

+0

これには2つの問題があります。まず、コードサイズを増やします。第二に、コンパイル時に生成しなければならないクラスをすべて知っているわけではありません。たとえば、典型的に/潜在的にコンパイルされた形式のライブラリからのコレクション実装を考えてみましょう。あなたはあなたの新しいクラス 'RichardsFoo'で動作すると期待しています。 – Raphael

+0

これは_erased_コンパイルされたクラスを格納することで簡単にバイパスされます。だからscalacがKlass [SomethingStrange]に遭遇すると、それは型消去されたKlassを取得し、Klass_SomethingStrangeをインスタンス化します。これは.NETが実行時にどのように動作するかを示しています。 – Cyberax

+1

@Raphael:コードサイズを乗算するという事実は、コスト/利益が悪いと考える場合にのみ問題になります。次に、http://fastutil.di.unimi.it/をご覧ください。特殊な実装が汎用APIの上に存在するという例があります。ここでの問題は、コードサイズの乗算です。私が前に言ったように、それは問題かもしれないし、問題ではないかもしれない。 –

3

oxbow_lakesを補完する答え:それは不可能であり、決して起こらないようです(少なくともすぐに)。

(反駁)は、JVMが具体化ジェネリックをサポートしていません理由からのようだ:

  • 低いパフォーマンス。
  • 下位互換性が壊れます。これは、多くのライブラリの複製と修正を解決することができます。
  • マニフェストを使用して実装できます。「ソリューション」と最大の障害です。

参照:

簡単にベンチマークしてpを見ることができます誤差の影響は非常に です。特にメモリ消費量が増加します。

私は、 がManifests/TypeTagsを使ってScalaでやっているように、オプションの証明をすることをお勧めします。

実行時の特殊化が可能な場合は、 の高性能と汎用コードを目指すことができます。しかし、それはおそらく Scala 2.12または2.13の目標です。

+0

オーデスキーの見積もり+1。私は強く同意する。 – Ingo

関連する問題