Используя командную строку 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: я не вижу никакого способа поддержать это, учитывая семантику интерпретатора, потому что эти два метода должны быть скомпилированы. все вместе." Он рекомендовал поместить методы во включающий объект.
Есть ли реализация языка JVM или эксперт по Scala, который мог бы объяснить, почему? Я вижу, если бы методы вызывали друг друга, например, было бы проблемой, но в этом случае?
Или, если это слишком большой вопрос, и вы думаете, что мне нужно больше предварительных знаний, есть ли у кого-нибудь хорошие ссылки на книги или сайты о языковых реализациях, особенно на JVM? (Я знаю о блоге Джона Роуза и книге Programming Language Pragmatics ... но это все. :)





REPL примет, если вы скопируете обе строки и вставите их одновременно.
Проблема связана с тем, что интерпретатору чаще всего приходится заменять существующих элементов с заданным именем, а не перегружать их. Например, я часто экспериментирую с чем-то, часто создавая метод под названием test:
def test(x: Int) = x + x
Чуть позже допустим, что я провожу эксперимент разные и создаю другой метод с именем test, не связанный с первым:
def test(ls: List[Int]) = (0 /: ls) { _ + _ }
Это не совсем нереальный сценарий. Фактически, именно так большинство людей используют интерпретатор, часто даже не осознавая этого. Если интерпретатор произвольно решил оставить обе версии test в области видимости, это могло привести к запутанным семантическим различиям в использовании теста. Например, мы могли бы вызвать на test, случайно передавая Int, а не List[Int] (не самая маловероятная авария в мире):
test(1 :: Nil) // => 1
test(2) // => 4 (expecting 2)
Со временем корневая область видимости интерпретатора будет невероятно загромождена различными версиями методов, полей и т. д. Я обычно оставляю свой интерпретатор открытым на несколько дней, но если бы такая перегрузка была разрешена, мы были бы вынуждены: очищайте "интерпретатор" так часто, как это может сбивать с толку.
Это не ограничение JVM или компилятора Scala, это осознанное дизайнерское решение. Как упоминалось в ошибке, вы все равно можете перегрузить, если находитесь в пределах чего-то, кроме корневой области. Включение ваших тестовых методов в класс кажется мне лучшим решением.
% 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")
()
Как показывает ответ импровизированный, возможна перегрузка. Комментарий Дэниел о дизайнерском решении правильный, но, на мой взгляд, неполный и немного вводящий в заблуждение. объявленный вне закона перегрузок отсутствует (поскольку они возможны), но их нелегко достичь.
К этому приводят следующие дизайнерские решения:
Проблема в том ... как достичь всех этих целей? Как мы обрабатываем ваш пример?
def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}
Начиная с 4-го элемента, val или def может быть определен только внутри class, trait, object или package object. Итак, REPL помещает определения внутри объектов, как это (не актуальное представление!)
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, после того, как вы определили один из них, вы не можете их расширить. Конечно, можно было все перекомпилировать, но мы от этого отказались. Поэтому вам нужно разместить его в другом месте:
package $line1 { // input line
object $read { // what was read
object $iw { // definitions
def foo(x: String): Unit = { println(foo(2)) }
}
}
}
И это объясняет, почему ваши примеры не являются перегрузками: они определены в двух разных местах. Если вы поместите их в одну строку, все они будут определены вместе, что приведет к их перегрузке, как показано в примере extempore.
Что касается других проектных решений, каждое новое определение импорта пакета и "res" из предыдущих пакетов, и импорт может затенять друг друга, что позволяет "переопределить" материал.