Hello all,
I am new to Ruby and am toying around with the language to get a
feel for the expression ubiquity in ruby. Anyway, explain to me the
following. The below bits of code have no real relevance; they are
merely illustrative of my question. The first declaration works as
expected; the return value is 9. The second declaration doesn’t run
at all. I get a “void value expression” error. Is the runtime
looking into my if statement, realizing that there is no ability for
x to get set and failing? Obviously the code is ridiculous, but it
seems odd to me that it would not even run. Why does the interpreter
care that x will never get set if the function will return cleanly
none the less?
def foo()
x = 0
x = if 3 > 4
8
else
9
end
return x
end
def foo()
x = 0
x = if 3 > 4
return 8
else
return 9
end
return x
end
Others have commented this already. Just another bit: once you think
about
semantics of “return” for a moment it should be immediately clear that
it is
not and cannot be an expression simply because it never returns control
flow
but instead transfers it to someplace else. The same holds true for
“next”.
Interestingly enough “raise” does not provoke such a compile error
although
I cannot see how it would ever return control flow locally:
I guess that is what surprises me. I would expect return to work
exactly like raise. It just seems strange to me that I can have a
perfectly good expression such as
if (3 < 4)
return 5
end
and then have that fail when I do
x = if (3 < 4)
return 5
end
Someone else gave the example of simplifying my question down to the
equivalent of trying the following
x = return 9
Why shouldn’t this be valid? As shown above, raise seems to return
nil, and return seems like it should return a similar value if there
is no other reasonable return value. Just for my own curiosity, what
implications would returning “nil” have? Anyway, thanks for all your
help. I realize most of this is of academic value, but it really
helps to see how Ruby works underneath.
Hmmm. But raise does actually break out of the current frame (and the
whole main loop) if it isn’t rescued…mabye raise, since it already
enters to exception handler, shortcircuts the void assignment
exception; or mabye it completes the assignment with nil, then exists
the stack? Like I said, I’m just guessing here…I have no idea what
really happens under the hood.
helps to see how Ruby works underneath.
Hi Patrick,
I’ve no idea what goes on under the hood, but I would think it is
because raise doesn’t exit the current stack frame (it can be rescued
and execution continues), so it’s more like a method that gets
evaluated and its return (nil) then gets assigned (nb: right hand side
of assignment is always resolved before it is assigned). But return
(and I assume break / next) break out of the current stack frame, so
the assignment is left dangling. That’s my guess.
On Mon, Sep 25, 2006 at 05:10:18AM +0900, Robert K. wrote:
Regardless of whether or not it makes logical sense to say a = return b,
I think the confusion is that
x = raise b is not a syntax error while
x = return b is a syntax error.
Frankly, I think this is probably either a) an oversight in the grammar
or b) the need to handle the super weird edge case of
a = raise ‘error’ rescue nil
Either a = raise b should be a syntax error, or a = return b should not.
I’d lean towards the former, if it were up to me.
No, “raise” does neither return nil nor anything else. It does not
return in the same way as “return” never returns. The whole point of
the two is that they do not behave like an expression but transfer
control flow up the call stack.
Nevertheless I think it’s significant that raise is a method
whereas return (of course) is not.
I wonder if there is some case where raise would/does return
a meaningful value somehow? My gut reaction is ‘no’… but
this is Ruby…
I guess that is what surprises me. I would expect return to work
exactly like raise.
I on the other hand expect “raise” to work exactly like “return”.
Someone else gave the example of simplifying my question down to the
equivalent of trying the following
x = return 9
Why shouldn’t this be valid?
Because “return” never returns. (Uh, this starts sounding like Zen…)
As shown above, raise seems to return nil,
No, “raise” does neither return nil nor anything else. It does not
return in the same way as “return” never returns. The whole point of
the two is that they do not behave like an expression but transfer
control flow up the call stack.
and return seems like it should return a similar value if there is no
other reasonable return value. Just for my own curiosity, what
implications would returning “nil” have?
It does not work. Please take the time and meditate about this.
Anyway, thanks for all your
help. I realize most of this is of academic value, but it really helps
to see how Ruby works underneath.
In this case I’d rather say, you hit behavior that is common to all
programming languages that know “return”. But I get the feeling that
you still not fully understood the difference between an arbitrary
expression and a return statement…
No, “raise” does neither return nil nor anything else. It does not return
in the same way as “return” never returns. The whole point of the two is
that they do not behave like an expression but transfer control flow up
the call stack.
Nevertheless I think it’s significant that raise is a method
whereas return (of course) is not.
I think so too. I would not want to see a method call of any kind be
dealt with as a syntax error just because it has an assignment to its
left. Since return can’t really be a method (at least my brain can’t
wrap itself around that concept I don’t think that the two of them
need to be thought of together with regard to the syntax-error matter.
I wonder if there is some case where raise would/does return
a meaningful value somehow? My gut reaction is ‘no’… but
this is Ruby…
I am fairly certain that the return value can never be captured. At
least I can’t figure out how to do it.
In this:
def raise_value
x = raise
rescue
x
end
p raise_value # nil
I’m pretty sure that x is nil because of the thing where the parser
sees an assignment and defines the variable. I don’t think an
assignment ever actually takes place – as witness the fact that:
assignment ever actually takes place – as witness the fact that:
in fact have a value.
Even more trivial:
x = raise rescue 1
But I’d still say that the return value of the call to raise is
not what’s being captured in x.
Well, from a syntax point of view, it sure LOOKs like the rvalue of
the assignment is the expression “raise rescue 1”
It’s an interested corner of the language. Kernel#raise is a method
so it should have a value, even though unless it’s rescued it causes a
non-local return.
In this:
sees an assignment and defines the variable. I don’t think an
Of course this is a trivial example, but the point is that raise can
the assignment is the expression “raise rescue 1”
It is; that expression evaluates to 1, and that’s what x gets. But I
don’t think the call to raise is, itself, returning 1.
Regardless of what happens under the covers with setjmp/longjmp, I
can’t see anyway to explain the results syntactically other than
saying that the ‘raise … rescue …’ is an rvalue in the assigment.
The genesis of this thread was why the ruby compiler disallows:
x = return y
but allows
x = raise rescue 1
And some have expressed the opinion that both should be disallowed.
I can’t agree. I think that the proper documentation of
Kernel#raise/Thread#raise should be that it causes a non-local return
unless it is rescued, in which case the value is the value of the
expression in the rescue. That’s how it works now.