In Ruby, everything is an expression. It's a big part of the design and use of the language:
Rubyists aren't afraid to occasionally use the result of an if or case expression, a simple example:
x = if foo.nil? then BarClass.new else foo end y = case foo when nil then BarClass.new else foo end
One of the alluring properties of Ruby to me was that it strove for purity in many areas,
including the OO model (with annoying impurities such as no singletons for Fixnums notwithstanding),
and this idea that everything was an expression. A class creation was an expression! A def creating
a method was even an expression, even though it resembles a statement in both appearance (having a body with no do)
and semantically (it is a closed scope: no closure over variables in enclosing scope). Such simplicity
is tempting.
What I am slowly learning as I design my module for Laser that
builds the control-flow graph for a block of Ruby code, is that Ruby actually has statements, but
pretends it doesn't! Consider that there are a few things you do in Ruby code that don't have values
and we only ever realistically use them as statements already. These are
the control flow demons: break, next, redo, retry.
First of all, each one of those results in a jump. Even if we were to assign a value to break 3, that
value can never be consumed as there is no continuation!
self.foo(redo)
does not make much sense, does it? Try punching that into irb, and you get something you likely
haven't seen before:
>> self.foo(redo) SyntaxError: (irb):3: void value expression
There are many, many places in Ruby where expressions can go that a call to next truly does not belong, and
whenever Ruby encounters a request for the value of one of these calls, it will throw up the void value
expression Error. Keep in mind that this is a SyntaxError - it fails to parse entirely. Running Ripper.sexp('self.foo(redo)') also causes the error! The use of redo for its expression value however is an actual parsing error. Just like a statement.
It turns out Ruby 1.9.2 is going to a lot of efforts to catch some errors at the parser level, such as this code:
class A def foo next end end
Not a syntax violation in 1.8.x, but on 1.9.2, it throws a SyntaxError for using a next in a clearly incorrect place.
Anyway, back to statements: There are times when we don't ask for a jump's value, right?
x = 14 while x > 10 || x == 0 || (if x == 10; p x; x = 0; next; end; x == 10) p x x -= 1 end
There we've got a next in the condition of the loop restarting the loop at the condition again! And that
code does not give a void value expression error, because the value of the if expression is never needed.
So we've got pseudo-statements that are also allowed in expressions as long as you don't try to take its value. What good is something as an expression if it has no value? Moreover, statement expressions mean can still use them in very crazy places as a result. No statementful language would allow control flow in a loop condition!
At any rate, as usual, Laser will be issuing warnings for certain places where these critters do not belong.

Enjoy this article? Then feel free to: