Чому «уникати перевантаження методів»?


78

Чи може це мати щось спільне з тим фактом, що Scala успадковує стирання типу Java?
Джастін Нісснер,

1
@Justin: Яке відношення до цього має стирання типу?
missingfaktor

6
Чому б вам не запитати Хорхе Ортіса, чому він не радить перевантажувати методи?
Джон

Не впевнений, чи застосовується це, оскільки я не знаю первісного наміру Хорхе, але: michid.wordpress.com/2008/02/08/…
Джастін Нісснер,

12

Відповіді:


108

Перевантаження набагато ускладнює підняття методу до функції:

object A {
   def foo(a: Int) = 0
   def foo(b: Boolean) = 0
   def foo(a: Int, b: Int) = 0

   val function = foo _ // fails, must use = foo(_, _) or (a: Int) => foo(a)
}

Ви не можете вибірково імпортувати один із набору перевантажених методів.

Існує більша ймовірність виникнення неоднозначності при спробі застосувати неявні погляди для адаптації аргументів до типів параметрів:

scala> implicit def S2B(s: String) = !s.isEmpty                             
S2B: (s: String)Boolean

scala> implicit def S2I(s: String) = s.length                               
S2I: (s: String)Int

scala> object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") }
<console>:15: error: ambiguous reference to overloaded definition,
both method foo in object test of type (b: Boolean)Int
and  method foo in object test of type (a: Int)Int
match argument types (java.lang.String)
       object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") }

Він може тихо зробити параметри за замовчуванням непридатними:

object test { 
    def foo(a: Int) = 0; 
    def foo(a: Int, b: Int = 0) = 1 
}

В індивідуальному порядку ці причини не змушують вас повністю уникати перевантажень. Я відчуваю, що мені не вистачає деяких більших проблем.

ОНОВЛЕННЯ

Докази складаються.

ОНОВЛЕННЯ 2

  • Ви не можете (наразі) використовувати перевантажені методи в об’єктах пакета.
  • Помилки застосовності важче діагностувати для абонентів вашого API.

ОНОВЛЕННЯ 3

  • статичне дозвіл на перевантаження може позбавити API будь-якого типу безпеки:
scala> object O { def apply[T](ts: T*) = (); def apply(f: (String => Int)) = () }
defined object O

scala> O((i: String) => f(i)) // oops, I meant to call the second overload but someone changed the return type of `f` when I wasn't looking...

2
В даний час також існує помилка в Scalac, яка в деяких випадках виникає через перевантаження. issues.scala-lang.org/browse/SI-7596 .
cvogt

2
Перші дві проблеми не впливають на кожне допустиме використання перевантаження. Подав звіт про помилку для третього випуску. Обмеження за замовчуванням є вибором , і в теорії може бути виправлено. Помилка _.fooпроблеми полягає у обмеженому висновку Scala, а не в перевантаженні. Ви відповідаєте на запитання, але деякі причини пов’язані з іншими слабкими місцями Scala, які можна було б покращити. Перевантаження є ефективнішим, ніж виконання, що знижує диз’юнкцію, або декартовий твір імен викликає шум і відключає загальну семантику.
Шелбі Мур III

3
Наразі типові класи в даний час не можуть бути використані через відсутність першокласного типу об’єднання . Так, я бачу таке ставлення у спільноті Скали, але уявіть натомість addIntToDouble, addDoubleToIntтобто декартовий продукт імен замість статичного введення для кожної загальної семантики. Заміна набору тексту іменуванням видається регресивною. Java отримала більше речей, ніж ми можемо визнати.
Шелбі Мур III

2
Я писав у дискусійній темі (для звіту про помилку, про який я згадав у попередньому коментарі), "Що таке зле IMO очікує, що перевантаження стане тим, чим він не є, або зменшує важливість наявності однієї назви для загальної семантики".
Шелбі Мур III

