Почему рубина не поддерживает перегрузку метода?

Вместо поддержки метода перегрузки Ruby перезаписывает существующие методы. Может ли кто-нибудь объяснить, почему язык был разработан таким образом?

Ответ 1

Перегрузка метода может быть достигнута путем объявления двух методов с одинаковым именем и разными сигнатурами. Эти разные сигнатуры могут быть либо:

  • Аргументы с разными типами данных, например: method(int a, int b) vs method(String a, String b)
  • Переменная количество аргументов, например: method(a) vs method(a, b)

Мы не можем добиться перегрузки метода с помощью первого способа, потому что в ruby ​​нет объявления типа данных (динамический типизированный язык). Таким образом, единственный способ определить вышеупомянутый метод: def(a,b)

Со вторым вариантом может показаться, что мы можем добиться перегрузки метода, но мы не можем. Скажем, у меня есть два метода с различным числом аргументов,

def method(a); end;
def method(a, b = true); end; # second argument has a default value

method(10)
# Now the method call can match the first one as well as the second one, 
# so here is the problem.

Таким образом, ruby ​​должен поддерживать один метод в цепочке поиска метода с уникальным именем.

Ответ 2

"Перегрузка" - это термин, который просто не имеет смысла в Ruby. Это в основном синоним "статической аргументной отправки", но Ruby не имеет статической отправки вообще. Итак, причина, по которой Ruby не поддерживает статическую отправку на основе аргументов, заключается в том, что она не поддерживает статическую отправку, период. Он не поддерживает статическую отправку любого типа, будь то на основе аргументов или иначе.

Теперь, если вы фактически не спрашиваете о перегрузке, но, возможно, о динамической отправке на основе аргументов, тогда ответ таков: потому что Matz не реализовал его. Потому что никто не потрудился предложить это. Потому что никто не потрудился ее реализовать.

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

В С# вы можете фактически закодировать любую проблему с 3-SAT в разрешении перегрузки, что означает, что разрешение перегрузки в С# NP-hard.

Теперь попробуйте с динамической отправкой, где у вас есть дополнительное измерение времени, которое нужно держать в голове.

Существуют языки, динамически отправляемые на основе всех аргументов процедуры, в отличие от объектно-ориентированных языков, которые отправляют только "скрытый" нулевой аргумент self. Общий Lisp, например, отправляет динамические типы и даже динамические значения всех аргументов. Clojure отправляет на произвольную функцию всех аргументов (что BTW чрезвычайно круто и чрезвычайно мощно).

Но я не знаю ни одного языка OO с динамической отправкой на основе аргументов. Мартин Одерски сказал, что он может рассмотреть возможность добавления отправки на основе аргументов в Scala, но только в том случае, если он может одновременно удалить перегрузку и быть обратно совместимым как с существующим кодом Scala, который использует перегрузку и совместим с Java (особенно упомянутые Swing и AWT, которые играют некоторые чрезвычайно сложные трюки, выполняющие почти все неприятные темные угловые случаи Java довольно сложных правил перегрузки). У меня были некоторые идеи о добавлении отправки на основе аргументов в Ruby, но я никогда не мог понять, как сделать это с обратной совместимостью.

Ответ 3

Я предполагаю, что вы ищете возможность сделать это:

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end

Ruby поддерживает это по-другому:

def my_method(*args)
  if args.length == 1
    #method 1
  else
    #method 2
  end
end

Общий шаблон также должен передаваться в качестве хэширования:

def my_method(options)
    if options[:arg1] and options[:arg2]
      #method 2
    elsif options[:arg1]
      #method 1
    end
end

my_method arg1: 'hello', arg2: 'world'

Надеюсь, что поможет

Ответ 4

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

f(1)
f('foo')
f(true)

а также между различными аргументами

f(1)
f(1, 'foo')
f(1, 'foo', true)

Первое различие не существует в рубине. Ruby использует динамическую типизацию или "утиную печать". Второе различие может обрабатываться аргументами по умолчанию или путем работы с аргументами:

def f(n, s = 'foo', flux_compensator = true)
   ...
end


def f(*args)
  case args.size
  when  
     ...
  when 2
    ...
  when 3
    ...
  end
end

Ответ 5

Это не отвечает на вопрос, почему Ruby не имеет перегрузки методов, но сторонние библиотеки могут его предоставить.

Библиотека contracts.ruby позволяет перегружать. Пример, адаптированный из учебника:

class Factorial
  include Contracts

  Contract 1 => 1
  def fact(x)
    x
  end

  Contract Num => Num
  def fact(x)
    x * fact(x - 1)
  end
end

# try it out
Factorial.new.fact(5)  # => 120

Обратите внимание, что это на самом деле более мощно, чем перегрузка Java, потому что вы можете указать значения, соответствующие (например, 1), а не просто типы.

При этом вы увидите снижение производительности; вам придется запускать тесты, чтобы решить, сколько вы можете терпеть.

Ответ 6

Я часто делаю следующую структуру:

def method(param)
    case param
    when String
         method_for_String(param)
    when Type1
         method_for_Type1(param)

    ...

    else
         #default implementation
    end
end

Это позволяет пользователю объекта использовать чистый и понятный метод имя_ метода: Но если он хочет оптимизировать выполнение, он может напрямую вызвать правильный метод.

Кроме того, он делает ваши тестовые очистители и улучшает.