Struggling with Blocks

G

gwtmp01

Nonetheless, I have sympathy for the OP. The different semantics of
'begin ... end' and 'do ... end' _are_ confusing.

There is certainly a syntactic similarity to begin/end and do/end but
why are the semantics of those particular pairs confusing?
Why stop there? You also have to deal with: module/end, class/end,
def/end, if/end, case/end.

At some point you just have to realize that different syntactic
blocks have different semantics and then learn the semantics.

Gary Wright
 
R

Rick DeNatale

I have to admit that I was struggling a bit when I made the analogy to
an anonymous method. The key is that begin pushes a new stack frame
onto the call stack.

You are right about the scoping of local variables.

Having spent a bit more time perusing the 1.8 source code, my analogy
was a little off.

What really happens is that the rescue and ensure statements
effectively push a tag onto a stack which comprises local variables on
the C call stack, and then do a setjmp before executing the code being
protected, the tag contains the jmp_buf for setjmp, along with other
state needed for exception handling. Raising the exception does a
longjmp to the jmp_buf in the tag on the top of the stack, passing a
state which indicates that an exception is being raised.

The tag stack is also used for other purposes such as implementing
break, next, redo, and retry in loops.

Now I guess I could say that this "kind of" like an internal call, but
it's stretching things a bit further than I'd like

The break...end statement really just serves as a syntactic marker to
delimit where a series of statements being protected by
rescue/else/ensure starts, and where the last rescue/else/ensure ends.
If a begin..end sequence doesn't contain any rescue/ensure clauses, it
has no effect.

def..end also can mark those protected statements, although it also
serves the purpose of delimiting a method.

Now there might be a reason why the do..end, or {..} of a block can't
have the same feature, but I'm scratching my head to figure out why.
You CAN have a block which contains a single statement with a rescue
modifier:

lambda do
raise "help!" rescue "gotcha"
end

is legal as is the semantically equivalent:

lambda {raise "help!" rescue "gotcha"}

but

lambda do
raise "help!"
rescue
"gotcha"
end

is not.

Perhaps someone else (Matz?) can enlighten me/us on why
 
M

Morton Goldberg

Having spent a bit more time perusing the 1.8 source code, my analogy
was a little off.

What really happens is that the rescue and ensure statements
effectively push a tag onto a stack which comprises local variables on
the C call stack, and then do a setjmp before executing the code being
protected, the tag contains the jmp_buf for setjmp, along with other
state needed for exception handling. Raising the exception does a
longjmp to the jmp_buf in the tag on the top of the stack, passing a
state which indicates that an exception is being raised.

The tag stack is also used for other purposes such as implementing
break, next, redo, and retry in loops.

Now I guess I could say that this "kind of" like an internal call, but
it's stretching things a bit further than I'd like

The break...end statement really just serves as a syntactic marker to
delimit where a series of statements being protected by
rescue/else/ensure starts, and where the last rescue/else/ensure ends.
If a begin..end sequence doesn't contain any rescue/ensure clauses, it
has no effect.

def..end also can mark those protected statements, although it also
serves the purpose of delimiting a method.

Now there might be a reason why the do..end, or {..} of a block can't
have the same feature, but I'm scratching my head to figure out why.
You CAN have a block which contains a single statement with a rescue
modifier:

lambda do
raise "help!" rescue "gotcha"
end

is legal as is the semantically equivalent:

lambda {raise "help!" rescue "gotcha"}

but

lambda do
raise "help!"
rescue
"gotcha"
end

is not.

Perhaps someone else (Matz?) can enlighten me/us on why

Even without further explication, the above is already enlightening.
Thanks for taking the time and trouble to research this and write it up.

Regards, Morton
 
R

Rick DeNatale

Even without further explication, the above is already enlightening.
Thanks for taking the time and trouble to research this and write it up.

The journey is the reward!
 
M

MonkeeSage

Rick said:
You CAN have a block which contains a single statement with a rescue
modifier:

lambda do
raise "help!" rescue "gotcha"
end

is legal as is the semantically equivalent:

