ruby or operator behaving differently inside initialise method - ruby

fairly new to programming and came across the following code which does gives the intended result, that is it stores the object in the instance variable if passed as an argumnet or defaults to generating a new object and storing that. but the syntax is using an or(||) operator which as i understood always returns a boolean. so is it behaving differently or there is more to or/and operators?
def initialize(args = {})`
#sms = args[:sms] || Messenger.new
end

The or operator in ruby does not "return" a boolean: it evalutes to the first expression if the first expression is truthy (a term which means everything but false and nil), otherwise it evalutes to the latter
5 || nil => 5
nil || 5 => 5
false || nil => nil
nil || false => false

in ruby, everything can be evaluated as an expression, and an expression will return a value
so || and even && do not return a boolean it will return a truthy or falsy value
in the case of || it will check if the first expression is truthy if not it will go to the next one, then the next one, if no truthy value is found it will return the last one which will be falsy as well
irb(main):004:0> nil || "true value" || 0
=> "true value"
irb(main):006:0> nil || false || "true value" || 0 || true
=> "true value"
in the case of && it will do the opposite, it will return the first falsy value it can find
irb(main):001:0> nil && false && "true value" && 0 && true
=> nil
and the if condition will understand truthy and falsy values which will be what these logical operators return
rb(main):007:0> if "true"
irb(main):008:1> puts "true"
irb(main):009:1> else
irb(main):010:1> puts "false"
irb(main):011:1> end
true
irb(main):017:0> if nil
irb(main):018:1> puts "true"
irb(main):019:1> else
irb(main):020:1> puts "false"
irb(main):021:1> end
false

Related

Why isn't 'false' equal to 'nil'?

false is not equal to nil, for example:
false == nil # => false
The same goes for false and 0:
false == 0 # => false
You get the same result for nil and 0:
nil == 0 # => false
Why does Ruby act like this?
In Ruby, the only time nil will return true on a == comparison is when you do: nil == nil
You can read more about nil here:
https://ruby-doc.org/core-2.2.3/NilClass.html
nil is the equivalent of undefined in JavaScript or None in Python
Consider in JavaScript:
>> (undefined === false)
=> false
Or Python:
>> (None == False)
=> False
Nil and False (Equality)
Despite what many people new to the language may expect, nil and false are objects rather than language keywords. Consider FalseClass which says:
The global value false is the only instance of class FalseClass and represents a logically false value in boolean expressions.
Likewise, NilClass is:
[t]he class of the singleton object nil.
When you use a method like Comparable#== to compare them, Ruby invokes the spaceship operator <=> on the instances since neither class defines the #== method. Because nil and false are not the same objects, and because neither overrides the basic equality operator, the expression false == nil will always be false.
Likewise, an Integer like 0 is neither an instance of FalseClass nor NilClass. Both false == 0 and nil == 0 are therefore also false because neither instance evaluates to zero.
Truthy and Falsey (Conditionals)
For branching purposes, both false and nil evaluate as false in a conditional expression. Everything else evaluates as true.
Consider the following non-idiomatic, contrived, but hopefully illustrative example of Ruby's basic truth tables:
def truth_table value
if value
true
else
false
end
end
truth_table nil
#=> false
truth_table false
#=> false
truth_table true
#=> true
truth_table 'a'
#=> true
truth_table 0
#=> true
truth_table 1
#=> true
Boolean Evaluation
Nil and false are semantically different in Ruby, and descend from different base classes. They are also not equal to each other. However, they can both be treated as "not true" for branching purposes or within a Boolean context. For example, consider the following idioms for casting a result as a Boolean true or false value:
!(nil)
#=> true
!!(nil)
#=> false
You can use this idiom to shorten the previous truth table example to:
# Convert any value to a Boolean.
def truth_table value
!!value
end
# Test our truth table again in a more compact but less readable way.
table = {}
[nil, false, true, 'a', 0, 1].map { |v| table[v] = truth_table(v) }; table
#=> {nil=>false, false=>false, true=>true, "a"=>true, 0=>true, 1=>true}

Boolean behaving strange, logic issue

I'm having an issue with boolean expected behavior. I can make the desired result but I'm not sure why the strange behavior occurs. This produces a wrong result:
def palindrome?(string)
rev = string.reverse
if rev == string
true
else
false
end
end
palindrome?("abc") == false # => true
palindrome?("abcba") == true # => true
palindrome?("z") == true # => true
while this produces the correct result:
def palindrome?(string)
rev = string.reverse
if rev == string
true
end
end
palindrome?("abc") == false # => false
palindrome?("abcba") == true # => true
palindrome?("z") == true # => true
The following might be similar:
def nearby_az(string)
counter = 0
string = string.split("")
if string.count == 1
false
elsif (string.index("z") - string.index("a")) <= 3
true
else
false
end
end
nearby_az("a") == false # => true
nearby_az("z") == false # => true
Because Ruby method will return the result of executing the last expression. For this if statement:
if rev == string
true
end
it will return nil if rev != string.
Actually, your first function is producing the correct result. The problem is the second formula. It returns nothing if the string is not a palindrome; it only returns true if the string is a palindrome. Therefore,
palindrome?("abc") == false
for the second equation returns false because palindrome?("abc") returns nothing because "abc" is not a palindrome, and so it is not equal to false.
The second implementation of palindrome? will return nil if it is not a palindrome. Thus nil == false # => false. In Ruby, all functions return something although it may be nil.

Need clarification on Ruby logical operators

Recently I started learning Ruby. I am practicing logical operators in irb and I got these results, which I don't understand. Can you please clarify these examples for me?
1 and 0
#=> 0
0 and 1
#=> 1
0 && 1
#=> 1
As opposed to other languages like C, in Ruby all values except for nil and false are considered “truthy”. This means, that all these values behave like true in the context of a boolean expression.
Ruby's boolean operators will not return true or false. Instead, they return the first operand that causes the evaluation of the condition to be complete (also known as short-circuit evaluation). For boolean and that means, it will either return the first “falsy” operand or the last one:
false && 1 # => false (falsy)
nil && 1 # => nil (falsy)
false && nil # => false (falsy)
1 && 2 # => 2 (truthy)
For boolean or that means, it will either return the first “truthy” operand or the last one:
false || 1 # => 1 (truthy)
nil || 1 # => 1 (truthy)
false || nil # => nil (falsy)
1 || 2 # => 1 (truthy)
This allows for some interesting constructs. It is a very common pattern to use || to set default values, for example:
def hello(name)
name = name || 'generic humanoid'
puts "Hello, #{name}!"
end
hello(nil) # Hello, generic humanoid!
hello('Bob') # Hello, Bob!
Another similar way to acheive the same thing is
name || (name = 'generic humanoid')
With the added benefit that if name is truthy, no assignment is performed at all. There is even a shortcut for this assignment of default values:
name ||= 'generic humanoid'
If you paid careful attention you will have noticed that this may cause some trouble, if one valid value is false:
destroy_humans = nil
destroy_humans ||= true
destroy_humans
#=> true
destroy_humans = false
destroy_humans ||= true
destroy_humans
#=> true, OMG run!
This is rarely the desired effect. So if you know that the values can only be a String or nil, using || and ||= is fine. If the variable can be false, you have to be more verbose:
destroy_humans = nil
destroy_humans = true if destroy_humans.nil?
destroy_humans
#=> true
destroy_humans = false
destroy_humans = true if destroy_humans.nil?
destroy_humans
#=> false, extinction of humanity digressed!
That was close! But wait, there is another caveat – specifically with the usage of and and or. These should never be used for boolean expressions, because they have very low operator precedence. That means they will be evaluated last. Consider the following examples:
is_human = true
is_zombie = false
destroy_human = is_human && is_zombie
destroy_human
#=> false
is_human = true
is_zombie = false
destroy_human = is_human and is_zombie
destroy_human
#=> true, Waaaah but I'm not a zombie!
Let me add some parentheses to clarify what's happening here:
destroy_human = is_human && is_zombie
# equivalent to
destroy_human = (is_human && is_zombie)
destroy_human = is_human and is_zombie
# equivalent to
(destroy_human = is_human) and is_zombie
So and and or are really just useful as “control-flow operators”, for example:
join_roboparty or fail 'forever alone :('
# this will raise a RuntimeError when join_roboparty returns a falsy value
join_roboparty and puts 'robotz party hard :)'
# this will only output the message if join_roboparty returns a truthy value
I hope that clarifies everything you need to know about these operators. It takes a bit of getting used to, because it differs from the way other languages handle it. But once you know how to use the different options, you've got some powerful tools at hand.
Both values are 'truthy' (in Ruby everything that isn't nil or false is truthy), so in all cases the second value is returned. On the contrary, if you use 'or', first value will be returned:
1 || 0 #=> 1
0 || 1 #=> 0
In Ruby both 0 and 1 is truth value. (Only nil and false are false value)
If both operands are truth value, and, && returns the last value.

Ruby return wrong type

def starts_with_consonant?(str)
str.empty? || str.class != String || /\A[^aeiou]/i=~str
end
p starts_with_consonant? "Apple" #=>nil
p starts_with_consonant? "microsoft"#=> 0
I expect it to return true or false, but it returns nil and 0.
Its because the Regex test in your last expression returns nil or 0, in the case of a match. You need to coerce the match into a boolean
def starts_with_consonant?(str)
str.empty? || str.class != String || (/\A[^aeiou]/i=~str != nil)
end
In Ruby every object is considered true(truthy), except for nil and false. This includes 0:
puts '0 is true' if 0
0 is true
For all intents and purposes, your code already returns false and true, it will work correctly with if or boolean operators like && and ||.
Only direct comparison will show the difference:
starts_with_consonant? "Apple" == false
=> false
But such comparison is not needed for anything in Ruby, and often is considered bad style. Just use if or unless
if starts_with_consonant? "Apple"
#...
end
unless starts_with_consonant? "Apple"
#...
end

Ruby OR || syntax with an object

I have seen this syntax in Ruby:
x = ary['value'] || Value.new
I get the part that if left side of the || is false, then the right side will be executed. But I do not get the part that:
false || (object) becomes (object)
I thought || should resolve to boolean. At least in most other languages. Why is Ruby resolving to an object.
Another similar question I also have:
'test' || true
=> "test"
How does 'test' get evaluated as true?
In Ruby nil and false is evaluates to false only always. Look below:
p a = nil || 2 #=> 2
p a = false || 2 #=> 2
p nil || false || 2 #=> 2
p '' || 2 #=> ""
For more reference look here True, False And Nil Objects In Ruby and Ruby short circuit "or" explanation
In Ruby, you have four cases:
False is false
True is true
Nil is false-y
Anything else is truth-y
So,
5 && 8 #=> 8
5 || false #=> 5
false || 5 #=> 5
(5 && 8) == true #=> false
!!(5 && 8) == true #=> true
!!(false || nil) == false #=> true
The great rule of thumb on boolean evaluation in Ruby is that only the objects NilClass and FalseClass are evaluated as false. Any other object, including String, is evaluated to true, even if it's empty.
In Ruby, as in most programming languages, some values are truthy, while others are falsey. A truthy value is true for the purposes of boolean evaluation, and likewise a falsey value is false.
However, the truthy value isn't actually cast to true and a falsey isn't cast to false, which lets the boolean expression evaluate to something useful, rather than a flat true or false.
nil and false are the only falsey values in Ruby. Note that 0 is truthy, unlike in most other languages.
See https://gist.github.com/jfarmer/2647362 for more details.
This is called short-circuiting.
Any expression formed by joining together other expressions with the || operator will be short-circuited, meaning it "stops" at the first truthy expression.
So consider this example:
nil || Value.new
The Ruby interpreter looks at nil first; then, since that is not truthy, it moves on to the next expression.
Now consider the example at the end of your question:
"test" || true
Ruby looks at "test" first; and since that is truthy, the evaluation stops there.
The same is true of the && operator, which is essentially the same short-circuiting logic but only stopping once it finds a falsey expression.
Take this example:
person && person.name
If person is nil, then the above expression will evaluate to nil since that is the first falsey expression.
On the other hand:
person && person.male? && person.name
Suppose person is not nil, but is female. Then (presuming male? returned false) the above expression would evaluate to false.
And of course, if no expression is falsey, then the && operator just gives you the last one (which is typically what you want).

Resources