Метод вызова оператора Ruby против обычных вызовов метода

Мне интересно, почему призывы к операторским методам не требуют точки? Вернее, почему нельзя вызывать обычные методы без точки?

Пример

class Foo
 def +(object)
 puts "this will work"
 end
 def plus(object)
 puts "this won't"
 end
end 
f = Foo.new
f + "anything" # "this will work"
f plus "anything" # NoMethodError: undefined method `plus' for main:Object
5 ответов

Реализация не требует дополнительной сложности, необходимой для обеспечения общего определения новых операторов.

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

Конечно, это не потому, что Мац ленился.

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


Ответ на этот вопрос, относительно почти каждого вопроса дизайна языка: "Просто потому, что". Языковой дизайн - это серия преимущественно субъективных компромиссов. И для большинства этих субъективных компромиссов единственный правильный ответ на вопрос, почему что-то так, как есть, просто "потому что Мац сказал так".

Есть, конечно, другие варианты:

  • Lisp не имеет операторов вообще. +, -, ::, >, = и т.д. являются просто нормальными именами юридических функций (фактически имена переменных), как и foo или bar?

    (plus 1 2)
    (+ 1 2)
  • Smalltalk почти не имеет операторов. Единственным специальным корпусом Smalltalk является то, что методы, состоящие только из символов оператора, не должны заканчиваться двоеточием. В частности, поскольку операторов нет, все вызовы методов имеют одинаковый приоритет и оцениваются строго слева направо: 2 + 3 * 4 is 20, а не 14.

    1 plus: 2
    1 + 2
  • Scala почти не имеет операторов. Точно так же, как Lisp и Smalltalk, *, -, #::: и т.д. Являются просто юридическими именами методов. (На самом деле, они также являются легальным классом, признаком, типом и именами полей.) Любой метод можно назвать либо с точкой, либо без нее. Если вы используете форму без точки, и метод принимает только один аргумент, вы также можете оставить скобки. Scala имеет приоритет, хотя, хотя он не определяется пользователем; он просто определяется первым символом имени. В качестве добавленного твиста имена операторов-операторов, заканчивающиеся двоеточием, инвертированы или право-ассоциативны, то есть a :: b эквивалентно b.::(a), а не a.::(b).

    1.plus(2)
    1 plus(2)
    1 plus 2
    1.+(2)
    1 +(2)
    1 + 2
  • В Haskell любая функция, имя которой состоит из символов оператора, считается оператором. Любая функция может рассматриваться как оператор, заключая ее в обратные вызовы, и любой оператор может рассматриваться как функция, заключая его в скобки. Кроме того, программист может свободно определять ассоциативность, фиксированность и приоритет для пользовательских операторов.

    plus 1 2
    1 `plus` 2
    (+) 1 2
    1 + 2

Нет никакой конкретной причины, по которой Ruby не может поддерживать пользовательские операторы в стиле, подобном Scala. Существует причина, по которой Ruby не может поддерживать произвольные методы в позиции оператора, просто потому, что

foo plus bar

уже является законным, и, таким образом, это будет несовместимое с обратной стороной изменение.

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

puts(!true)

является законным, но

puts(not true)

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

Функция Refinement, обсуждаемая в настоящее время на ruby-core, является еще одним примером. То, как это указано в настоящее время, сделает невозможным оптимизацию вызовов методов и встроенных методов, даже если рассматриваемая программа фактически не использует Refinement вообще. С помощью простой настройки он может быть столь же выразительным и мощным, и обеспечить, чтобы стоимость пессимизации была произведена только для областей, которые фактически используют Refinement s. По-видимому, единственная причина, почему это было указано таким образом, заключается в том, что а) было проще прототипировать этот путь, и б) у YARV нет оптимизатора, поэтому никто даже не потрудился задуматься о последствиях (ну, никто, кроме Чарльза Оливер Нуттер).

Итак, в основном по любому вопросу, который у вас есть о дизайне Ruby, ответ почти всегда будет либо "потому что Matz сказал так", либо "потому что в 1993 году было проще реализовать этот путь".


Поскольку Ruby имеет "синтаксический сахар", который позволяет использовать различные удобные синтаксисы для заданных ситуаций. Например:

class Foo
 def bar=( o ); end
end
# This is actually calling the bar= method with a parameter, not assigning a value
Foo.new.bar = 42

Здесь приведен список выражений оператора которые могут быть реализованы как методы в Ruby.


Поскольку синтаксис Ruby был разработан так, чтобы выглядеть примерно так же, как популярные языки OO, а те используют оператор dot для вызова методов. Язык, на котором он заимствовал свою объектную модель от Smalltalk, не использовал точки для сообщений, и на самом деле имел довольно "странный" синтаксис, который многие люди находили вне помещения. Ruby был назван "Smalltalk с синтаксисом Algol", где Algol - это язык, который дал нам соглашения, о которых вы говорите здесь. (Конечно, на самом деле больше различий, чем просто синтаксис Algol.)


Отсутствие скобок было некоторым "преимуществом" для ruby ​​1.8, но с ruby ​​1.9 вы даже не можете писать method_0 method_1 some param, он будет отклонен, поэтому язык переходит скорее к строгой версии, а не к произвольным формам.

licensed under cc by-sa 3.0 with attribution.