Implicit value of if/unless statement in ruby - ruby

I'm new to Ruby, and trying to understand why this works (it does seem to, at least according to "the Master"):
def array_of_fixnums?(array)
true unless array.find { |item| item.class != Fixnum }
end
My concern is where the "false-ness" is coming from when the array contains non-fixnum values. Am I right to assume there is no implicit "else false" in the unless statement? In that case I assume it must be coming from the nil value returned by Enumerable#find. Is that correct?
If so, that seems a bit shaky. Might it be better to return false explicitly, like this?
array.find { |item| item.class != Fixnum } ? false : true
Is there another, better way entirely? Thanks for helping me wrap my head around this, and for any "best practice" suggestions.

Your method is returning nil not because find returns nil, but because if your inline conditional does not pass, the method has no explicit return value. This would be more clear if it were not inline, consider:
def array_of_fixnums?(array)
unless array.find { |item| item.class != Fixnum }
return true
end
# otherwise don't explicitly return (implicit nil)
end
While relying on the falsiness of nil will often work, it is problematic in that it does not follow the principle of least surprise. A ? method should return true or false.
But your method has worse problems. It uses confusing logic (a double negative), and itself relies on the falsiness of nil and the truthiness of not nil, to function. Consider what happens if your method were passed [false]. Oops.
The better way would be something like:
array.all? {|n| n.is_a? Fixnum }
The reasoning is that this method does exactly what it says, plainly.
Returning a boolean explicitly, while not necessarily wrong, is superfluous and often considered bad practice. Rather consider the example, which says, in ruby speak, is every one of the values in this array a Fixnum?. The result of that expression is what the method is after; there's no reason to evaluate it then return true|false.

From the find method doc:
Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.
Thus, all you need is:
def array_of_fixnums?(array)
array.find { |item| item.class != Fixnum }
end
If anything is found, that will be returned, otherwise nil will be returned, and the method will evaluate do false.
However, as the item return could be an false or nil (if any item in the list is false or nil) I would recommend that you use the .any? method instead.
def array_of_fixnums?(array)
array.any? { |item| item.class != Fixnum }
end

Let's look at why true unless condition works as it does.
I believe x unless condition was provided as an esthetic improvement to x if !condition, but the two are equivalent (and, in my opinion, it is an improvement). To get rid of double-negatives, I'll just consider x if condition.
One of Ruby's basic tenets is that every statement must return a value (namely, the last expression evaluated). Therefore, x if condition must be evaluated as if condition then x else y end. But what is y? If y evaluated as true or false, x if condition would be meaningless when x is a boolean expression. To see why, consider:
w = (x == 5 if z == 6)
If w were true, we could conclude that x = 5 and z = 6, but if w were false, we wouldn't know whether z = 6 and x != 5, or z != 6 and x = 5 or x != 5. Considering that any value of y other than nil is evaluated as true or false, the only reasonable value for y is nil. That way, if w == nil is be available. Of course, this is still a problem (in other situations) when x could possibly be nil.
I would welcome clarification and elaboration. Perhaps there is also a less convoluted way of making this argument.

Related

Is there a comparison Operator that returns true if both values are set and equal: false if both are nil

