what is the value of if/unless modifier? - ruby

I could not find anywhere a formal specification of the if (or unless) modifier:
123 if x > 0
what is the value of the above statement if x is not above zero? irb suggests nil, but is it documented anywhere?
(yes, this is perhaps a stupid question, sorry, could not find a spec).

An expression that is not evaluated is the same as not existing. And your condition should be part of some code block such as definition or the main environment, etc. A code block without a content is evaluated to nil.
class A; end
# => nil
def foo; end
foo
# => nil
()
# => nil
begin; end
# => nil
eval("")
# => nil
So the reason your example returns nil has nothing to do with condition itself. It is just due to there being no evaluation.

Yes, it is nil. What else could it return? The statement in the if clause is used for the if itself, and the then statement is not executed. There is nothing to return, so nil.
Relevant spec
it "returns nil if else-body is empty and expression is false" do
if false
123
else
end.should == nil
end

Related

Best way to test if a variable is nil or false implicitly

I was hoping someone could help me with identifying the most idiomatic way to test if a variable is nil or false implicitly for the sake of readability.
Here is my explicit example:
if somevar.nil? # explicitly checking for nil.
puts "Its nil."
end
Is it better to write:
if !somevar # implicit
puts "Var is nil or false."
end
I really like this way:
if not somevar # implicit
puts "Var is nil or false"
end
From what I am reading, testing for nil explicitly should be avoided to some extent as its generally not needed. Most examples suggest to take action only if a variable exists. However, I want to perform an action when a variable does not exist.
Main inspiration for ditching nil came from: ruby_idioms
def truthiness(value)
if value
"truthy"
else
"falsy"
end
end
puts "nil:", truthiness(nil) # falsy
puts "false:", truthiness(false) # falsy
puts "true:", truthiness(true) # truthy
puts "[]:", truthiness([]) # truthy
puts "{}:", truthiness({}) # truthy
puts "0:", truthiness(0) # truthy
I know you know Python, so I can say that:
In Python, None, False, and empty containers such as [], {}, and even 0 are all falsy.
In Ruby, nil and false are falsy, whereas most other things are truthy, even [], {}, and 0.
I think the most idiomatic way in ruby for testing whether something is false or nil is using unless:
unless somevar
puts "Var is nil or false."
end
From the ruby style guide
Favor unless over if for negative conditions (or control flow
||).
# bad
do_something if !some_condition
# bad
do_something if not some_condition
# good
do_something unless some_condition
# another good option
some_condition || do_something
You can use defined? keyword :
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.
unless defined?(var)
#your code
end
Quick PRY to test, what the defined? returns when a variable is defined and when not :-
arup#linux-wzza:~/Ruby> pry
[1] pry(main)> defined?(x)
=> nil
[2] pry(main)> x = nil
=> nil
[3] pry(main)> defined? x
=> "local-variable"
[4] pry(main)>

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.

What happens when you have a case statement with no argument and the when clauses are lambdas?

This code does not perform as I expect:
case
when -> { false } then "why?"
else "This is what I expect"
end
# => "why?"
Neither does this
case
when ->(x) {false} then "why?"
else "This is what I expect"
end
# => "why?"
The first then clause is executed in both cases, which must mean that the lambda I supply to the when clause is not being called. I understand that the case equality operator === should be called on whatever the subject of the when clause is. I am wondering what goes on the other side of the === when there is no argument supplied to case. I was thinking it might be nil, but it can't be:
-> {false} === nil
# => ArgumentError: wrong number of arguments (1 for 0)
->(x) {false} === nil
# => false
This performs as expected and, if it were being executed, would lead to my expected case results or an exception. Can someone explain the results above? It seems that the case equality operator isn't being used at all, and yet the first when clause is evaluating to true. BTW, I am doing this because the output of a case can be used for variable assignment and it is less wordy then having several elsif clauses. I would like to be able to use arbitrary Procs in a case statement with no argument.
case
when -> { false } then puts "why?"
else puts "This is what I expect"
end
case
when 'cat' then puts "why?"
else puts "This is what I expect"
end
case
when -> { false }.call then puts "why?"
else puts "This is what I expect"
end
outputs:
why?
why?
This is what I expect
As The Pickaxe ( http://pragprog.com/book/ruby3/programming-ruby-1-9 ) says, there are two forms of case statement.
The first allows a series of conditions to be evaluated, executing
code corresponding to the first condition that is true: case when ‹
boolean-expression ›+ ‹ then › ...
The second form of a case expression takes a target expression
following the case keyword. case target when ‹ comparison ›+ ‹ then ›
...
In your case (case without target) any expression that is not false or nil (such as a Proc or the string 'cat') evaluates to true. The Proc is not executed, unless you call it.
You can use a case statement without a parameter to act similar to an if statement. For example:
case
when x > 0 then puts "positive"
else puts "negative"
end
You're assuming that it's trying to compare to nil, which isn't the case. Rather, when there's no parameter, the case statement is only testing for a "truthy" value (anything except nil and false). So when it hits your first when statement, it's checking to see if your Proc (meaning the actual ruby Proc object, not the results of executing your Proc) is nil or false, which isn't. Since it's "truthy", that code gets executed.
Try this, and you'll notice the Proc never even gets called/executed (you'll only see "bar", not "foo"):
case
when -> { puts 'foo' } then puts 'bar'
else puts "This line will never be printed"
end

