I encountered some code that looks like:
(input_array || []).each do |a|
some stuff
end
What is the purpose of input_array || []? I would naively think that this would evaluate to a boolean value which would cause each to throw an error, but that's clearly not whats happening.
The semantics of || are:
if the first expression is not nil or false, return it
if the first expression is nil or false, return the second expression
This is used to provide a default value if the first is nil.
Related
I tested this on console:
[] || 1 # => []
Shouldn't it return the value that exits, and not []? I can change it to ternary operator, which works fine, but why does the condition above not work?
Because [] is truthy value in Ruby, so the second part of your expression is never executed, it always evaluates to []. In Ruby, just false and nil aren't truthy.
Oh, anyway, you don't need that. map returns an empty array if the array is empty.
Model.new(
name: abc.name,
description: abc.description,
product_ids: abc.product_ids.map(&:id)
)
The exact semantics of || are:
if first expression is not nil or false, return it
if first expression is nil or false, return the second expression
So since [] is truthy it will evaluate to [], as explained by #Ursus.
Trying to learn Ruby I ran into this kind of syntax.... Can anyone explain me what it means?
a = nil if b.nonzero?
nonzero? : Returns self if num is not zero, nil otherwise.
And thus does not return a boolean
Values in ruby are truthy and falsey. That is, if a value is not nil, or false, it is true. So if you have a function that returns 1, you can use that in a boolean expression some_function && true would resolve true.
Likewise, if it returned nil, some_function && true would return false.
There's a detailed and in depth explanation here: https://gist.github.com/jfarmer/2647362
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.
I have an array, if I find a value in it, I want to execute a block of code. Also, if the array is nil, I want to execute that block. So the code I tried is:
if !array.respond_to? :index || array.index(str)
#some code
So if it's nil it's true, or if str is somewhere in the array, it's true, right? But if it finds the item at index 0, it doesn't enter the block. Also, according to irb false || 0 evalueates to 0. WTF?? I thought that everything was true except false and nil. I guess || does something odd that I'm not expecting??
My questions are: What's going on? What's a nice way to write a conditional that does what I want?
Using nil? and include? with an inline if seems most idiomatic to me.
#your code if arr.nil? || arr.include?(str)
if array.nil? || array.member?(s)
# ...
false || 0 evaluates to 0 because it's an or. False isn't truthy (obviously ;) but 0 is, so the expression is truthy.
Are you checking for a nil array or an empty one? If you've already declared the array it won't be nil even if it's empty. I'd write it like:
if array.empty? || array.include(str)
or if you really want to check for a nil array:
if array.nil? || array.include(str)
I'd use .include rather than .index to avoid getting a 0.
if array.nil? || array.member?(str)
#code block
end
The || operator almost reminds you of a coalesce.
Given a = false, b = :bacon
return a || b #returns :bacon
Experimenting with the conditional operator in ruby,
def nada
false ? true : nil
end
def err
false ? true : raise('false')
end
work as expected but
def reflection
false ? true : return false
end
produces a syntax error, unexpected keyword_false, expecting keyword_end
def reflection
false ? true : return(false)
end
and attempted with brackets syntax error, unexpected tLPAREN, expecting keyword_end
yet
def reflection
false ? true : (return false)
end
works as expected, and the more verbose if...then...else...end
def falsy
if false then true else return false end
end
also works as expected.
So what's up with the conditional (ternary) operator?
You can use it like this, by putting the entire return expression in parentheses:
def reflection
false ? true : (return false)
end
Of course, it does not make much sense used like this, but since you're experimenting (good!), the above works! The error is because of the way the Ruby grammar works I suppose - it expects a certain structure to form a valid expression.
UPDATE
Quoting some information from a draft specification:
An expression is a program construct which make up a statement (see 12
). A single expression can be a statement as an expression-statement
(see 12.2).12
NOTE A difference between an expression and a statement is that an
expression is ordinarily used where its value is required, but a
statement is ordinarily used where its value is not necessarily
required. However, there are some exceptions. For example, a
jump-expression (see 11.5.2.4) does not have a value, and the value
of the last statement of a compound-statement can be used.
NB. In the above, jump-expression includes return among others.
I think this is all related to the ruby parser.
ruby parses return as the else-expression of the ternary operator
ruby is then surprised when it finds false instead of end
wrapping return false in parentheses causes ruby to interpret the entire thing as the else-expression
return(false) doesn't work because ruby is still trying to interpret just the return part as the else-expression, and is surprised when it finds a left-parenthesis (updated)
Note: I don't think this is a great answer.
A great answer could, for example, explain the parse errors with reference to the ruby grammar.
The ternery operator is just that, an operator. You don't return from it. You return from functions. When you put a return in an if, you return from the function that the if is in. Since there is no variable awaiting assignment from the result of the if, there is no problem. When you return from the ternery operator, there is no value assigned to the variable.