Exceptions: why does adding parenthesis change anything? - ruby

There are a few things I'd like to understand in how Ruby treats inline Error handlers
Case 1
This is a common use case
def foo
raise Error
end
bar = foo rescue 1
# => 1
bar
# => 1
It works as expected. The expression foo rescue 1 returns 1 and it is correctly assigned to bar.
Case 2
Ruby allows descructuring arrays, so this behavior seems odd.
baz = 'a'
baz, bar = foo rescue [1, 2]
# => [1, 2]
baz
# => 'a'
bar
# => nil
The expression returns the array [1, 2] but doesn't deconstruct or assign it. It completely skips the assignment altogether.
Case 3
When you wrap the the error in parenthesis, however, the deconstruction works.
baz, bar = (foo rescue [1, 2])
# => [1, 2]
baz
# => 1
bar
# => 2
Case 4
Bonus points: Raising the Error and trying to handle it inline also skips assignment
baz = raise Error rescue 1
# => 1
baz
# => nil
But adding parenthesis makes it work.
Edit:
I tested this on Ruby 1.9.3-p392 and Ruby 2.0.0
Edit 2:
I added labels to cases
Edit 3:
Apparently some folks think this isn't a question, so maybe the title wasn't obvious enough. Here's the question in full text:
Why do these inconsistencies happen, and why does adding parenthesis change anything?

Your case 2 is the same as this:
baz = 'a'
(baz, bar = foo) rescue [1, 2]
Since foo raises an error, assignment of values to baz and bar fails, so baz remains to be "a", and bar remains to be nil, a value that was assigned during the parsing stage. Then, the assignment gets rescued, and the return value would be [1, 2].
Your case 4 is the same as this:
(baz = raise Error) rescue 1
Since the righthand side of the assignment raises an error, assignment to baz fails, and baz would remain to be nil, that was assigned during the parsing stage. Then, the assignment is rescued, and the return value is 1.

Update:
Sorry, my example did not worked. I guess this is a bug in the parser as simple assignment does not suffer this issue.
bar, baz = 1,2 # bar == 1, baz == 2
bar = foo rescue [3,4] # bar == [3,4], baz == 2
bar, baz = 1,2 # bar == 1, baz == 2
bar, baz = foo rescue [3,4] # no assignment done: bar == 1, baz == 2
Even precedence of rescue can not explain why the simple assignment is done and multi- does not.

Related

Why is it okay to write or but not ||?

I understand there is a difference in the precedence as shown in another answer:
p foo = false || true
# => true
p foo = false or true
# => false
But it seems like there is something more that's different between or and ||.
For example:
p foo = 42 or raise "Something went wrong with foo"
# => 42
p foo = nil or raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
p foo = 42 || raise "Something went wrong with foo"
# => syntax error, unexpected tOP_ASGN, expecting end-of-input
I was expecting to get:
p foo = 42 or raise "Something went wrong with foo"
# => 42
p foo = nil or raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
p foo = 42 || raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
But it's a syntax error. So what is happening?
Theory :
Here's a precedence table for Ruby.
It's not clear from this table but Ruby method invocation without parentheses has a lower precedence than || and =, but higher than or. See this question.
So for your code, from highest to lowest precedence :
||
=
raise "something"
or
Expression with or
foo = 42 or raise "Something went wrong with foo"
First comes = :
( foo = 42 ) or raise "Something went wrong with foo"
Then raise :
( foo = 42 ) or ( raise "Something went wrong with foo" )
Then or :
( ( foo = 42 ) or ( raise "Something went wrong with foo" ) )
Expression with ||
foo = 42 || raise "Something went wrong with foo"
First comes || :
foo = ( 42 || raise ) "Something went wrong with foo"
Here's your syntax error!
You want :
foo = 42 || (raise "Something went wrong with foo") #=> 42
or
foo = 42 || raise("Something went wrong with foo") #=> 42
or just
foo = 42 || raise
Warning!
When you have troubles with precedence, you should be careful about adding another puts or p without parentheses !
For example :
p [1,2,3].map do |i|
i*2
end
outputs :
#<Enumerator: [1, 2, 3]:map>
even though you might have expected :
[2, 4, 6]
|| and or are not the same operation.
The first is equivalent to a method call, the latter is a control flow keyword. You probably always want to use || to avoid confusion with precedence. Most style guides for Ruby have a clause that bans the use of and and or for that reason.
So then,
A or B
# can be considered equivalent to
if A then A else B end
whereas
A || B
# can be considered equivalent to
A.or { B } # given a hypothetical "logical or" method
Now let's look into your or example
p foo = false or true
is equivalent to
temp = p(foo = false) # => nil
if temp
temp
else
true
end
and thus when executed prints false and returns true
[1] pry(main)> p foo = false or true
false
=> true
[2] pry(main)> foo
=> false
whereas
p foo = false || true
is equivalent to (glossing over the difference between boolean and logical OR for now since your example is dealing with booleans anyway)
p(foo = false.|(true))
and thus when executed prints true and returns true
[1] pry(main)> p foo = false || true
true
=> true
[2] pry(main)> foo
=> true