Is there an ideomatic or shorter way to "compare two values but don't allow them to be nil".
One way would be:
(!left.nil? || !right.nil?) && (left == right)
One of the nil checks could be omitted, here, but I left it in the example, to show the intent better.
Is there a method in Ruby core, on Kernel or BasicObject, next to equal?, eql?, == and such that matches this?
I aim at something similar to what e.g. minitest assert_equal does (edited for brevity, nil-check at line 5):
def assert_equal exp, act, msg = nil
msg = message(msg, E) { diff exp, act }
result = assert exp == act, msg
if nil == exp then
refute_nil exp, "Use assert_nil if expecting nil."
end
result
end
In my case this is for an autorization system where I repeatedly have to guard against both sides being nil. E.g.
class ContactPolicy < ApplicationPolicy
def add?
!actor.id.nil? && subject.id == actor.id
end
end
After repeating that pattern in various forms, I wondered if there is not a better Ruby-ism for this. I am now leaning towards a refactoring using null-objects that have a def ==(other) which always return false. The question of whether this "equal but not nil" remains interesting though.
Possible Approaches
The optimal answer will depend on why you think either value might be nil, and other aspects of your code that are not shown in your original question. Without that context, it appears that this is primarily an attempt to guard against nil values raising a NameError exception, while also preventing your expression from resolving to (nil == nil) == true. If that's the case, you can take one of the following approaches, among others:
Rescue all possible exceptions.
Rescue NameError explicitly.
Avoid exception handling, and use a conditional expression chain to test whether your variables are both defined and not-nil.
Handle NameError Exceptions from Undefined Variables
You might rescue all exceptions, but this is generally considered a poor approach in the general case.
# Swallow anything descended from Exception. This is
# common in the wild, so it's idiomatic by definition,
# but it can cast too wide of a net sometimes, and may
# lead to unexpected problems.
left == right rescue nil
It would be better to rescue NameError, but it's up to you to figure out how the program should handle the exception. Perhaps you will decide to set the variables, and then retry the rescued block, but there are certainly other approaches.
# Rescue just NameError; other exceptions would still
# get raised when necessary.
begin
left == right
rescue NameError => e
# handle the exception
end
Avoiding Undefined Variables
There are a number of situations where Ruby will autovivify variables within a given scope, but your example isn't one of them. In this case, you can rely on operator precedence to ensure your values are defined using the defined? keyword.
For example, assuming left and right are either undefined? or can respond_to? :nil?, then you can use the following expression to:
Ensure both left and right are defined. Returns "expression" if they are both defined variables within an array literal.
Ensure neither of the values in [left, right] are nil using Enumerable#none? (which is mixed into Array) and the Object#nil? method that Array inherits.
Check equality of your two defined, non-nil variables.
defined? [left, right] and
[left, right].none? &:nil? and
left == right
#=> nil
Based on your question, the expectation is that when your variables resolve to nil == nil the whole expression should still return false.
The same conditional expression also guards against both variables being set to nil rather than simply being undefined. For example:
# Test with nil values.
left, right = nil, nil
defined? [left, right] and
[left, right].none? &:nil? and
left == right
#=> false

Why does .all? return true on an empty array?

Using Ruby I want to evaluate all items in an array, and return true if they all pass a conditional test.
I can do this using e.g. array.all? { |value| value == 2 }
So:
> array=[2,2]
> array.all? { |value| value == 2 }
=> true
> array=[2,3]
> array.all? { |value| value == 2 }
=> false
Great!
But, why does an empty array pass this test?
> array=[]
> array.all? { |value| value == 2 }
=> true
Shouldn't this return false?
And if I need it to return false, how should I modify the method?
This is a vacuous truth. It's the standard interpretation of a universal quantification, i.e. a
collection.all? { |x| some_predicate(x) }
over an empty collection, but it's known to strike people as counter-intuitive when they first see it in a formal setting. One nice way to think about why this is the preferred semantics is to think about how you would implement all?.
To make your test require that the array is non-empty, just do
array.any? && array.all? { |x| x == 2 }
Note that array.any? is fast no matter how large the array, whereas array.all? { |x| x == 2 } can be slow, depending on how big array is and how rare 2 is in it. So put the array.any? first.
Also note, there are degenerate cases where this won't work, for instance if array is [nil] or [false]. If cases like this might come up, replace array.any? with array.any? { true }.
In Ruby you can never loop over an empty collection (array, hashes, etc.), so in your case your block never gets executed. And if the block never gets executed, all? returns true (there is no condition to make the result false).
Read about all? in the Ruby documentation.
You can simply achieve your goal by
!array.empty? && array.all? { |value| value == 2 }
The documentation says : "The method returns true if the block never returns false or nil.."
In the case of an empty array the block never executes and hence the method will always return true. As far as returning false is concerned you'll have to arr.empty?
There is no item in that array that doesn't pass the test. I think you may need to throw in a test for array length.
Just go
!(array.empty? || array.any? {|x| x != 2})
(Which has the added advantage of failing fast—that is, it can be evaluated properly without having to scan the whole array.)
Since there is no item in the array that FAILS that test, it returns true. So just use somehting like:
array.size > 0 and array.all? { |value| value == 2}
Or something like that.
Zeroes, empty collections, empty matrices and such have always been a bit special, if not outright problematic. Greeks knew well why they didn't count 0 among natural integers.
Method all? would be the first to ask you "why are you calling me on an empty array?" What do you mean by "all?", when there is nothing in there? That's a contradiction. And the method does short thinking, and answers true for the reasons outlined in the other three answers. Remember, you are at fault for talking about "all elements" of an empty array to begin with.
As Amit Kumar Gupta writes, it is the standard interpretation of universal quantification. I have no idea why you expect it to be false. Here, you can see it should be true by inference.
Universal quantification is equivalent to conjunction, thus ("<=>" means equivalent):
"for all x in [a, b, c], P(x)" <=> "P(a) and P(b) and P(c)"
Notice that any proposition is equivalent to the conjunction of true and itself, so:
"for all x in [a, b, c], P(x)" <=> "true and P(a) and P(b) and P(c)"
If you lessen the elements in the set to two, you get:
"for all x in [a, b], P(x)" <=> "true and P(a) and P(b)"
and further to one element:
"for all x in [a], P(x)" <=> "true and P(a)"
Now, what happens with the empty set? Naturally,
"for all x in [], P(x)" <=> "true"
By noticing that existential quantification is equivalent to disjunction, you can also see that you should expect false with existential quantification over an empty set.
The source of all? method says that it uses static variable(which is initially set to true) and then performs the AND operation between the static variable value and the result of the iteration finally returns this static variable as a result.
as the array is Empty ruby will never iterate on this empty array and as a result of this all? method will return the static variable which was set to true.
Make sure the array is not empty first.
Then:
array.compact.present? && array.all? {|x| x != 2}

