Рубин: Что определяет определение класса для - PullRequest
4 голосов
/ 13 июня 2010

Почему в Ruby определение класса оценивается как nil? То же самое касается определения метода: почему он оценивается как nil? Не было бы полезно, если бы определение класса оценивалось как класс?

Ответы [ 3 ]

8 голосов
/ 13 июня 2010

Почему в Ruby определение класса оценивается как nil?

Это не так.

Прежде всего, в Ruby вы не определяете класс, вы выполняете тело класса. Во-вторых, выполнение тела класса не оценивается как nil, как вы утверждаете, оно оценивается как значение последнего выражения внутри тела класса. Это прекрасно согласуется с выполнением тела модуля и тела метода.

См., Например:

class Foo
  'Hello'
end
# => 'Hello'

То же самое относится и к определению метода: почему он оценивается как nil?

На самом деле, это тоже не так. Определение метода приводит к определенному для реализации значению. Причина, по которой он оценивает значение, определяемое реализацией, состоит в том, что до сих пор сообщество Ruby не достигло консенсуса относительно того, что оно должно возвращать.

Некоторые утверждают, что он должен вычислять объект CompiledMethod, соответствующий этому методу. Это то, что делает Рубиниус. Однако здесь есть проблема: не все реализации Ruby компилируют свои методы. И не все из них, которые делают компилируют их, компилируют их во время определения. Например, JRuby компилирует их только тогда, когда они выполняются, точнее после они были выполнены 20 раз. Иногда он не компилирует их вообще , например, в средах, где запрещена компиляция, таких как Google App Engine. Кроме того, не все реализации Ruby имеют представление Ruby для своих скомпилированных методов. И даже если бы они это сделали, их скомпилированные методы сильно отличаются: скомпилированные методы Рубиниуса - это байт-код Rubinius, скомпилированные методы YARV - это байт-код YARV, скомпилированные методы JRuby - это байт-код JVM, скомпилированные методы IronRuby - это деревья DLR. И, конечно же, наиболее широко используемая реализация, MRI, даже не имеет компилятора .

Другие говорят, что он должен оценивать объект UnboundMethod, соответствующий этому методу. Здесь проблема в том, что нет единственного UnboundMethod объекта, соответствующего методу. Их бесконечно много. Методы Ruby не являются объектами. Они могут быть преобразованы в объекты, но сами они не являются объектами. И когда они преобразуются в объекты, они каждый раз генерируют новый объект. Таким образом, объект UnboundMethod, который будет возвращен, на самом деле не имеет прямого отношения к определенному методу. Кроме того, что бы вы хотели сделать с несвязанным методом? Единственный случай, когда I использует несвязанный метод, - это обертка уже существующего метода, который я не могу изменить; но если у меня есть доступ к определению, мне не нужно его оборачивать.

Третья группа говорит, что определение метода должно соответствовать его названию. Это кажется несколько произвольным выбором. На самом деле, я не видел какой-либо убедительной причины, почему так должно быть. Буквально единственным аргументом является то, что это позволит вам сделать Ruby похожим на Java:

# here's how you make a single method private today
def foo(bar) end
private :foo

# instead you could do this:
private def foo(bar) end

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

Интересно, что Module#define_method возвращает что-то полезное. Если вы используете метод для определения метода, он возвращает старый метод. Если вы используете метод для определения метода, он возвращает вариант этого процесса. Если вы используете блок для определения метода, он возвращает proc, соответствующий этому блоку. Другими словами, он возвращает исполняемый объект, соответствующий телу метода:

class Foo
  $bar  = ->{}
  $baz  = define_method :baz, $bar

  $qux  = instance_method :baz
  $quux = define_method :quux, $qux

  define_method :corge do;end
end
# => #<Proc:0x1cda900@(irb):8 (lambda)>

$bar.eql?   $baz  # => true
$bar.equal? $baz  # => false
$qux.equal? $quux # => true

Не было бы полезно, если бы определение класса оценивалось как класс?

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

Кроме того, единственным случаем, когда мне когда-либо требовалось тело класса для возврата самого класса, было получение ссылки на одноэлементный класс объекта, например:-известная схема:

class Object; def singleton_class; class << self; self end end end

Но теперь, когда Object#singleton_class является частью базовой библиотеки, в этом больше нет необходимости.

3 голосов
/ 13 июня 2010

Определение класса не оценивается как nil, оно возвращает то, что было последним выражением (например, вызов метода)

class X
  2
end  # => 2

Типичным примером является singleton = class << SomeClass; self; end, хотя теперь мы можем использовать SomeClass.singleton_class.

Определение метода возвращает nil (поэтому классы, заканчивающиеся определением метода, возвращают nil).Что бы вы хотели, чтобы определение метода вернуло, и когда это было бы полезно?

Если вы хотите, чтобы класс был возвращен, его легко завершить с помощью self:

class X
  def foo
    :bar
  end

  self
end # => X
1 голос
/ 13 июня 2010

Вы можете достичь этой функциональности, вызвав Class.new {...} вместо использования ключевого слова class.

someclass = Class.new{
  def bar
    puts "baz"
  end
}

и затем вы можете присвоить имя классу, присвоив константу классу var.

Foo = someclass

, поэтому для создания экземпляра класса вы можете выполнить одно из следующих действий:

instance1 = someclass.new
instance2 = Foo.new

instance1.bar  #=> baz
instance2.bar  #=> baz
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...