Почему Ruby 1.9 позволяет переопределять!! =! ~? - PullRequest
3 голосов
/ 30 июля 2010

Было две веские причины, по которым Ruby 1.8 не поддерживал определенные виды перегрузки, такие как || / or, && / and, ! / not, ?::

  • Семантика короткого замыкания не может быть реализована с помощью методов в Ruby без очень значительных изменений в языке.
  • В Ruby жестко задан способ обработки только nil и false как ложных в логических контекстах.

Первая причина не относится к ! / not, а вторая все еще имеет значение.Это не значит, что вы можете вводить свои собственные объекты типа boolean, используя просто !, тогда как && / || все еще жестко запрограммированы.Для других применений уже есть оператор комплементарности ~ с & / |.

Я могу представить, что существует большой код, ожидающий, что !obj будет синонимом obj ? false : true, а !!obj сobj ? true : false - Я даже не уверен, как код должен иметь дело с объектами, которые оцениваются как истинные в логическом контексте, но ! с чем-то не ложным.

Это не похоже на планы Rubyввести поддержку других ложных значений.Ничто в Ruby stdlib, похоже, не переопределяет !, поэтому я не нашел никаких примеров.

Есть ли у него действительно хорошее применение, которое я пропускаю?

1 Ответ

1 голос
/ 30 июля 2010

Self-ответ. Я нашел одно несколько разумное применение до сих пор. Я могу взломать это, чтобы работать в 1.9.2 всего за несколько строк:

describe "Mathematics" do
  it "2 + 2 != 5" do
    (2+2).should != 5
  end
end

До 1.9.2 Ruby это означает:

describe "Mathematics" do
  it "2 + 2 != 5" do
    ((2+2).should == 5) ? false : true
  end
end

Но так как возвращаемое значение отбрасывается, у нас нет способов отличить == 5 от != 5, кроме запроса Ruby для дерева разбора блока. PositiveOperatorMatcher#==(5) просто вызовет ExpectationNotMetError исключение, и это будет оно. Кажется, should !~ /pattern/, should !be_something и т. Д. Также можно заставить работать.

Это некоторое улучшение по сравнению с (2+2).should_not == 5, но не очень большое. И нет способа взломать его дальше, чтобы получить такие вещи, как (2+2).should be_even or be_odd.

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

Кстати, было бы недостаточно разрешить переопределения для ! при переводе != / !~ по-старому. Для !((2+2).should == 5) для работы #== не может вызвать исключение до вызова !. Но если #== завершится неудачно и ! не получится, выполнение будет просто продолжено. Мы сможем сообщить об утверждении как о неудачном после выхода из блока, но за счет выполнения теста после первого сбоя. (если только мы не включим обработчики ruby ​​debug сразу после неудачного утверждения, чтобы увидеть, будет ли следующий вызов метода !self или что-то в этом роде)

...