Why do method declarations evaluate to nil in ruby?

Defining a method doesn't seem to evaluate to a truthy value as can be checked by putting one inside an if condition:
if(def some_method; puts "random text"; end) then
puts "declaration evaluates to true"
else
puts "declaration evaluates to false"
end
# => declaration evaluates to false
Why/How does a method declaration evaluate to nil?
It actually evaluates to nil. This makes sense; why would a method creation return anything?
irb(main):001:0> def test; print 'test'; end
=> nil
However, it has to return something, so to return "nothing" would be to return nil.
Every statement in Ruby evaluates to something. The def statement's value is not supposed to be checked and is therefore nil.
You will find the behavior you are looking for in the reflective "meta-programming" method define_method.
class EmptyClass
m = define_method(:newmethod) {p "I am the new method"}
p m # => <Proc:0x50b3f359#E:\NetBeansProjects\RubyApplication1\lib\main.rb:6>
end
From Ruby gotchas:
Boolean evaluation of non-boolean data is strict: 0, "" and [] are all evaluated to true. In C, the expression 0 ? 1 : 0 evaluates to 0 (i.e. false). In Ruby, however, it yields 1, as all numbers evaluate to true; only nil and false evaluate to false. A corollary to this rule is that Ruby methods by convention — for example, regular-expression searches — return numbers, strings, lists, or other non-false values on success, but nil on failure. This convention is also used in Smalltalk, where only the special objects true and false can be used in a boolean expression.
Method definions such as def some_method; puts "random text"; end always return nil.
Now, that means the method is evaluated to nil. According to the Ruby Documentation:
Returns false if obj is nil or false; true otherwise.
Since your method return nil, if will evaluate it as false therefore execute the else statement.

Double ampersand in Ruby

