33

少し前に、Oracle は Java 8 に Closures を追加するのは良い考えだと判断しました。当初からクロージャがあった Scala と比較して、設計上の問題がどのように解決されているのだろうか。

javac.infoからの未解決の問題の引用:

  1. 関数型にメソッド ハンドルを使用できますか? それを機能させる方法は明らかではありません。1 つの問題は、メソッド ハンドルが型パラメーターを具体化することですが、関数のサブタイピングを妨げる方法です。

  2. 「スロー」型パラメーターの明示的な宣言を取り除くことはできますか? アイデアは、宣言された境界がチェックされた例外型である場合は常に分離型推論を使用することです。これは厳密な下位互換性はありませんが、実際の既存のコードを壊す可能性は低いです。ただし、構文のあいまいさのために、おそらく型引数の「スロー」を取り除くことはできません。

  3. 古いスタイルのループ インデックス変数で @Shared を許可しない

  4. 複数のメソッドを定義する Comparator などのインターフェイスを処理します。1つを除くすべてのメソッドは、Object から継承されたメソッドによって実装されます。「単一のメソッドとのインターフェース」の定義は、オブジェクトのメソッドによって実装されないメソッドのみをカウントする必要があり、それらの 1 つを実装するとそれらすべてが実装される場合は、複数のメソッドを 1 つとしてカウントする必要があります。主に、これには、インターフェースが単一の抽象メソッドのみを持つことが何を意味するかについて、より正確な仕様が必要です。

  5. 関数型からインターフェイスへのマッピングを指定します:名前、パラメーターなど。関数型からシステム生成インターフェイスへのマッピングを正確に完全に指定する必要があります。

  6. 型推論。型推論の規則は、例外型パラメータの推論に対応するために拡張する必要があります。同様に、クロージャ変換で使用されるサブタイプの関係も反映する必要があります。

  7. 例外の透明性を改善するために、例外タイプのパラメーターを削除しました。 おそらく、除外された例外タイプのパラメーターが境界を意味するようにします。これにより、java.util.concurrent.Callable など、例外の型パラメーターを持たない既存の汎用インターフェイスを、新しい汎用例外パラメーターを追加することで後付けできます。

  8. 関数型のクラス リテラルはどのように形成されますか? #void().class ですか? もしそうなら、オブジェクトタイプが消去された場合、どのように機能しますか? #?(?).class ですか?

  9. システム クラス ローダーは、関数型インターフェイスを動的に生成する必要があります。 関数型に対応するインターフェイスは、ブートストラップ クラス ローダーによってオンデマンドで生成される必要があるため、それらをすべてのユーザー コードで共有できます。プロトタイプの場合、javac にこれらのインターフェースを生成させて、プロトタイプで生成されたコードをストック (JDK5-6) VM で実行できるようにすることができます。

  10. ラムダ式の評価は毎回新しいオブジェクトを生成する必要がありますか? うまくいけば、そうではありません。たとえば、ラムダが外側のスコープから変数をキャプチャしない場合は、静的に割り当てることができます。同様に、他の状況では、ループ内で宣言された変数をキャプチャしない場合、ラムダを内側のループの外に移動できます。したがって、仕様がラムダ式の結果の参照同一性について何も約束しないのが最善であり、そのような最適化はコンパイラーによって実行されます。

私が理解している限り、2.、6.、および 7. は Scala では問題ではありません。Scala は、Java のようなある種の「シャドウ型システム」として Checked Exceptions を使用しないためです。

残りはどうですか?

4

2 に答える 2

29

1) 関数型にメソッド ハンドルを使用できますか?

Scala は、メソッド ハンドルを持たない JDK 5 および 6 をターゲットにしているため、まだその問題に対処しようとはしていません。

2) 「スロー」型パラメータの明示的な宣言を取り除くことはできますか?

Scala にはチェック例外がありません。

3) 古いスタイルのループ インデックス変数で @Shared を許可しない。

Scala にはループ インデックス変数がありません。それでも、同じ考えはある種の while ループで表現できます。Scala のセマンティクスは、ここではかなり標準的です。シンボルバインディングがキャプチャされ、シンボルがたまたま変更可能な参照セルにマップされた場合は、自分の頭でそれを行います。

4) 複数のメソッドを定義する Comparator のようなインターフェイスを処理する

Scala ユーザーは、関数 (または暗黙の関数) を使用して、適切な型の関数をインターフェイスに強制する傾向があります。例えば

[implicit] def toComparator[A](f : (A, A) => Int) = new Comparator[A] { 
    def compare(x : A, y : A) = f(x, y) 
}

