else inside rescue is useless?

C

cap

http://www.awprofessional.com/bookstore/product.asp?isbn=0672328844&rl=1

The "The ruby way" book said

In the event that error types are specified, it may be that an
exception does not match any of these types. For that situation, we are
allowed to use an else clause after all the rescue clauses.

begin
# Error-prone code...
rescue Type1
# ...
rescue Type2
# ...
else
# Other exceptions...
end

but actually, I found the "else" clause is only executed when there is
no exception been thrown,such as :

-------------------------
begin
puts "do something"
rescue NameError
puts "name error #{$!}"
else
puts "else #{$!}"
end
--------------------------

the result is

-------------------
do something
else
------------------

so the "else" clause is useless because we can simply put the content
of "else" caluse to the last line in the "begin" block before the first
"rescue"

Am I wrong or the book is wrong?

my ruby version is
ruby 1.8.4 (2005-12-24) [i686-linux]
 
R

Robert Klemme

http://www.awprofessional.com/bookstore/product.asp?isbn=0672328844&rl=1

The "The ruby way" book said

In the event that error types are specified, it may be that an
exception does not match any of these types. For that situation, we are
allowed to use an else clause after all the rescue clauses.

begin
# Error-prone code...
rescue Type1
# ...
rescue Type2
# ...
else
# Other exceptions...
end

This is plain wrong - at least in 1.8.5. The Pickaxe says the "else"
clause is executed if there was *no exception*. To catch any exception
you would have to replace the "else" with "rescue Exception => e" as
David indicated. However, there's a glitch (read on).
but actually, I found the "else" clause is only executed when there is
no exception been thrown,such as :

-------------------------
begin
puts "do something"
rescue NameError
puts "name error #{$!}"
else
puts "else #{$!}"
end
--------------------------

the result is

-------------------
do something
else
------------------

so the "else" clause is useless because we can simply put the content
of "else" caluse to the last line in the "begin" block before the first
"rescue"

Am I wrong or the book is wrong?

I think the book is wrong - and there is also a difference between the
Pickaxe and the interpreter implementation (unfortunately). Try this code:

def foo(x)
begin
puts "x=#{x}"

case x
when 0
puts "returning"
return
when 1
raise "Some error"
when 2
raise NameError, "wrong name"
when 3
break "broken"
else
puts "do something"
end

puts "unsafe else"
rescue NameError
puts "name error: #{$!}"
else
puts "else"
ensure
puts "leaving!"
end
end

5.times do |i|
begin
foo i
rescue Exception => e
puts "Exception: #{e}"
end
end

With my 1.8.5 I see:

x=0
returning
leaving!
x=1
leaving!
Exception: Some error
x=2
name error: wrong name
leaving!
x=3
leaving!
Exception: unexpected break
x=4
do something
unsafe else
else
leaving!

