Why Ruby 1.9 allows overriding ! != !~? - ruby

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 and false 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 boolean 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)

Related

Rubocop, safe navigation, and negation

I've got constructs like the following that Rubocop complains about that I'm trying to appease
unless foo && !foo.empty? ...
(item exists and is not empty)
and
bar && !bar.positive?
(item exists and is not positive)
In both cases, Rubocop throws a Style/SafeNavigation and says I should use &. instead. However, there does not seem to be a way to write it.
In both cases part of the problem is that there is no concise way of expressing the opposite function - there is no "not empty" for strings, and no "negative or zero" for numbers. Writing bar&.negative? returns the opposite result for zero, for example.
In the other case, writing unless !foo&.empty? has Rubocop complain about unless+a negation, but again there's no way to rewrite it as an if foo&. with any operation that exists.
(btw this is straight Ruby, no Rails, so I don't have blank? and present?)
This:
next unless foo && !foo.empty?
Could be replaced with:
next if foo.nil? || foo.empty?
And similarly, this:
next unless bar && !bar.positive?
Could be replaced with:
next if bar.nil? || bar.positive?
The real code smell here, in my opinion, was the next unless ... not. The double negative indicates that there's probably a cleaner way to write it.
This is an oversight in RuboCop, because the suggested correction can not be guaranteed to have the same semantics. It will be fixed in the upcoming 0.50.1 patch, and the cop will ignore cases with negation.
In the meanwhile, if you're dealing with conditions where your variable could be nil, one option is to use the fact that nil responds to the coercion methods. Example, assuming foo is either an array or nil:
# With nil check
unless foo && !foo.empty?
# With array coercion
if foo.to_a.empty?

Ruby evaluates the default value in fetch even when key is found

h = {a: "foo"}
h.fetch(:a, h.fetch(:b))
yields key not found: :b
It seems strange that Ruby evaluates the default value even when the key is found? Is there a way around this?
Edit: Apparently this behavior falls under the paradigm of lazy vs eager evaluation. Nearly all imperative languages use eager evaluation, while many functional languages use lazy evaluation. However some languages, such as Python (which was before last week the only language I knew), have lazy evaluation capabilities for some operations.
It seems strange that Ruby evaluates the default value even when the key is found? Is there a way around this?
The overwhelming majority of mainstream programming languages is strict, i.e. arguments are fully evaluated before being passed. The only exception is Haskell, and calling it mainstream is a bit of a stretch.
So, no, it is not really "strange", it is how (almost) every language works, and it is also how every single other method in Ruby works. Every method in Ruby always fully evaluates all its arguments. That is, why, for example defined? and alias cannot be methods but must be builtin language constructs.
However, there is a way to delay evaluation, so to speak, using blocks: the content of a block is only evaluated each time it is called. Thankfully, Hash#fetch does take a block, so you can just use
h.fetch(:a) { h.fetch(:b) }
If you want the computation to only run if the key is not found, you can use the alternate form of fetch which accepts a block:
h.fetch(a) { h.fetch b }
I didn't actually know this was the case but I had a hunch and tried it and it worked. The reason I thought to try this was that something similar can be done in other methods such as gsub, i.e.
"123".gsub(/[0-9]/) { |i| i.to_i + 1 } == "234"
You could use the || operator instead, since it's lazy evaluated by design.
For example the following code where Nope is not defined does not throw an error.
{ a: "foo" }[:a] || Nope
However the fetch version will throw an error.
{ a: "foo" }.fetch(:a, Nope)
Personally I prefer how fetch is designed because it wont hide a bug in the default value.
However, in cases where I would rather not evaluate a default statement, then I would definitely reach for the || operator before doing something in a block or a lambda/proc.

Why does Enumerable#detect need a Proc/lambda?

Enumerable#detect returns the first value of an array where the block evaluates to true. It has an optional argument that needs to respond to call and is invoked in this case, returning its value. So,
(1..10).detect(lambda{ "none" }){|i| i == 11} #=> "none"
Why do we need the lambda? Why don't we just pass the default value itself, since (in my tests) the lambda can't have any parameters anyway? Like this:
(1..10).detect("none"){|i| i == 11} #=> "none"
As with all things in Ruby, the "principle of least surprise" applies. Which is not to say "least surprise for you" of course. Matz is quite candid about what it actually means:
Everyone has an individual background. Someone may come from Python, someone else may come from Perl, and they may be surprised by different aspects of the language. Then they come up to me and say, 'I was surprised by this feature of the language, so Ruby violates the principle of least surprise.' Wait. Wait. The principle of least surprise is not for you only. The principle of least surprise means principle of least my surprise. And it means the principle of least surprise after you learn Ruby very well. For example, I was a C++ programmer before I started designing Ruby. I programmed in C++ exclusively for two or three years. And after two years of C++ programming, it still surprises me.
So, the rational here is really anyone's guess.
One possibility is that it allows for or is consistent with use-cases where you want to conditionally run something expensive:
arr.detect(lambda { do_something_expensive }) { |i| is_i_ok? i }
Or as hinted by #majioa, perhaps to pass a method:
arr.detect(method(:some_method)) { |i| is_i_ok? i }
Accepting a callable object allows allows "lazy" and generic solutions, for example in cases where you'd want to do something expensive, raise an exception, etc...
I can't see a reason why detect couldn't accept non callable arguments, though, especially now in Ruby 2.1 where it's easy to create cheap frozen litterals. I've opened a feature request to that effect.
It is probably so you can generate an appropiate result from the input. You can then do something like
arr = (1..10).to_a
arr.detect(lambda{ arr.length }){|i| i == 11} #=> 10
As you said, returning a constant value is very easy with a lambda anyway.
Really it is interestion question. I can understand why authors have added the feature with method call, you can just pass method variable, containing a Method object or similar, as an argument. I think it is simply voluntaristic solution for the :detect method, because it could be easy to add switch on type of passed argument to select weither it is the Method or not.
I've reverified the examples, and got:
(1..10).detect(proc {'wqw'}) { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
# => "wqw"
(1..10).detect('wqw') { |i| i % 5 == 0 and i % 7 == 0 } #=> nil
# NoMethodError: undefined method `call' for "wqw":String
That is amazing. =)
The best use case for having a lambda is to raise a custom exception
arr = (1..10).to_a
arr.detect(lambda{ raise "not found" }){|i| i == 11} #=> RuntimeError: not found
So, as the K is trivial (just surround with ->{ }) there is not much sense in checking for fallback behavior.
The similar case of passing a &-ed symbol instead of a block is in fact not similar at all, as in that case it indicates something that will be called on the items of the enumerable.

Ruby: Using the unless keyword with a return statement

I am currently working on a project where I have code that looks like this:
# the first return is the one causing problems
def collect
return Hash["IdentANode", Array[#id, ",", #ident_a_node.collect]] unless #ident_a_node.nil? and #id.nil?
return Hash["IdentANode", #id] unless #id.nil?
end
Where I use the unless operator to conditionally execute the return statement. For some reason this code still executes even if #ident_a_node is nil. When executing I get this message:
IdentANode.rb:14:in collect': undefined methodcollect' for
nil:NilClass (NoMethodError)
Which confuses me because I had thought that the unless keyword would prevent this from happening. When I change the statement to this form:
if not #ident_a_node.nil? and not #id.nil?
return Hash["IdentANode", Array[#id, ",", #ident_a_node.collect]]
end
or this form:
return Hash["IdentANode", Array[#id, ",", #ident_a_node.collect]] if not #ident_a_node.nil? and not #id.nil?
The return statement is not executed, what gives? Why is there a difference between these two statements? Does having multiple conditions with the unless keyword cause problems?
Any ideas would be appreciated
You've got a logic failure in there. You're testing that they're both nil to avoid running it when you should be testing if either is nil. You've probably gotten yourself into this situation by having too many layers of negation. Anything more than one is unacceptable.
In other words, you can get away with "if it's not raining out" but shouldn't use things like "unless the is_not_raining flag is not set to the inverse of false".
My personal opinion is that trailing conditions should not be used unless it's obvious that they're present. As you can see in your example, you have to scroll horizontally to find the condition, hiding important information from the developer.
As a matter of style, do not use not when ! will do the same job. Secondly, you're testing specifically against nil when you probably just want a defined value of some kind.
Other issues include using Hash[] and Array[] which are surely artifacts of using a language which requires them. Ruby, like JavaScript, allows implicit declaration of these using { } and [ ] respectively.
A proper Ruby-styled version of your code is:
if (#ident_a_node and #id)
return { "IdentANode" => [ #id, ",", #ident_a_node.collect ] }
end
Don't use unless with and/or, it's just plain confusing. unless #ident_a_node.nil? and #id.nil? means if !(#ident_a_node.nil? and #id.nil?), which means it will return whenever one of the two instance variables is not nil.
if !(#ident_a_node.nil? and #id.nil?)
is the same as
if !#ident_a_node.nil? or !#id.nil?
which should make it even clearer, why it is not the same as
if not #ident_a_node.nil? and not #id.nil?

Preferred way to write true AND false conditional in Ruby

I apologize if this question is answered somewhere but I'm not positive I'm phrasing it right for Google, and I haven't seen it in any style guides.
Since Ruby has multiple ways to show negativity in a conditional, what is the preferred way to write a conditional that is checking that one part is true and one part is false? Example:
if array && !array.include?('Bob')
#do stuff!
But you could also say:
if array
#do stuff! unless array.include?('Bob')
or:
if array && not array.include?('Bob')
#do stuff
or:
if !array.nil? && !array.include?('Bob')
or a wacky double unless:
unless array.nil?
#do stuff unless array.include?('Bob')
And several others. Any idea which is considered the most Rubyish? Sources to back your opinion up?
As far as documented guidelines, the only thing that i can think of is the Google guide that admonishes "don't use and and or; always use && and || instead.".
Other than that, it somewhat depends on the context. If all you have is code to be executed if one condition is true and the other false, then I would definitely put them in a single if with && !:
if array && !array.include?('Bob')
#do stuff!
On the other hand, you might have additonal code that gets executed if the first condition is true even if the second one is also true; in that case, the nested unless or if makes sense:
if array
do stuff! unless array.include? 'Bob'
do other stuff anyway
end
Does not improve readability, but still interesting.
If you use Rails you can use ActiveSupport try extension like this:
if array.try(:include?, 'Bob')
# Do stuff

Resources