Riddle me this (a question about expressions)

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

Patrick T. wrote:

def foo()
x = 0
x = if 3 > 4
return 8
else
return 9
end
return x
end

The problem is that you are effectively trying to do this
x = return 9
And that doesn’t have a well defined meaning

Fred

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:

RUBY_VERSION
=> “1.8.3”

def foo() x = raise “Test” end
=> nil

RUBY_VERSION
=> “1.8.5”

def foo() x = raise “Test” end
=> nil

Kind regards

robert

Robert K. schrieb:

Interestingly enough “raise” does not provoke such a compile error
although I cannot see how it would ever return control flow locally:

def raise *args
“my raise”
end

x = raise NameError
p x # => “my raise”

Don’t ask me why it is implemented like this, though.

Regards,
Pit

On Sep 24, 2006, at 5:45 AM, Robert K. wrote:

=> “1.8.3”
robert

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.

Patrick

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.

Regards,
Jordan

Patrick T. wrote:

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.

Regards,
Jordan

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…

Hal

Patrick T. wrote:

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”. :slight_smile:

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…

Kind regards

robert

On Sep 24, 2006, at 4:50 PM, [email protected] wrote:

On Mon, 25 Sep 2006, Hal F. wrote:

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.

Well, since it is a method…

module Kernel
def raise
‘not what you expected’
end
end

x = raise
p x

Maybe that is cheating, but as long as raise is a method there is the
possibility that it might return a value.

Gary W.

Hi –

On Mon, 25 Sep 2006, Hal F. wrote:

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 :slight_smile: 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:

x = raise || 1

also leaves x as nil.

David

On Mon, Sep 25, 2006 at 12:34:27PM +0900, Rick DeNatale wrote:

I think so too. I would not want to see a method call of any kind be
least I can’t figure out how to do it.

x = raise rescue nil || 1

Of course this is a trivial example, but the point is that raise can
in fact have a value.

def x
fail “Exception”
end

x = return x() rescue nil

Just muddying the waters.

On 9/24/06, [email protected] [email protected] wrote:

Hi –

On Mon, 25 Sep 2006, Hal F. wrote:

I wonder if there is some case where raise would/does return
rescue

also leaves x as nil.

But try:

x = raise rescue nil || 1

Of course this is a trivial example, but the point is that raise can
in fact have a value.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

:slight_smile: But I’d still say that the return value of the call to raise is
not what’s being captured in x.

Of course not :slight_smile: viz.

(x = raise) rescue 1; x #=> nil
x = (raise rescue 1); x #=> 1

and indeed it’s more trivial than the case at the top of this post, I’m
not
going to enumerate all possibilities of that :wink:

Bye,
Kero.

Hi –

On Mon, 25 Sep 2006, Rick DeNatale wrote:

x

also leaves x as nil.

But try:

x = raise rescue nil || 1

Of course this is a trivial example, but the point is that raise can
in fact have a value.

Even more trivial:

x = raise rescue 1

:slight_smile: But I’d still say that the return value of the call to raise is
not what’s being captured in x.

David

On 9/25/06, Rick DeNatale [email protected] wrote:

It’s an interested corner of the language.

s/interested/interesting/

On 9/25/06, [email protected] [email protected] wrote:

assignment ever actually takes place – as witness the fact that:
in fact have a value.

Even more trivial:

x = raise rescue 1

:slight_smile: 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.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hi –

On Tue, 26 Sep 2006, Rick DeNatale wrote:

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.

David

On 9/25/06, [email protected] [email protected] wrote:

But try:
:slight_smile: But I’d still say that the return value of the call to raise is


David A. Black | [email protected]
Author of “Ruby for Rails” [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB’s Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

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.

Of course the final arbiter is Matz.