`non-object-ness` of `nil` in ruby - ruby

From one of the online resource of Ruby I found the below statement:
The special object nil is, indeed, an object (it’s the only instance of a class called
NilClass). But in practice, it’s also a kind of non-object. The boolean value of nil is
false, but that’s just the start of its non-object-ness.
While nil responds to method calls as below,like other objects,what non-objectness author tried to say :
nil.to_s #=> ""
nil.to_i #=> 0
nil.object_id #=> 4
nil.inspect #=> "nil"
Can anyone help me here to understand the philosophy - non-object-ness of nil ?

nil is equivalent with null in other languages. Usually, null should not treated as a sane value.
However - as you may noticed - the syntax of Ruby language does everything over the method calls on objects, a lot more things than Python. Determining a sanity of values is a part of it.
Consider the following example
def foobar(arg)
if arg < 1
return nil
else
return "Oh, hi!"
end
end
res = foobar(rand(2))
puts res unless res.nil?
As you see, in the last line I check the nil-ness of the result with calling a nil? method. This is a most effective way to do it, because comparation operators can be overloaded and can do a very different things. The nil? returns with true only if the value can be treated as nil (usually, if the value is nil - but nil? method is overridable too, even if it is highly discouraged. Developers usually are not override this method).
Another useful property of nil it is has a to_s method, so you can x = "#{nil} and it results an empty string.
If nil weren't be an object, you cannot call nil? or other useful functions on that, but you can faced with a NullPointerException like in Java or a segmentation fault like in C/C++. And usually it is pointless.

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

Ruby defined?( 42[0][:foo] ) && defined?( 93[0]["bar"] ) == true. Why?

Short story:
"Why does defined?(59[0][:whatever]) evaluate to true?"
Long story:
I came across some strange behaviour lately which threw me off.
I was developing a method that did some washing of the data:
#Me washing input data:
def foo(data)
unless data && defined?(data[0]) && defined?(data[0][:some_param])
method2(data[0][:some_param])
else
freak_out()
end
end
I usually write tests where I spew in all kinds of junk data to make sure nothing strange happens:
describe "nice description" do
it "does not call method2 on junk data" do
expect(some_subject).to_not receive(:method2)
random_junk_data_array.each do |junk|
some_subject.foo(junk)
end
end
end
Well, method2 was called here. It happened when junk was a fixnum.
I am using ruby 2.1.0, and I can see that Fixnum has a #[] method which fetches the bit at that position, nice.
But why is fixnum[0][:some_param] considered to be defined?
defined? expression tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.
Let me explain with an example :-
defined?("a") # => "expression"
# this returns "method", as there is a method defined on the class String. So, the
# method invocation is possible, and this is a method call, thus returning `"method"`.
defined?("a".ord) # => "method"
# it return nil as there is no method `foo` defined on `"a"`,
# so the call is not possible.
defined?("a".foo) # => nil
Now coming to your point :-
As you said data[0] gives a Fixnum instance, and of-course Fixnum#[] exist. Thus fixnum_instance[:some_param] is also a valid method call. It just testing if the method is defined or not. If defined, it will tell yes this is a "method" expression. Otherwise nil. Not actually will check if the method call succeeded or not.
In Ruby all objects has truthy values except nil and false, thus "method" being a string object also has the truthy value, thus your condition got succeeded.

Is ruby's multidimensional array out of bounds behaviour consistent?