In Ruby why won't `foo = true unless defined?(foo)` make the assignment?

What's going on here? What is the subtle difference between the two forms of "unless"?
> irb(main):001:0> foo = true unless defined?(foo)
=> nil
irb(main):002:0> unless defined?(fooo) ; fooo = false ; end
=> false
thx
Apparently, ruby creates local variable at parse time setting them to nilso it is defined and this is done whether the code is executed or not.
When the code is evaluated at your first line, it doesn't execute the assignment part since foo is set to nil. In the second line, because fooo has not been parsed yet, defined?returns nil letting the code inside the block execute and assign fooo.
As an example you can try this:
if false
foo = 43
end
defined? foo
=> "local-variable"
This is taken from a forum post at ruby-forum.
Let's start with something simpler:
# z is not yet defined
irb(main):001:0> defined?(z)
=> nil
# Even though the assignment won't execute,
# the mere presence of the assignment statement
# causes z to come to life.
irb(main):002:0> z = 123 if false
=> nil
irb(main):003:0> defined?(z)
=> "local-variable"
irb(main):004:0> z
=> nil
Now we can figure out your first example.
foo = true unless defined?(foo)
Is foo defined? Before we press ENTER in irb, no. However, the presence of the assignment statement causes foo to come to life. That means the assignment statement won't be executed, leaving foo in existence but having nil as its value. And what is the last expression evaluated in the irb line? It is unless defined?(foo), which evaluates to nil.
For more info on how assignments (even those that do not get executed) cause variables to exist, see this discussion of Variable/Method Ambiguity.
In your second example, there is nothing mysterious at all: fooo is not defined, so the code in the block executes, setting fooo to false. That assignment is the last expression evaluated, so false is the return value of our block.
irb(main)> foo = true unless defined?(Integer)
=> nil
irb(main)> foo = true unless defined?(thisIsUndefined)
=> true
Your first block is returning nil because the way it's written leaves 2 options:
foo is not defined --> assign true
foo is defined --> do nothing
Here, foo must be defined when the line is evaluated. Thus, nothing happens and nil is returned.
irb(main)> unless defined?(Integer) ; fooo = false ; end
=> nil
irb(main)> unless defined?(thisIsUndefined) ; fooo = false ; end
=> false
Your second block operates the same way your first one does. If fooo is not defined, the block is entered and fooo is set to false. The result of the last line of the block is the return value of the block, thus the false you are seeing. If fooo does exist, then the block is skipped over and nothing happens, therefore there is nothing to return, therefore the nil.
Based on your code, I would say that foo was defined when this code was run and fooo was not (test code shown was generated in Ruby 1.8.6). If you did not define either of these before running this code, then you may have something called foo that is defined by default (do defined?(foo) by itself to check). Try using a different name and see if you get the same results.
Edit:
irb(main)> defined?(bar)
=> nil
irb(main)> bar = true unless defined?(bar)
=> nil
irb(main)> defined?(bar)
=> "local-variable"
Apparently, defined?() is returning true since it has already seen bar (at the beginning of the line), even though you are still in the process of defining it.
in the first instance you call foo into existence in the assignment statement. Maybe this will clarify:
bar = if true
puts bar.class
else
puts "not reached"
end
NilClass
=> nil
baz = if true
puts baz.class
42
else
puts "not reached"
end
NilClass
=> 42
August, all look fine in 1.8.7:
$ irb
irb(main):001:0> unless defined?(fooo); fooo = true; end
=> true
irb(main):002:0> fooo
=> true
irb(main):003:0> `ruby --version`
=> "ruby 1.8.7 (2008-06-20 patchlevel 22) [i486-linux]\n"
Well.. One form is a block and one form isn't. The second part, the block, returns the last statement evaluated. The first one.. Hrm.. I don't know exactly what it's doing.
In Ruby 1.8.7:
foo = true unless defined?(foo)
p foo # => nil
unless defined?(fooo); fooo = true; end
p foo # => nil
I don't have an explanation for the behaviour you are seeing.