5) 関数型からインターフェイスへのマッピングを指定します。

Scala の標準ライブラリには、0 <= N <= 22 の FuncitonN 特性が含まれており、仕様では、関数リテラルがそれらの特性のインスタンスを作成すると述べています。

6) 型推論。型推論の規則は、例外型パラメータの推論に対応するために拡張する必要があります。

Scala にはチェック例外がないため、この問題全体をパントできます。

7) 例外の透明性を改善するために、例外タイプのパラメーターを削除しました。

同じ取引で、チェック例外はありません。

8) 関数型のクラス リテラルはどのように形成されますか? #void().class ですか? もしそうなら、オブジェクトタイプが消去された場合、どのように機能しますか? #?(?).class ですか?

classOf[A => B] //or, equivalently, 
classOf[Function1[A,B]]

型消去は型消去です。上記のリテラルは、A と B の選択に関係なく、scala.lang.Function1 を生成します。必要に応じて、次のように記述できます。

classOf[ _ => _ ] // or
classOf[Function1[ _,_ ]]

9) システム クラス ローダーは、関数型インターフェイスを動的に生成する必要があります。

Scala は、FunctionN クラスを動的に生成する必要がないように、引数の数を最大 22 に任意に制限します。

10) ラムダ式の評価は毎回新しいオブジェクトを生成しなければなりませんか?

Scala の仕様では、そうしなければならないとは言っていません。しかし、2.8.1 の時点で、コンパイラは、ラムダがその環境から何もキャプチャしない場合を最適化しません。私はまだ 2.9.0 でテストしていません。

于 2011-05-26T18:20:56.080 に答える
12

ここでは 4 番のみを扱います。

Java の「クロージャー」が他の言語に見られるクロージャーと異なる点の 1 つは、関数を記述しないインターフェイスの代わりに使用できることRunnableです。これが、SAM、Single Abstract Method の意味です。

Javaがこれを行うのは、これらのインターフェースがJavaライブラリに豊富にあり、Javaが関数型またはクロージャなしで作成されたため、Javaライブラリに豊富にあるためです。それらが存在しない場合、制御の反転を必要とするすべてのコードは、SAM インターフェイスの使用に頼らなければなりませんでした。

たとえば、並べ替える配列のメンバー間の比較を実行Arrays.sortするオブジェクトを受け取ります。Comparator対照的に、Scala はList[A]function を受け取ることで aをソートでき(A, A) => Int、これはクロージャを介して簡単に渡されます。ただし、末尾の注 1 を参照してください。

したがって、Scala のライブラリは関数型とクロージャを備えた言語用に作成されているため、Scala で SAM クロージャなどをサポートする必要はありません。

もちろん、Scala と Java の相互運用性の問題もあります。Scala のライブラリは SAM のようなものを必要としないかもしれませんが、Javaライブラリには必要です。解決できる方法は 2 つあります。まず、Scala はクロージャーと関数型をサポートしているため、ヘルパー メソッドの作成は非常に簡単です。例えば:

def runnable(f: () => Unit) = new Runnable {
    def run() = f()
}

runnable { () => println("Hello") } // creates a Runnable

実際、この特定の例は、Scala の by-name パラメーターを使用してさらに短くすることができますが、それは重要ではありません。とにかく、これは Java がやろうとしていることではなく、Java ができたはずのことです。SAM インターフェイスの普及を考えると、それほど驚くべきことではありません。

Scala がこれを処理するもう 1 つの方法は、暗黙的な変換を使用することです。implicit上記のメソッドの先頭に追加するだけで、 aが必要で関数が提供されている場合はrunnableいつでも自動的に (注 2) 適用されるメソッドが作成されます。Runnable() => Unit

ただし、暗黙は非常にユニークであり、まだある程度議論の余地があります。

注1:実際、この特定の例は悪意を持って選択されました... Comparator1つではなく2つの抽象メソッドがあり、これが全体の問題です。そのメソッドの 1 つは別の観点から実装できるため、抽象リストから防御メソッドを「差し引く」だけだと思います。

そして、Scala 側では(A, A) => Boolean、 ではなくを使用するソート メソッドがありますが(A, A) => Int、標準のソート メソッドはOrderingオブジェクトを呼び出します。これは、Java のComparator! ただし、Scala の場合、は型 classOrderingの役割を果たします。

注 2 : Implicits は、scope にインポートされると、自動的に適用されます。

于 2011-05-26T22:49:51.170 に答える