8

Scala のコマンド ライン REPL を使用する:

def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}

与える

error: type mismatch;
found: Int(2)
required: String

REPL でオーバーロードされた再帰メソッドを定義できないようです。これは Scala REPL のバグだと思って提出しましたが、ほとんど即座に「wontfix: この 2 つのメソッドをコンパイルする必要があるため、インタープリターのセマンティクスを考えると、これをサポートできる方法がわかりません」で閉じられました。一緒。" 彼は、メソッドを囲んでいるオブジェクトに入れることを勧めました。

その理由を説明できる JVM 言語の実装または Scala の専門家はいますか? たとえば、メソッドが互いに呼び出されると問題になることがわかりますが、この場合はどうなりますか?

または、これが大きすぎる質問で、前提知識がもっと必要だと思われる場合は、言語の実装、特に JVM に関する書籍やサイトへの適切なリンクを誰か教えてください。(John Rose のブログと本 Programming Language Pragmatics については知っていますが、それだけです。:)

4

4 に答える 4

11

この問題は、インタープリターが既存の要素をオーバーロードするのではなく、指定された名前に置き換える必要があることが最も多いという事実によるものです。たとえば、私はしばしば何かを実験して実行し、しばしば と呼ばれるメソッドを作成しますtest:

def test(x: Int) = x + x

少し後で、別の実験を実行していて、最初の実験とは関係のない という名前の別のメソッドを作成したとしましょうtest:

def test(ls: List[Int]) = (0 /: ls) { _ + _ }

これは完全に非現実的なシナリオではありません。実際、ほとんどの人がインタプリタをそのように使用しており、多くの場合それを意識することさえありません。インタープリターが恣意的に の両方のバージョンをtestスコープ内に保持することを決定した場合、test を使用する際に紛らわしいセマンティックの違いが生じる可能性があります。たとえば、 を呼び出して、誤ってではなくtestを渡す可能性があります(世界で最もありそうもない事故ではありません)。IntList[Int]

test(1 :: Nil)  // => 1
test(2)         // => 4  (expecting 2)

時間が経つにつれて、インタープリターのルート スコープは、さまざまなバージョンのメソッド、フィールドなどで信じられないほど雑然とします。私は、一度に何日もインタープリターを開いたままにしておく傾向がありますが、このようなオーバーロードが許可されている場合、"物事が混乱しすぎるようになったので、時々インタプリタを「フラッシュ」してください。

これは JVM や Scala コンパイラの制限ではなく、意図的な設計上の決定です。バグで述べたように、ルート スコープ以外の範囲内にいる場合でもオーバーロードできます。クラス内にテスト メソッドを含めることは、私にとって最良の解決策のように思えます。

于 2008-09-23T17:06:52.137 に答える
5
% scala28
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def foo(x: Int): Unit = () ; def foo(x: String): Unit = { println(foo(2)) } 
foo: (x: String)Unit <and> (x: Int)Unit
foo: (x: String)Unit <and> (x: Int)Unit

scala> foo(5)

scala> foo("abc")
()
于 2010-09-08T04:03:22.987 に答える
4

両方の行を同時にコピーして貼り付けると、REPL は受け入れます。

于 2008-09-23T15:15:29.673 に答える
1

extempore's answer に示されているように、過負荷になる可能性があります。デザインの決定に関するダニエルのコメントは正しいですが、不完全で少し誤解を招くと思います。オーバーロードが禁止されることはありませんが (可能であるため)、簡単には達成できません。

これにつながる設計上の決定は次のとおりです。

  1. 以前のすべての定義が利用可能でなければなりません。
  2. 毎回入力されたものすべてを再コンパイルするのではなく、新しく入力されたコードのみがコンパイルされます。
  3. 定義を再定義できる必要があります (Daniel が述べたように)。
  4. クラスやオブジェクトだけでなく、vals や defs などのメンバーを定義できる必要があります。

問題は、これらすべての目標をどのように達成するかです。あなたの例をどのように処理しますか?

def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}

4 番目の項目から始まる Avalまたはは、 、、またはdef内でのみ定義できます。したがって、REPL は、このように定義をオブジェクト内に配置します (実際の表現ではありません! ) 。classtraitobjectpackage object

package $line1 { // input line
  object $read { // what was read
    object $iw { // definitions
      def foo(x: Int): Unit = {}
    }
    // val res1 would be here somewhere if this was an expression
  }
}

現在、JVM の仕組みにより、それらの 1 つを定義すると、それらを拡張することはできません。もちろん、すべてを再コンパイルすることもできますが、それは破棄しました。したがって、別の場所に配置する必要があります。

package $line1 { // input line
  object $read { // what was read
    object $iw { // definitions
      def foo(x: String): Unit = { println(foo(2)) }
    }
  }
}

そして、これはあなたの例がオーバーロードではない理由を説明しています.2つの異なる場所で定義されています. それらを同じ行に入れると、それらはすべてまとめて定義され、extempore の例に示すようにオーバーロードになります。

その他の設計上の決定に関しては、新しいパッケージごとに以前のパッケージから定義と "res" がインポートされ、インポートは互いにシャドーイングできるため、"再定義" が可能になります。

于 2012-06-30T19:17:07.280 に答える