Different behaviour of 'do .. end' and "{..}" block in ruby [duplicate]

This question already has answers here:
What is the difference or value of these block coding styles in Ruby?
(7 answers)
Closed 6 years ago.
Sorry if this question is duplicate. But I can't find the difference in usage. When I run the below codes I am getting different answers. I see from most tutorials that using of "do ... end" is same as "{ ... }" blocks.
include Comparable
a = [1, 4, 2, 3, 5]
p a.sort do |x,y|
y <=> x
end
output shows as = [1, 2, 3, 4, 5]
but when I run like this...
include Comparable
a = [1, 4, 2, 3, 5]
p a.sort { |x,y|
y <=> x
}
output shows as = [5, 4, 3, 2, 1]
what was wrong here. is there any situations that two syntaxes has any different behaviour?
The precedence is different. The first one is interpreted as
p (a.sort) do
...
end
Since the block is not passed to sort, it is sorted in the default, ascending order. Then, the block is passed to p, which does not use it.
sawa's answer is correct, but as the OP has asked for more clarification, I'm supplying my own answer.
All four of these method calls behave the same, passing a block to a foo method:
foo { ... }
foo do ... end
foo() { ... }
foo() do ... end
When you write two methods and a block, without parentheses around the arguments, it is unclear which method the block goes with:
foo bar { ... }
foo bar do ... end
The question is: "Am I passing a block to bar, and then passing its return value to foo? Or am I calling foo with bar as an argument and also passing along a block?"
With parentheses, you can make this clear using either block style:
# Passing a block to `bar`, and then passing the result to `foo`
foo( bar { ... } )
foo( bar do ... end )
# Passing an argument and block to `foo`
foo( bar ) { ... }
foo( bar ) do ... end
The difference between {…} and do…end that you have run into is where Ruby chooses to put the parentheses when you omit them. The two block notations have different precedence, and so you end with different results:
# Passing a block to `bar`, and then passing the result to `foo`
foo bar{ ... }
foo( bar do ... end )
# Passing an argument and block to `foo`
foo bar do ... end
foo( bar ){ ... }
So, specifically in your case:
# This code…
p a.sort do |x,y|
y <=> x
end
# …is the same as this code:
b = a.sort
p(b){ |x,y| y <=> x }
# Note that you are passing a block to the `p` method
# but it doesn't do anything with it. Thus, it is
# functionally equivalent to just:
p a.sort
And,
# This code…
p a.sort { |x,y|
y <=> x
}
# …is functionally the same as this code:
b = a.sort{ |x,y| y <=> x }
p b
Finally, if you still don't get it, perhaps deeply considering the following code and output will help:
def foo(a=nil)
yield :foo if block_given?
end
def bar
yield :bar if block_given?
:bar_result
end
foo bar { |m| puts "block sent to #{m}" }
#=> block sent to bar
#=> foo got :bar_result
foo( bar do |m| puts "block sent to #{m}" end )
#=> block sent to bar
#=> foo got :bar_result
foo( bar ){ |m| puts "block sent to #{m}" }
#=> foo got :bar_result
#=> block sent to foo
foo bar do |m| puts "block sent to #{m}" end
#=> foo got :bar_result
#=> block sent to foo
Notice that the first and last examples in the code immediately above differ only in whether they use {…} or do…end for the block.
As others have pointed out, there is a difference in precedence between the two types of blocks. However, the precedence does not really influence how it's used in practice.
Some Rubyists follow the Weirich Convention, named so by Avdi Grimm.
In short, the use of one over the other is based on whether it's functional (return value is used), in which case { ... } is used, or procedural (changes the state of the system in some way or performs output), in which case do ... end is used.

