Why Ruby 1.9 allows overriding ! != !~?
There were two good reasons why Ruby 1.8 didn't support certain kinds of overloading like ||
/or
, &&
/and
, !
/not
, ?:
:
- Short circuit semantics cannot be implemented with methods in Ruby without very extensive changes to the language.
- Ruby is hard coded to treat only
nil
andfalse
as false in boolean contexts.
The first reason doesn't apply to !
/not
but second still does. It's not like you can introduce your own kinds of boolean-like objects using just !
while &&
/||
are still hard-coded. For other uses there's already complementarity operator ~
with &
/|
.
I can imagine there's a lot of code expecting !obj
to be synonymous with obj ? false : true
, and !!obj
with obj ? true : false
- I'm not even sure how is code supposed to deal with objects that evaluate to true in boole开发者_运维知识库an context, but !
to something non-false.
It doesn't look like Ruby plans to introduce support for other false values. Nothing in Ruby stdlib seems to override !
so I haven't found any examples.
Does it have some really good use I'm missing?
Self-replying. I found one somewhat reasonable use so far. I can hack this to work in 1.9.2 in just a few lines:
describe "Mathematics" do
it "2 + 2 != 5" do
(2+2).should != 5
end
end
Before 1.9.2 Ruby this translates to:
describe "Mathematics" do
it "2 + 2 != 5" do
((2+2).should == 5) ? false : true
end
end
But as return value gets thrown away we have no ways of distinguishing == 5
from != 5
short of asking Ruby for block's parse tree. PositiveOperatorMatcher#==(5)
would just raise ExpectationNotMetError
exception and that would be it. It seems should !~ /pattern/
, should !be_something
etc. can also be made to work.
This is some improvement over (2+2).should_not == 5
, but not really a big one. And there's no way to hack it further to get things like (2+2).should be_even or be_odd
.
Of course the right thing to do here would be asking Ruby for parse tree and macro-expanding that. Or using debug hooks in Ruby interpreter. Are there use cases better than this one?
By the way, it wouldn't be enough to allow overrides for !
while translating !=
/!~
the old way. For !((2+2).should == 5)
to work #==
cannot raise an exception before !
gets called. But if #==
fails and !
doesn't get run execution will simply continue. We will be able to report assertion as failed after block exits, but at cost of executing test after the first failure. (unless we turn on ruby debug hooks just after failed assertion to see if the very next method call is !self
, or something like that)
精彩评论