Почему компилятор Scala запрещает перегруженные методы с аргументами по умолчанию?

Хотя могут быть допустимые случаи, когда такие перегрузки методов могут становиться двусмысленными, почему компилятор не разрешает код, который не является двусмысленным во время компиляции или во время выполнения?

Пример:

// This fails: def foo(a: String)(b: Int = 42) = a + b def foo(a: Int) (b: Int = 42) = a + b // This fails, too. Even if there is no position in the argument list, // where the types are the same. def foo(a: Int) (b: Int = 42) = a + b def foo(a: String)(b: String = "Foo") = a + b // This is OK: def foo(a: String)(b: Int) = a + b def foo(a: Int) (b: Int = 42) = a + b // Even this is OK. def foo(a: Int)(b: Int) = a + b def foo(a: Int)(b: String = "Foo") = a + b val bar = foo(42)_ // This complains obviously ... 

Есть ли причины, по которым эти ограничения не могут быть немного ослаблены?

Особенно, когда преобразование сильно перегруженного Java-кода в аргументы по умолчанию Scala очень важно, и не так легко найти после замены множества методов Java одним методом Scala, который spec / compiler накладывает произвольные ограничения.

Я хотел бы привести Лукаса Ритца ( отсюда ):

Причина в том, что нам нужна детерминированная схема именования для сгенерированных методов, возвращающих аргументы по умолчанию. Если вы пишете

def f(a: Int = 1)

компилятор генерирует

def f$default$1 = 1

Если у вас две перегрузки с настройками по умолчанию в одной и той же позиции параметра, нам нужна другая схема именования. Но мы хотим сохранить сгенерированный байт-код стабильным в течение нескольких запусков компилятора.

Решением для будущей версии Scala может быть включение имен типов аргументов не по умолчанию (тех, которые в начале метода, которые устраняют неоднозначность перегруженных версий), в схему именования, например, в этом случае:

 def foo(a: String)(b: Int = 42) = a + b def foo(a: Int) (b: Int = 42) = a + b 

это было бы что-то вроде:

 def foo$String$default$2 = 42 def foo$Int$default$2 = 42 

Кто-то хочет написать предложение SIP ?

Было бы очень сложно получить читаемую и точную спецификацию для взаимодействия перегрузки разрешения с аргументами по умолчанию. Конечно, для многих отдельных случаев, таких как представленный здесь, легко сказать, что должно произойти. Но этого недостаточно. Нам нужна спецификация, которая решает все возможные угловые случаи. Перегрузочное разрешение уже очень сложно определить. Добавление аргументов по умолчанию в миксе будет еще сложнее. Вот почему мы решили отделить их.

Я не могу ответить на ваш вопрос, но вот обходной путь:

 implicit def left2Either[A,B](a:A):Either[A,B] = Left(a) implicit def right2Either[A,B](b:B):Either[A,B] = Right(b) def foo(a: Either[Int, String], b: Int = 42) = a match { case Left(i) => i + b case Right(s) => s + b } 

Если у вас есть два очень длинных списка аргументов, которые отличаются только одним аргументом, это может стоить проблем …

То, что сработало для меня, – это переопределить (стиль Java) методы перегрузки.

 def foo(a: Int, b: Int) = a + b def foo(a: Int, b: String) = a + b def foo(a: Int) = a + "42" def foo(a: String) = a + "42" 

Это гарантирует компилятору, какое разрешение вы хотите в соответствии с текущими параметрами.

Одним из возможных сценариев является

 def foo(a: Int)(b: Int = 10)(c: String = "10") = a + b + c def foo(a: Int)(b: String = "10")(c: Int = 10) = a + b + c 

Компилятор будет смущен, о котором нужно позвонить. В предотвращении других возможных опасностей компилятор разрешил бы не более одного перегруженного метода иметь аргументы по умолчанию.

Просто мое предположение 🙂

Я понимаю, что в скомпилированных classах со значениями аргументов по умолчанию могут возникать конфликты имен. Я видел что-то в этом направлении, упомянутое в нескольких streamах.

Спецификация названных аргументов приведена здесь: http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf

Говорится:

  Overloading If there are multiple overloaded alternatives of a method, at most one is allowed to specify default arguments. 

Таким образом, пока что, во всяком случае, это не сработает.

Вы могли бы сделать что-то вроде того, что вы могли бы сделать на Java, например:

 def foo(a: String)(b: Int) = a + (if (b > 0) b else 42) 

Вот обобщение ответа @Landei:

Что вы действительно хотите:

 def pretty(tree: Tree, showFields: Boolean = false): String = // ... def pretty(tree: List[Tree], showFields: Boolean = false): String = // ... def pretty(tree: Option[Tree], showFields: Boolean = false): String = // ... 

Workarround

 def pretty(input: CanPretty, showFields: Boolean = false): String = { input match { case TreeCanPretty(tree) => prettyTree(tree, showFields) case ListTreeCanPretty(tree) => prettyList(tree, showFields) case OptionTreeCanPretty(tree) => prettyOption(tree, showFields) } } sealed trait CanPretty case class TreeCanPretty(tree: Tree) extends CanPretty case class ListTreeCanPretty(tree: List[Tree]) extends CanPretty case class OptionTreeCanPretty(tree: Option[Tree]) extends CanPretty import scala.language.implicitConversions implicit def treeCanPretty(tree: Tree): CanPretty = TreeCanPretty(tree) implicit def listTreeCanPretty(tree: List[Tree]): CanPretty = ListTreeCanPretty(tree) implicit def optionTreeCanPretty(tree: Option[Tree]): CanPretty = OptionTreeCanPretty(tree) private def prettyTree(tree: Tree, showFields: Boolean): String = "fun ..." private def prettyList(tree: List[Tree], showFields: Boolean): String = "fun ..." private def prettyOption(tree: Option[Tree], showFields: Boolean): String = "fun ..." 

Если вы вызвали foo() который он должен вызвать?

  • Изменить статус по умолчанию Magento для дублированных продуктов
  • Конструкторы и наследование по умолчанию в Java
  • Каков пакет по умолчанию, в который помещаются мои classы, если я не укажу его?
  • Как вы используете конструктор, отличный от стандартного для члена?
  • Как изменить каталог данных MySQL?
  • Таблицы стилей CSS по умолчанию для браузеров
  • Поддерживает ли Java значения параметров по умолчанию?
  • Что означает «default» после объявления функции classа?
  • Почему «final» не допускается в методах интерфейса Java 8?
  • Почему компилятор не может вывести тип шаблона из аргументов по умолчанию?
  • Интерфейс с методами по умолчанию vs Абстрактный class в Java 8
  • Давайте будем гением компьютера.