lambda {raise "help!" rescue "gotcha"}

but

lambda do
raise "help!"
rescue
"gotcha"
end

is not.

Perhaps someone else (Matz?) can enlighten me/us on why

I sure can't enlighten you, but I can comment: that is wierd! I must
admit that I didn't believe you at first, but I tried it, and sure
enough...you are correct. What the?! Wierd!!

Regards,
Jordan
 
N

Newbie

The OP is confused as to why the different semantics exist at all. It appears (to a newbie) to be
very arbitrary. One pair of delimiters for a block being passed to a yield method or turned into a
proc, another for a block with error trapping.
 
G

gwtmp01

Now there might be a reason why the do..end, or {..} of a block can't
have the same feature, but I'm scratching my head to figure out why.
You CAN have a block which contains a single statement with a rescue
modifier:

lambda do
raise "help!" rescue "gotcha"
end

is legal as is the semantically equivalent:

lambda {raise "help!" rescue "gotcha"}


These two example illustrate the ability to attach a rescue modifier
to a method call.
The fact that in these example the method calls are sitting by
themselves inside
a block is irrelevant--the rescue is associated with the call and not
with
the enclosing block. You can attach a rescue clause to any statement:

(3/0) rescue "divide by zero"

[1,2,3].map {|x| x/0 } rescue "divide by zero"

I think is important to view blocks as part of the syntax of a method
call
in the same way that the period, parenthesis, and commas are part of
the syntax
of a method call:

receiver.method(arg1, arg2) { #code in a block }

So 'rescue', when it is attached to a single method call, is really
part of the
syntax of the method call and the surrounding context is of no concern.
You can even have a rescue right in the middle of actual arguments:


a = Array.new( 3/0 ) # raises ZeroDivisionError
a = Array.new( (3/0 rescue 100) ) # creates an array with 100 entries

Note: I first tried that last example without the extra set of parens
and the
parser didn't like it: Array.new( 3/0 rescue 100 ). Seems like an
interesting
corner case on statement/expression parsing.

Anyway, my point is that rescue modifiers and their semantics are
orthogonal to
block syntax (do/end {} ) and its semantics.
lambda do
raise "help!"
rescue
"gotcha"
end

is not.


In this example, the rescue is *not* a modifier of the method call
'raise'. The
parser has fully processed the raise and is attempting to parse the
next method call.
In that case rescue is not valid because do/end blocks do not open a
new exception
context in the way that a method call or begin/end block do.

It might be easier to see what is going on from a parsing standpoint
by substituting
semicolons for newlines:

lambda do
raise "help!" rescue "gotcha" # one statement, a call to raise
end

lambda do
raise "help!"; rescue "gotcha" # two statements, second one invalid
end


The method/statement modifier version of 'rescue' is syntactically
different than
the version of 'rescue' attached to method definitions or begin/end
blocks.

This may not have clarified the question regarding *why* do/end
blocks don't
create a new exception scope but hopefully it clarifies the syntax
associated with
a rescue modifier clause.

Gary Wright
 
R

Rick DeNatale

These two example illustrate the ability to attach a rescue modifier
to a method call.
The fact that in these example the method calls are sitting by
themselves inside
a block is irrelevant--the rescue is associated with the call and not
with
the enclosing block.

Yes, I knew all that. I was trying to think of a semantic reason to
require wrapping exception handlers in a block inside a proc, when I
realized that a statement modifer gave some capability to do this as a
kind of degenerate case. The fact that you can have a block with a
single statement with a rescue modifier seems to weaken any argument
that you shouldn't be able to say

x.each do |a|
statement 1
statement 2
rescue
rescue-statement
end

when you can say

def meth(a)
statement-1
statement-2
rescue
rescue-statement
end

It seems to be a purely syntactic restriction, with no semantic necessity.

I just searched RCRchive, and there's an existing RCR336 advocating
allowing rescue's in do..end blocks without requiring begin..end
inside. It seems like a good change to me, so I just voted for it.

But then I could be wrong.
 

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,773
Messages
2,569,594
Members
45,118
Latest member
LatishaWhy
Top