Checking if a variable is defined?

How can I check whether a variable is defined in Ruby? Is there an isset-type method available?
Use the defined? keyword (documentation). It will return a String with the kind of the item, or nil if it doesn’t exist.
>> a = 1
=> 1
>> defined? a
=> "local-variable"
>> defined? b
=> nil
>> defined? nil
=> "nil"
>> defined? String
=> "constant"
>> defined? 1
=> "expression"
As skalee commented: "It is worth noting that variable which is set to nil is initialized."
>> n = nil
>> defined? n
=> "local-variable"
This is useful if you want to do nothing if it does exist but create it if it doesn't exist.
def get_var
#var ||= SomeClass.new()
end
This only creates the new instance once. After that it just keeps returning the var.
The correct syntax for the above statement is:
if (defined?(var)).nil? # will now return true or false
print "var is not defined\n".color(:red)
else
print "var is defined\n".color(:green)
end
substituting (var) with your variable. This syntax will return a true/false value for evaluation in the if statement.
defined?(your_var) will work. Depending on what you're doing you can also do something like your_var.nil?
Try "unless" instead of "if"
a = "apple"
# Note that b is not declared
c = nil
unless defined? a
puts "a is not defined"
end
unless defined? b
puts "b is not defined"
end
unless defined? c
puts "c is not defined"
end
WARNING Re: A Common Ruby Pattern
the defined? method is the answer. See the accepted answer above.
But watch out... consider this common ruby pattern:
def method1
#x ||= method2
end
def method2
nil
end
method2 always returns nil.
The first time you call method1, the #x variable is not set - therefore method2 will be run. and
method2 will set #x to nil.
But what happens the second time you call method1?
Remember #x has already been set to nil. But method2 will still be run again!! If method2 is a costly undertaking this might not be something that you want.
Let the defined? method come to the rescue:
def method1
return #x if defined? #x
#x = method2
end
As with most things, the devil is in the implementation details.
Use defined? YourVariable
Keep it simple silly .. ;)
Here is some code, nothing rocket science but it works well enough
require 'rubygems'
require 'rainbow'
if defined?(var).nil? # .nil? is optional but might make for clearer intent.
print "var is not defined\n".color(:red)
else
print "car is defined\n".color(:green)
end
Clearly, the colouring code is not necessary, just a nice visualation in this toy example.
You can try:
unless defined?(var)
#ruby code goes here
end
=> true
Because it returns a boolean.
As many other examples show you don't actually need a boolean from a method to make logical choices in ruby. It would be a poor form to coerce everything to a boolean unless you actually need a boolean.
But if you absolutely need a boolean. Use !! (bang bang) or "falsy falsy reveals the truth".
› irb
>> a = nil
=> nil
>> defined?(a)
=> "local-variable"
>> defined?(b)
=> nil
>> !!defined?(a)
=> true
>> !!defined?(b)
=> false
Why it doesn't usually pay to coerce:
>> (!!defined?(a) ? "var is defined".colorize(:green) : "var is not defined".colorize(:red)) == (defined?(a) ? "var is defined".colorize(:green) : "var is not defined".colorize(:red))
=> true
Here's an example where it matters because it relies on the implicit coercion of the boolean value to its string representation.
>> puts "var is defined? #{!!defined?(a)} vs #{defined?(a)}"
var is defined? true vs local-variable
=> nil
It should be mentioned that using defined to check if a specific field is set in a hash might behave unexpected:
var = {}
if defined? var['unknown']
puts 'this is unexpected'
end
# will output "this is unexpected"
The syntax is correct here, but defined? var['unknown'] will be evaluated to the string "method", so the if block will be executed
edit: The correct notation for checking if a key exists in a hash would be:
if var.key?('unknown')
Please note the distinction between "defined" and "assigned".
$ ruby -e 'def f; if 1>2; x=99; end;p x, defined? x; end;f'
nil
"local-variable"
x is defined even though it is never assigned!
defined? is great, but if you are in a Rails environment you can also use try, especially in cases where you want to check a dynamic variable name:
foo = 1
my_foo = "foo"
my_bar = "bar"
try(:foo) # => 1
try(:bar) # => nil
try(my_foo) # => 1
try(my_bar) # => nil
Also, you can check if it's defined while in a string via interpolation, if you code:
puts "Is array1 defined and what type is it? #{defined?(#array1)}"
The system will tell you the type if it is defined.
If it is not defined it will just return a warning saying the variable is not initialized.
Hope this helps! :)
Leaving an incredibly simple example in case it helps.
When variable doesn't exist:
if defined? a then "hi" end
# => nil
When variable does exist:
a = 2
if defined? a then "hi" end
# => "hi"

Resources