Методы в Ruby: объекты или нет?
Вдохновленный этим обсуждением, после некоторого поиска в Google я не смог найти ответ на довольно простой вопрос, касающийся методов в Ruby: методы являются объектами или нет?
Тут и там разные мнения, и мне бы очень хотелось услышать, скажем, подробное объяснение.
Я в курсе Object#method
метод, который принимает имя метода и возвращает Method
экземпляр, но, с другой стороны, есть нечто подобное, что вы можете сделать с блоками, чтобы превратить их в Proc
экземпляры и блоки не являются объектами, так что же отличает методы?
6 ответов
Методы являются фундаментальной частью синтаксиса Ruby, но они не являются значениями, с которыми могут работать программы Ruby. То есть методы Ruby не являются объектами, такими как строки, числа и массивы. Однако возможно получить объект метода, который представляет данный метод, и мы можем вызывать методы косвенно через объекты метода.
Из языка программирования Ruby:
http://ecx.images-amazon.com/images/I/517LDwIEYwL._SL75_.jpg
Вы не можете действительно сказать.
Единственный способ получить доступ к методу - это отправить #method
сообщение для некоторого объекта, который затем вернет Method
объект. Но разве это Method
возражать сам метод? Или это обертка вокруг метода? Или это конвертированная версия оригинального метода?
Вы не можете знать: если вы хотите посмотреть на метод, вы должны вызвать #method
, в этот момент вы обязательно получите объект. Что это было до того, как ты позвонил #method
вы не можете смотреть, поэтому вы не можете сказать.
Пара точек данных: в Ruby все возвращает значение. Что значит def
вернуть? Всегда возвращается nil
не Method
объект. А также define_method
? Возвращает Proc
, но не Method
(ни UnboundMethod
). [Примечание: у Рубиния def
возвращает скомпилированный байт-код метода, но все еще не Method
объект.]
Если вы посмотрите на 4-й и 5-й абзацы раздела 6.1 Спецификации языка Ruby (строки 29-34 и 1-5 на страницах 5 и 6), вы можете ясно увидеть, что между методами и объектами проводится различие. И если вы посмотрите на спецификацию встроенных классов, вы обнаружите, что ни Method
ни UnboundMethod
там, и не Object#method
, IOW: вы можете создать полностью совместимый со стандартами интерпретатор Ruby, в котором методы не являются объектами.
Теперь блоки OTOH определенно не являются объектами. Есть много способов построить Proc
объекты из блоков, которые затем ведут себя так же, как и исходный блок (lambda
, proc
, Proc.new
, &
sigil), но сами блоки не являются объектами.
Подумайте об этом так: вы можете передать строку File.new
построить объект файла, но это не делает строку файлом. Вы можете передать блок Proc.new
построить объект proc, но это не делает блок proc.
В Ruby методы и блоки сами по себе не являются нативными или первоклассными объектами. Однако их можно очень легко обернуть в объекты, так что это, как правило, не имеет значения.
Но попробуйте и имейте в виду результат,
a = Object.method(:new).object_id
b = Object.method(:new).object_id
a == b
=> false
В Haskell все значения (включая числа, а также лямбды и функции) являются первоклассными значениями. В каждом аспекте языка все они рассматриваются одинаково. В Ruby дело обстоит иначе, но его можно приблизить.
Объекты и методы не одинаковы, даже если возвращаемое значение для методов - объект, а не ноль. Объекты живут в куче, если только не в области метода, лямбды или процедуры, а сам метод живет в стеке и ему присваивается адрес после интерпретации, в то время как статические объекты и объекты класса размещаются в куче. Ruby по-прежнему использует C, чтобы интерпретировать его и передать его в структуру VALUE.
В Ruby методы не являются объектами. Это сбивает с толку, потому что есть класс Method, и вы можете получить экземпляры метода. Эти экземпляры являются просто прокси для самого метода. Эти экземпляры предоставляют некоторые полезные функции. У них есть внутренняя магия, которая связывает их с реальным методом (так что вы можете делать такие вещи, как Method#call
) но вы не можете получить доступ к этому материалу (AFAIK).
1.method(:to_s).object_id == 1.method(:to_s).object_id #=> false
Это означает, что либо 1
имеет два #to_s
методы (чего он не делает) или то, что возвращается методом #method
на самом деле это не сам метод, а какой-то прокси для метода. Если бы методы были на самом деле объектами, у вас бывали ситуации, когда вы могли получить один и тот же экземпляр дважды. Если бы методы были объектами, то вы могли бы делать такие вещи, как установить для них переменную экземпляра, а затем они получить значение этой переменной экземпляра после второго извлечения объекта метода. Вы не можете сделать это. Таким образом, хотя это, как правило, не имеет значения, бывают ситуации, когда я не могу делать то, что хотел бы.
1.method(:to_s).instance_variable_set(:@foo, 'foo') #=> "foo"
1.method(:to_s).instance_variable_get(:@foo) #=> nil
# And just in case you question it...
1.object_id == 1.object_id #=> true
Поскольку круглые скобки являются необязательными в ruby, объекты метода обычно "скрыты" в том смысле, что вам нужно явно извлечь объект метода через method
метод. Однако если вы попытаетесь захватить объект метода, станет совершенно ясно, что он действует как объект. Поскольку Ruby >= 2.1, этим легче воспользоваться, чем когда-либо.
Например, вы можете заставить ваши методы вести себя больше, чем они делают в Javascript (где parens не является объектом метода, а parens используются для вызова метода) следующим образом:
foo = method def foo
def a(num)
3 * num.to_i
end
n = yield if block_given?
a(n || 3)
rescue
"oops!"
end
def foo.bar(num)
a(num)
end
foo.class #=> Method
foo() #=> 9
foo.call #=> 9
foo.call{2} #=> 6
foo(){2} #=> 6
foo.call{ raise "blam!" } #=> "oops!"
foo.bar(5) #=> 15
Посмотрите эту суть для версии с этим примером, написанным как тесты.
Ответ JRL цитирует книгу Мэца, в которой говорится, что методы не являются объектами, такими как строки и т. Д., Но объекты методов реальны, и, кроме вещей parens / no-parens, они действуют почти так же, как и любой другой объект ruby. Это язык утки, поэтому я бы сказал, что в моей книге методы определены как объекты.