I’ve touched on this gotcha briefly in the past when discussing the Wat video, but I thought a few examples of when single-line conditionals can bite you would be fun.
In Ruby, we can write a conditional containing a single expression that normally takes up three lines:
unless condition something end
on a single line to save space:
something unless condition
And that is all well and good - these two are pretty much the same. But they’re not identical in practice. There are a few weird things about to come up.
Our first example will be using
defined? to conditionally print
if defined?(var1) puts var1 # never runs end var1 # NameError: undefined local variable
So that works just as we’d expect. The conditional never runs because
nil. After the conditional, access the
var1 gives us a
NameError because (appropriately) its not
defined. Let’s modify that a little bit and put an assignment inside of the
if defined?(var2) var2 = 5 # never runs end var2 # nil defined?(var2) # "local-variable"
So that might look a bit odd. We never ran the conditional, so
gets set - that makes sense. But after the block,
var2 doesn’t throw a
NameError when accessed anymore. This is because the Ruby parser makes room
var2 when it sees it on the lefthand side of an expression (even
though its inside of a conditional that doesn’t run).
Let’s write the same on one line though:
var3 = 5 if defined?(var3) var3 # 5
Even more interesting - an undefined variable written this way will become
defined and assigned when run. The first thing that happens is that the parser
comes along and defines
var3 which it sees on the lefthand side
of a conditional. Then
defined? runs, which this time evaluates
"local-variable", causing the conditional to pass, and
5 to be assigned
var3. In cases like this, the single-line conditional will produce an
entirely different result than block conditionals.