なぜ構造タイピングのためのコンパイル時間生成技術が個別の編集を防ぐのですか?
-
29-09-2019 - |
質問
私は読んでいた(OK、スキミング)DubochetとOdersky's JVMに構造タイプをコンパイルします 次の主張に混乱していました。
生成技術は、JVMの構造タイプを表すためのJavaインターフェイスを作成します。このような手法の複雑さは、プログラム内のどこでも構造タイプとして使用されるすべてのクラスが適切なインターフェイスを実装する必要があるという点にあります。 これがコンパイル時に行われると、個別のコンピレーションを防ぎます。
(強調が追加されました)
論文のオートクロース例を考えてみましょう。
type Closeable = Any { def close(): Unit }
def autoclose(t: Closeable)(run: Closeable => Unit): Unit = {
try { run(t) }
finally { t.close }
}
のインターフェイスを生成できませんでした Closeable
次のように入力します:
public interface AnonymousInterface1 {
public void close();
}
の定義を変えます autoclose
に
// UPDATE: using a view bound here, so implicit conversion is applied on-demand
def autoclose[T <% AnonymousInterface1](t: T)(run: T => Unit): Unit = {
try { run(t) }
finally { t.close }
}
次に、コールサイトを検討します autoclose
:
val fis = new FileInputStream(new File("f.txt"))
autoclose(fis) { ... }
以来 fis
aです FileInputStream
, 、実装していません AnonymousInterface1
, 、ラッパーを生成する必要があります。
class FileInputStreamAnonymousInterface1Proxy(val self: FileInputStream)
extends AnonymousInterface1 {
def close() = self.close();
}
object FileInputStreamAnonymousInterface1Proxy {
implicit def fis2proxy(fis: FileInputStream): FileInputStreamAnonymousInterface1Proxy =
new FileInputStreamAnonymousInterface1Proxy(fis)
}
私は行方不明になっているに違いありません なにか, 、しかし、それが何であるかは私には不明です。なぜこのアプローチは個別の編集を防ぐのでしょうか?
解決
私は実際にあなたが説明する暗黙のアプローチ(タイプクラスを使用)を使用します Scala Arm Library. 。これは問題に対する手作業のソリューションであることを忘れないでください。
ここでの最大の問題は、暗黙の解決です。コンパイラはその場でラッパーを生成しません。事前にそうして、それらが暗黙の範囲であることを確認する必要があります。これは、(Scala-Armの場合)可能な限りリソースに「一般的な」ラッパーを提供し、適切なラッパーが見つからない場合に反射ベースのタイプに戻ることを意味します。これにより、ユーザーが通常の暗黙のルールを使用して独自のラッパーを指定できるようにするという利点が得られます。
見る: リソースタイプ - 特性 そして、そのすべてが事前に定義されたラッパーです。
また、暗黙の解像度の魔法をより詳細に説明するこの手法についてブログを書きました。 モンキーパッチ、アヒルのタイピング、タイプクラス.
いずれにせよ、構造タイプを使用するたびに、タイプクラスを手で手に入れたくないでしょう。実際にコンパイラがインターフェイスを自動的に作成し、魔法を実行することを望んでいた場合、乱雑になる可能性があります。構造タイプを定義するたびに、コンパイラはインターフェイスを作成する必要があります(おそらくエーテルのどこか?)。ここで、これらのものに名前空間を追加する必要があります。また、コールごとに、コンパイラは何らかのラッパーインプルメントクラスを生成する必要があります(再び名前空間問題があります)。最後に、個別にコンパイルされているのと同じ構造タイプの2つの異なる方法がある場合、必要なインターフェイスの数を爆発させました。
ハードルを克服できなかったわけではありませんが、特定のタイプの「直接」アクセスを備えた構造タイピングが必要な場合は、今日のタイプ - 特性パターンが最善の策のようです。
他のヒント
私が思い出すように 話し合い に Scala-Inernals メーリングリスト、これの問題はオブジェクトIDです。これは、コンパイルの現在のアプローチによって保存されていますが、値をラップすると失われます。
それについて考えてください。クラスAを検討してください
class A { def a1(i: Int): String = { ... }; def a2(s: String): Boolean = { ... }
プログラムの一部の場所、おそらく別々にコンパイルされたライブラリで、この構造タイプが使用されています。
{ def a1(i: Int): String }
そして他の場所では、これが使用されます:
{ def a2(s: String): Boolean }
グローバル分析とは別に、クラスAは、それらの遠方構造タイプが指定されている場所で使用できるようにするために必要なインターフェイスでどのように装飾されますか?
特定のクラスが適合できるすべての可能な構造タイプが、その構造タイプをキャプチャするインターフェイスを生成するために使用される場合、そのようなインターフェイスの爆発があります。構造タイプが複数の必要なメンバーに言及する可能性があることを忘れないでください。したがって、nパブリック要素(valsまたはdef)を持つクラスの場合、それらのnのすべての可能なサブセットが必要であり、それがCardinalityが2^nであるNのパワーセットです。