What bugs me is that the "else" clause is not executed if it is left via
a "return". Taking the Pickaxe 2nd edition pg. 362 literally ("If an
else clause is present, its body is executed if no exceptions were
raised in code.") it would have to be executed in that case, too. So,
if the interpreter would behave as stated in the book, there was
actually a difference between the code at the end of the block and the
code inside the else clause.

Even in the current situation (where there seems to be no semantically
difference) putting code into the else clause instead of the end of the
block might have an advantage documentation wise.

Matz, can you clarify whether this is a bug in the interpreter or
whether the Pickaxe is wrong (or incomplete) here?

Kind regards

robert
 
W

Wilson Bilkovich

http://www.awprofessional.com/bookstore/product.asp?isbn=0672328844&rl=1

The "The ruby way" book said

In the event that error types are specified, it may be that an
exception does not match any of these types. For that situation, we are
allowed to use an else clause after all the rescue clauses.

begin
# Error-prone code...
rescue Type1
# ...
rescue Type2
# ...
else
# Other exceptions...
end

but actually, I found the "else" clause is only executed when there is
no exception been thrown,such as :

-------------------------
begin
puts "do something"
rescue NameError
puts "name error #{$!}"
else
puts "else #{$!}"
end
--------------------------

the result is

-------------------
do something
else
------------------

so the "else" clause is useless because we can simply put the content
of "else" caluse to the last line in the "begin" block before the first
"rescue"

Am I wrong or the book is wrong?

my ruby version is
ruby 1.8.4 (2005-12-24) [i686-linux]

From the Rubinius test suite:
@Count = 1
begin
p @Count
raise ArgumentError, 'just kidding' unless @Count > 3
rescue Exception => e
@Count += 1
retry
else
p 7
ensure
p 8
end

this prints:
1
2
3
4
7
8

'else' only runs if no exception was raised.
 
M

Morton Goldberg

def foo(x)
begin
puts "x=#{x}"

case x
when 0
puts "returning"
return
when 1
raise "Some error"
when 2
raise NameError, "wrong name"
when 3
break "broken"
else
puts "do something"
end

puts "unsafe else"
rescue NameError
puts "name error: #{$!}"
else
puts "else"
ensure
puts "leaving!"
end
end

5.times do |i|
begin
foo i
rescue Exception => e
puts "Exception: #{e}"
end
end

With my 1.8.5 I see:

x=0
returning
leaving!
x=1
leaving!
Exception: Some error
x=2
name error: wrong name
leaving!
x=3
leaving!
Exception: unexpected break
x=4
do something
unsafe else
else
leaving!

What bugs me is that the "else" clause is not executed if it is
left via a "return". Taking the Pickaxe 2nd edition pg. 362
literally ("If an else clause is present, its body is executed if
no exceptions were raised in code.") it would have to be executed
in that case, too. So, if the interpreter would behave as stated
in the book, there was actually a difference between the code at
the end of the block and the code inside the else clause.

I would be horrified if the <else> were executed after a <return> --
<else> is not <ensure>. The Pickaxe book should be probably say: "If
an else clause is present, its body is executed if no exceptions were
raised and no exit was made in _code_ ." In fact, I just submitted an
erratum to this effect on the Pragmatic Programmer site.
Even in the current situation (where there seems to be no
semantically difference) putting code into the else clause instead
of the end of the block might have an advantage documentation wise.

Also from Pickaxe: "Exceptions raised during the execution of the
else clause are not captured by rescue clauses in the same block as
the else." That seems to be a semantic difference.

Regards, Morton
 
D

Dave Thomas

What bugs me is that the "else" clause is not executed if it is
left via a "return". Taking the Pickaxe 2nd edition pg. 362
literally ("If an else clause is present, its body is executed if
no exceptions were raised in code.") it would have to be executed
in that case, too. So, if the interpreter would behave as stated
in the book, there was actually a difference between the code at
the end of the block and the code inside the else clause.

Why would you expect the else clause to be executed if a return is
executed? That seems counter-intuitive to me.


Cheers


Dave
 
G

George Ogata

Even in the current situation (where there seems to be no semantically
difference) putting code into the else clause instead of the end of the
block might have an advantage documentation wise.

If I'm understanding you correctly, there is a difference between:

begin
f
g
rescue E
end

and

begin
f
rescue E
else
g
end

and that is that any E's raised in #g won't be rescued in the latter.
 
R

Robert Klemme

Why would you expect the else clause to be executed if a return is
executed? That seems counter-intuitive to me.

I have low expectations with regard to the "else" clause but the book
clearly states that it is executed if there was no exception - that
includes the case where the block is left via a "return". Behavior and
description simply are not in sync, that's my point.

I can't remember ever having used this type of "else" clause so far so I
am pretty nonreligious about it. Although I can imagine a situation
where I would want to make sure some cleanup code is executed only when
there was no error in the block - and this includes leaving via
"return". Of course you can argue that leaving via "return" is a non
standard way to leave the block... As I said, I am pretty relaxed here.
I just think this needs some clarification - either way or the other.

Kind regards

robert
 
D

Dave Thomas

I have low expectations with regard to the "else" clause but the
book clearly states that it is executed if there was no exception -
that includes the case where the block is left via a "return".
Behavior and description simply are not in sync, that's my point.


Interesting: I'd honestly never have thought of that interpretation.
I guess in my mind, the semantics of return take precedence over most
other semantics, and I explicitly note the times where that's not the
case. Thanks for pointing out an alternative.

Do you feel I should also document the other places when a return
could be issued? For example, in the description of a while loop, I
say "executes body zero or more times as long as boolean-expression
is true." Should I add "or a return is executed or an exception is
thrown" to this and similar descriptions?

I kind of feel that this would clutter the descriptions, and that
most readers would make the assumption that return and exceptions
would break the flow. Is that an incorrect assumption?


Regards


Dave
 
R

Robert Klemme

Interesting: I'd honestly never have thought of that interpretation. I
guess in my mind, the semantics of return take precedence over most
other semantics, and I explicitly note the times where that's not the
case. Thanks for pointing out an alternative.

Dave, you're welcome! I was probably in formal mode when reading that,
although I believe the situation here is a tad different from the
"while" loop case:
Do you feel I should also document the other places when a return could
be issued? For example, in the description of a while loop, I say
"executes body zero or more times as long as boolean-expression is
true." Should I add "or a return is executed or an exception is thrown"
to this and similar descriptions?

Maybe just "... or the body of the loop is left via other control flow
statements". You could even add a footnote which enumerates them. :)
I kind of feel that this would clutter the descriptions, and that most
readers would make the assumption that return and exceptions would break
the flow. Is that an incorrect assumption?

I agree about cluttering and I think a change is not needed in this
cases - in other words, I believe it's not an incorrect assumption. :)

I think the case with "else" clause after "rescue" clauses is a bit
different because control flow is not explicit like with loops. It is
immediately clear that "break", "return", "raise" and "throw" will
instantly leave the loop (is it?). However, with "rescue - else" we are
talking about less explicit control flow and so maybe a more explicit
description is helpful here.

As I said, it's probably a corner case and I have no idea how often this
"else" is actually used in applications. I for my part cannot remember
ever having used it - apart from the research for this thread.

It's a totally different question whether the behavior of the
interpreter should change so code in "else" clause is executed even for
a "return". There might be arguments in favor and against it. Maybe
Matz can shed some light about the reasoning why it is the way it is.

Kind regards

robert
 
D

dblack

Hi --

Interesting: I'd honestly never have thought of that interpretation. I guess
in my mind, the semantics of return take precedence over most other
semantics, and I explicitly note the times where that's not the case. Thanks
for pointing out an alternative.

Do you feel I should also document the other places when a return could be
issued? For example, in the description of a while loop, I say "executes body
zero or more times as long as boolean-expression is true." Should I add "or
a return is executed or an exception is thrown" to this and similar
descriptions?

I kind of feel that this would clutter the descriptions, and that most
readers would make the assumption that return and exceptions would break the
flow. Is that an incorrect assumption?

No, I think it's correct. I consider "executes body" to expand
automatically to "executes body unless execution is stopped, which can
always happen in various ways no matter what else is going on".

I think the reason the question arose in the particular case of
begin/rescue/else/ensure... is that it involves the role of what to me
is a somewhat elusive clause (the else) right in the middle of a
construct where the control flow is already relatively complex.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
D

dblack

Hi --

Dave, you're welcome! I was probably in formal mode when reading that,
although I believe the situation here is a tad different from the "while"
loop case:


Maybe just "... or the body of the loop is left via other control flow
statements". You could even add a footnote which enumerates them. :)

I don't think that's needed. Executing a body of code means executing
all the instructions in it, and it's already known that the
instruction set includes things that affect flow control. So unless
those things are specifically excluded in a given context, I would
assume they are available.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
B

Benedikt Heinen

Dave Thomas replied:
Interesting: I'd honestly never have thought of that interpretation. [...]
Do you feel I should also document the other places when a return could be
issued? For example, in the description of a while loop, I say "executes body
zero or more times as long as boolean-expression is true." Should I add "or
a return is executed or an exception is thrown" to this and similar
descriptions?

I kind of feel that this would clutter the descriptions, and that most
readers would make the assumption that return and exceptions would break the
flow. Is that an incorrect assumption?

I think detailing every condition under which else would not get called is
overdoing it a bit.

Instead of listing the conditions under which the else might not be
reached; how about instead of writing "The body of an else clause is
executed only if no exceptions are raised by the main body of code." You'd
write "The body of an else clause is executed only if the end of the main
body of code is reached without causing any exceptions". This to me would
sound more clear - a return statement in the main block would prevent the
end of the main body of code to be reached.


So, the difference is just that I can use the else block to insert code
that will run before the ensure (in your book example before the ensured
file.close) but outside of the rescue blocks (i.e. code in the else block
CAN throw an exception which will not be handled in the current
begin/rescue block... (Whether this is useful or not, is another
issue)...


def testme()
begin
begin
puts "foo"
#return(1);
puts "raising error"
puts "bar"
rescue
puts "inner rescue block"
else
raise ArgumentError.new("blah");
ensure
puts "ensured"
end

rescue
puts "outer rescue block"
end
end

testme();

will print

foo
bar
raising error
ensured
outer rescue block


If I were to uncomment the return(1), it would print

foo
ensured



Benedikt

ALLIANCE, n. In international politics, the union of two thieves who
have their hands so deeply inserted in each other's pockets that
they cannot separately plunder a third.
(Ambrose Bierce, The Devil's Dictionary)
 
B

Benedikt Heinen

From the Rubinius test suite:
@count = 1
begin
p @count
raise ArgumentError, 'just kidding' unless @count > 3
rescue Exception => e
@count += 1
retry
else
p 7
ensure
p 8
end
[...]

'else' only runs if no exception was raised.

Yes, else only runs if no exception was raised; and it will run BEFORE the
ensure block. BUT unlike code in the main code block (begin..rescue) it
CAN raise unchecked exceptions.



Benedikt

ALLIANCE, n. In international politics, the union of two thieves who
have their hands so deeply inserted in each other's pockets that
they cannot separately plunder a third.
(Ambrose Bierce, The Devil's Dictionary)
 
W

Wilson Bilkovich

From the Rubinius test suite:
@count = 1
begin
p @count
raise ArgumentError, 'just kidding' unless @count > 3
rescue Exception => e
@count += 1
retry
else
p 7
ensure
p 8
end
[...]

'else' only runs if no exception was raised.

Yes, else only runs if no exception was raised; and it will run BEFORE the
ensure block. BUT unlike code in the main code block (begin..rescue) it
CAN raise unchecked exceptions.

Do you feel like that's an implementation quirk of Ruby, or something
deliberate?
In other words, this code..

@count = 0
begin
@count += 1
raise RuntimeError.new("Ha Ha!") if @count == 1
rescue Exception
puts "hello from rescue"
retry
else
puts "else"
raise RuntimeError.new("Boom") if @count == 2
end

Is it part of ruby's 'spec' that the 'rescue' block only be executed
once, in the above code?
 
B

Benedikt Heinen

'else' only runs if no exception was raised.
Do you feel like that's an implementation quirk of Ruby, or something
deliberate?
In other words, this code..

@count = 0
begin
@count += 1
raise RuntimeError.new("Ha Ha!") if @count == 1
rescue Exception
puts "hello from rescue"
retry
else
puts "else"
raise RuntimeError.new("Boom") if @count == 2
end

Is it part of ruby's 'spec' that the 'rescue' block only be executed
once, in the above code?

It sounds like it is deliberate - otherwise, why introducing the else
block at all? That's the only thing that else does.

Also, when you say "rescue block only be executed once in the above code",
if you took the first raise ("Ha Ha!") away - it would not trigger the
rescue at all. This is apparently, why 'else' is positioned AFTER
'rescue' in the execution order. Once you're past the rescue block, there
is no rescue to jump back to. i.e. if you want to rescue from an exception
in the else block, you'd need to do it in a separate begin..rescue..end
block wrapping around the begin..rescue..else..end construct you are
using.



Benedikt




ALLIANCE, n. In international politics, the union of two thieves who
have their hands so deeply inserted in each other's pockets that
they cannot separately plunder a third.
(Ambrose Bierce, The Devil's Dictionary)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top