I am using the authlogic gem with Ruby on Rails, and I have been using the following to obtain the id of the user that is currently logged in:
current_user = UserSession.find
id = current_user && current_user.record.id
I'm not understanding how current_user && current_user.record.id returns the current user id. I would think this would return a boolean. Can someone explain how this works?
There is no Boolean type in Ruby; Ruby has a rather simple view of truth (or more precisely, it has a rather simple view of falsehood).
the false object, which is the singleton instance of FalseClass is considered falsy
the nil object, which is the singleton instance of NilClass is falsy
every other object is truthy (including, obviously, the true object, which is the singleton instance of TrueClass)
[BTW: this means that a lot of objects that are considered falsy in some other languages, are actually truthy in Ruby, like the integer 0, the real value 0.0, the empty string, the empty array, the empty hash, the character 'F']
So, the Boolean operators &&, ||, and and or do not return Boolean values. Instead they return the first object that determines the outcome of the expression.
(They are also short-circuiting, which means that they only evaluate the minimum sub-expressions that are needed to determine the outcome of the expression. So, an alternate formulation would be that they return the result of the last expression that was evaluated. Which, in turn, is analogous to what blocks, methods, class bodies and module bodies do.)
So, what does it mean to return the first object that determines the outcome? That's simple, really: the result of the expression
a && b
is truthy if both a and b are truthy, otherwise it is falsy. So, if a is falsy, it is completely irrelevant what b is: the result will be falsy either way. So, we might just as well simply return a. (Remember, a doesn't have to be false, it could also be nil and the programmer might want to know which one of the two it was.)
If, OTOH, a is truthy (IOW it is neither nil nor false), then the result of the whole expression is solely dependent on b: if b is truthy, the whole result will be truthy, otherwise if b is falsy, the whole result will be falsy. So, we might just as well return b itself, instead of first converting it to a Boolean.
|| and or are analogous or more precisely dual to && and and.
You posted this example:
id = current_user && current_user.record.id
Here, the author isn't even expecting current_user to be a Boolean value! Instead, he expects it to be either a User or nil. But that's perfectly fine, because a User is truthy and nil is falsy, so they still can be used in a Boolean expression.
The basic intention is that the author wants to prevent a NoMethodError exception being raised, if he tries to call #record on nil.
An alternative way of expressing this would be
id = current_user.record.id unless current_user.nil?
If you want all the gory details, check out Section 11.1 (page 36) of the Draft ISO Ruby Specification or the excutable specifications of the RubySpec project. (Here's the one for &&.)
I wrote a pure Ruby implementation of Ruby's Boolean operators and conditional expressions once for fun. The meat of the implementations is these two mixins.
The logical and is short circuiting. That means that if the construct is X && Y, and X is false then Y never gets checked because the whole thing is certainly going to be yield false.
That code is saying, essentially:
if (current_user is TRUE) {
id = current_user.record.id;
]
Here's some console output showing you get the second value if the first is true:
irb(main):005:0> true && 9
=> 9
and nil if the first is nil:
irb(main):008:0> nil && 9
=> nil

What does !! mean in ruby?

Just wondering what !! is in Ruby.
Not not.
It's used to convert a value to a boolean:
!!nil #=> false
!!"abc" #=> true
!!false #=> false
It's usually not necessary to use though since the only false values to Ruby are nil and false, so it's usually best to let that convention stand.
Think of it as
!(!some_val)
One thing that is it used for legitimately is preventing a huge chunk of data from being returned. For example you probably don't want to return 3MB of image data in your has_image? method, or you may not want to return your entire user object in the logged_in? method. Using !! converts these objects to a simple true/false.
It returns true if the object on the right is not nil and not false, false if it is nil or false
def logged_in?
!!#current_user
end
! means negate boolean state, two !s is nothing special, other than a double negation.
!true == false
# => true
It is commonly used to force a method to return a boolean. It will detect any kind of truthiness, such as string, integers and what not, and turn it into a boolean.
!"wtf"
# => false
!!"wtf"
# => true
A more real use case:
def title
"I return a string."
end
def title_exists?
!!title
end
This is useful when you want to make sure that a boolean is returned. IMHO it's kind of pointless, though, seeing that both if 'some string' and if true is the exact same flow, but some people find it useful to explicitly return a boolean.
Note that this idiom exists in other programming languages as well. C didn't have an intrinsic bool type, so all booleans were typed as int instead, with canonical values of 0 or 1. Takes this example (parentheses added for clarity):
!(1234) == 0
!(0) == 1
!(!(1234)) == 1
The "not-not" syntax converts any non-zero integer to 1, the canonical boolean true value.
In general, though, I find it much better to put in a reasonable comparison than to use this uncommon idiom:
int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
It's useful if you need to do an exclusive or. Copying from Matt Van Horn's answer with slight modifications:
1 ^ true
TypeError: can't convert true into Integer
!!1 ^ !!true
=> false
I used it to ensure two variables were either both nil, or both not nil.
raise "Inconsistency" if !!a ^ !!b
It is "double-negative", but the practice is being discouraged. If you're using rubocop, you'll see it complain on such code with a Style/DoubleNegation violation.
The rationale states:
As this is both cryptic and usually redundant, it should be avoided
[then paraphrasing:] Change !!something to !something.nil?
Understanding how it works can be useful if you need to convert, say, an enumeration into a boolean. I have code that does exactly that, using the classy_enum gem:
class LinkStatus < ClassyEnum::Base
def !
return true
end
end
class LinkStatus::No < LinkStatus
end
class LinkStatus::Claimed < LinkStatus
def !
return false
end
end
class LinkStatus::Confirmed < LinkStatus
def !
return false
end
end
class LinkStatus::Denied < LinkStatus
end
Then in service code I have, for example:
raise Application::Error unless !!object.link_status # => raises exception for "No" and "Denied" states.
Effectively the bangbang operator has become what I might otherwise have written as a method called to_bool.
Other answers have discussed what !! does and whether it is good practice or not.
However, none of the answers give the "standard Ruby way" of casting a value into a boolean.
true & variable
TrueClass, the class of the Ruby value true, implements a method &, which is documented as follows:
Returns false if obj is nil or false, true otherwise.
Why use a dirty double-negation when the standard library has you covered?

Resources