If I have a multidimensional array, I can exceed the bounds in the final dimension and get a nil return, but if I exceed the bounds of a non-final dimension, I receive an error. Is this by design, and if so what is the reasoning?
> ar = [ [00,01,02], [10,11,12], [20,21,22] ]
=> [[0, 1, 2], [10, 11, 12], [20, 21, 22]]
> ar[2][2]
=> 22
> ar[2][3]
=> nil
> ar[3][2]
NoMethodError: undefined method `[]' for nil:NilClass
from (irb):32
from :0
I understand why this is happening, but why isn't nil[] defined to return nil?
In Ruby, there are no multi-dimensional arrays. What you have there is an array, containing arrays as elements. So if you get the first "dimension", you get another array back (or nil if you exceeded the bounds of the outer array).
nil is an object of NilClass that has a finite (and small) set of defined methods. And the [] method, that is called when you use the whatever[:foo] syntax is just not defined on NilClass. Thus it can't return anything.
Generally, it wouldn't make sense to define all possible methods on nil as it would confuse people even more and would introduce a plethora of hard-to-detect bugs.
However if you know what you are doing and are willing to deal with the implications, you can use the try method that is defined by some frameworks (e.g. ActiveSupport for Rails) but is not part of Ruby itself. It catches the NoMethodError and returns nil instead. In your case you could use
> ar[2].try(:[], 2)
=> nil
However, this is generally discouraged as it makes things harder to debug. Instead you should check the bounds before trying to access the array (e.g. by using array.length) or by using include looping constructs like ar.each {|e| puts e}.
I don't know if Matz ever documented why the NilClass is designed the way it is. If it's not so then we can only guess. My guess is that it is based on the behavior of Smalltalk.
nil could either be message eating or exception throwing. Ruby has an exception throwing nil object.
If you have a message eating nil then it's difficult to determine the object that was the first one to return nil in a chained call like arr[1][2][3]. I don't know how often this really is an issue but it seems to be a valid point. As a counterexample Objective-C seems to do just fine with a message eating nil.
You can patch NilClass to become message eating
class NilClass
def method_missing(*)
nil
end
end
or for arrays only
class NilClass
def []
nil
end
end
both makes nil[] return a nil and either can break existing code
nil[][][][]
=> nil

Ruby Nil and Zero

What's the science behind the fact that the to_i method in Ruby's NilClass instances returns zero? Returning nil or raising an exception would not be more logical?
It fits the Ruby philosophy of permissiveness (as opposed to, for example, the strictness of Python):
nil.to_i #=> 0
"".to_i #=> 0
"123hello".to_i #=> 123
"hello".to_i #=> 0
As noted by Zabba, you can use Kernel#Integer(string) for strict conversion.
NilClass defines #to_i for the same reason it defines a #to_a that returns []. It's giving you something of the right type but an empty sort of value.
This is actually quite useful. For example:
<%= big.long.expr.nil? ? "" : big.long.expr %>
becomes:
<%= big.long.expr %>
Much nicer! (Erb is calling #to_s which, for nil, is "".) And:
if how.now.brown.cow && how.now.brown.cow[0]
how.now.brown.cow[0]
else
0
end
becomes:
how.now.brown.cow.to_a[0].to_i
The short conversions exist when only a representation is needed. The long conversions are the ones that the Ruby core methods call and they require something very close. Use them if you want a type check.
That is:
thing.to_int # only works when almost Integer already. NilClass throws NoMethodError
thing.to_i # this works for anything that cares to define a conversion
to_i means "convert me to an integer if you can".
If you want "if you're very much integer-like, give me your integer value, else give a NoMethodError", then use .to_int.
There's another question that asks about the difference between to_i and to_int, to_s versus to_str, etc. Let me know if you'd like me to find it for you.
The protocol of to_i says that you must return an Integer and you must not raise an exception. Both of your suggestions violate at least one of those rules. So, no, those would not only not be more logical, they would be simply invalid.
Note, however, that nil does not respond to to_int. If it did respond to to_int, that would, indeed, be "illogical".
If you happen to be in Rails then you can use try:
nil.to_i # => 0
nil.try :to_i # => nil
A concise Ruby 2.3+ method for returning nil for nil.to_i instead of 0, is to use the safe navigation operator:
irb(main):001:0> nil.to_i
=> 0
irb(main):002:0> nil&.to_i
=> nil

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

Resources