Ruby return in yield block called from a method with ensure

def foo
puts "in foo"
s = yield
puts "s = #{s}"
return 2
ensure
puts "in ensure"
return 1
end
def bar
foo do
puts "in bar block"
return 3
end
return 4
end
[36] pry(main)> r = bar
in foo
in bar block
in ensure
=> 4
I'd expect r = 3 but it turns out it is r = 4. If I remove the ensure code, r = 3 is expected. Why is it?
def foo
puts "in foo"
s = yield
puts "s = #{s}"
return 2
end
r = bar
in foo
in bar block
=> 3
It's a Ruby's feature of "unwinding the stack" from blocks. How your return works step by step:
You return 3 from bar block. return_value = 3 and Ruby marks that it is a return value from block, so it should unwind the stack and return 3 from parent function. It would not return to foo at all if there was no ensure section. It is very important difference between returning from functions and from blocks.
But Ruby always executes ensure, and there is one more return in ensure section of foo.
After return 1 in ensure section of foo, return_value is 1. It is not a value from block, so Ruby "forgets" about previous return 3 and forgets to return it from bar.
It returns 1 from foo and 4 from bar.
Moreover, if you write next 3 instead of return 3 in the block - it will return to foo after yield and execute puts "s = #{s}"; return 2 even without the ensure block. This is a magical Ruby feature for iterators and enumerators.

Is there a way to implicitly evaluate variables in ruby?

In PHP, I can do this:
$a = 1;
$c = 'a';
$$c = 2;
//now $a == 2
Is there any equivalent in ruby? By which I mean, any simple way to have it dereference a variable during execution like this? I'd rather not use eval, because it looks messy - I've already determined that eval can't be called as a method of a string.
It is possible but it's a bit more complicated., and you actually have two possibilities:
Kernel#local_variables
Returns the names of the current local variables.
fred = 1
for i in 1..10
# ...
end
local_variables #=> [:fred, :i]
Binding#local_variable_get/set
Returns a value of local variable symbol.
def foo
a = 1
binding.local_variable_get(:a) #=> 1
binding.local_variable_get(:b) #=> NameError
end
This method is short version of the following code.
binding.eval("#{symbol}")
if you just need this you can do
a = 1
c = 'a'
eval("#{c} = 2")
a == 2 # => true
... but this is moron way to do this
if you need this for instance variables
class Foo
attr_reader :a
def initialize
#a = 1
end
end
foo = Foo.new
foo.instance_variable_get(:a) #=> 1
foo.a #=> 1
foo.instance_variable_set(:"#a", 2)
foo.a #=> 2
...you can also eval instance like this:
# ...
foo.instance_eval do
#a = 'b'
end
foo.a # => 'b'

Two similar sentences have different behaviour. Is it ok?

Two similar sentences have different behaviour. Is it ok?
Compare:
a = 123 unless defined? a
a # => nil
but...
unless defined? b
b = 123
end
b # => 123
Yes, this is the correct behaviour. Local variables are created and initialized with nil before assignment. So this code
a = 123 unless defined? a
a # => nil
is a rough equivalent of
a = nil
a = 123 unless defined? a # `a` is not undefined anymore.
a # => nil
Another example (even though c is not defined before this line, this code does not throw a NameError).
c = 2 unless c # => 2

Resources