2
Відповідь та коментарі здаються мені дуже наївними і не згадують найважливішої причини використання перевантаження: коли метод насправді повинен виконувати зовсім іншу внутрішню логіку, залежно від переданих йому типів. Відповідь "використовувати імпліцити" не вдається негайно, оскільки може не існувати жодного можливого перетворення з одного об'єкта на інший тип, що втілює спеціалізовану логіку.
елі,

8

Причини, які наводять Гілад та Джейсон (ретронім), є дуже вагомими причинами, щоб уникнути перевантажень, якщо це можливо. Причини Гілада зосереджені на тому, чому перевантаження загалом проблематично, тоді як причини Джейсона зосереджені на тому, чому це проблематично в контексті інших особливостей Scala.

До списку Джейсона я б додав, що перевантаження погано взаємодіє з висновками типу. Розглянемо:

val x = ...
foo(x)

Зміна виведеного типу xможе змінити fooметод, який викликається. Значення з xнеобхідності не зміниться, тільки виведений типу x, який може статися з різних причин.

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


2
Якщо ви не хочете, щоб це сталося, ви оголошуєте тип для x. Якщо ви не заявляєте про це, ви говорите, що хочете, щоб це змінилося. Семантика fooмає бути однаковою для кожного перевантаження з однаковою кількістю параметрів, інакше вона була розроблена неправильно. Що стосується обмеження сфери химерного каскаду змін висновків, то загальнодоступні методи завжди повинні оголошувати їх типи повернення. Я думаю, це була одна з проблем, що впливають на бінарну сумісність Scala між версіями.
Шелбі Мур III

1

Я думаю, що поради призначені не для Scala, а для ОО взагалі (поки що я знаю, що Scala повинна бути найкращою у своїй породі між OO та функціоналом).

Перевизначення - це нормально, це серце поліморфізму і є головним у дизайні ОО.

З іншого боку, перевантаження є більш проблематичним. З перевантаженням методів важко зрозуміти, який метод буде справді викликаний, і це справді часто викликає плутанину. Також рідко можна виправдати, чому перевантаження дійсно необхідні. Здебільшого проблему можна вирішити іншим способом, і я згоден, що перевантаження - це запах.

Ось стаття, яка чудово пояснює, що я маю на увазі під назвою "перевантаження - це джерело плутанини", що, на мою думку, є основною причиною, чому це не рекомендується. Це для Java, але я думаю, що це стосується і Scala.


2
І Scala і так не є переважно мовою OO.
Даніель Ервікер

4
@ewenli - Хорхе добре відомий у співтоваристві Scala, і посилання, яке надав Рахул, було однією з підказок Хорхе щодо шкали, проте ваша відповідь не може нічого запропонувати щодо того, чому перевантаження шкідливе саме для Scala , що явно було метою цього питання. Крім того, я не маю уявлення, чому ви вирішили, що питання будь-яким чином було сплутане - вам слід просто вилучити це зі своєї відповіді, оскільки воно абсолютно невиправдане. -1
oxbow_lakes

6
@Daniel Scala - це насамперед мова ОО. Будь-яка причина, чому ви не думаєте так?
Даніель К. Собрал

3
О, творець конкуруючої функціональної мови не вважає, що Scala є достатньо функціональною! Великий шок! :)
Даніель Ервікер

3
@Daniel Scala може бути мультипарадигмою, але вона все ще є переважно об'єктно-орієнтованою мовою. Функціональні характеристики Scala реалізовані як об'єктно-орієнтовані особливості. Навіть найбільш функціональна програма Scala буде складатися виключно з об'єктів та відповідних класів та ознак. Зараз Мартін Одерський може говорити про свою мову все, що він хоче ( врешті-решт, це його мова), але, суворо технічно оцінюючи, Scala в першу чергу є об’єктно-орієнтованою, де під «в першу чергу» я маю на увазі, що все інше будується на цій характеристиці .
Даніель К. Собрал,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.