• Random
  • Archive
  • RSS
  • Ask me anything

Ryan Angilly

A business guy who became a really good software developer first by accident

Ruby statement modifers behave differently than conditional statements

If you’re a Ruby guy or gal, you know about statement modifiers. They are lovely little bits of syntax that let us do stuff like:

raise "You're an idiot" if params.nil?
call_some_awesome_method unless it_was_already_called

Which, we’ve all been told, is the exact same thing as doing:

if params.nil?
  raise "You're an idiot"
end

unless it_was_already_called
  call_some_awesome_method
end

Well, as it turns out, this isn’t true. Statement modifiers in Ruby behave differently than their conditional brethren. Something that caused me quite a bit of pain a couple weeks ago. Take the following bit of code. We’ll go through it line by line.

ra:~$ irb
irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
  from (irb):1
irb(main):002:0> defined? a
=> nil
irb(main):003:0> a
NameError: undefined local variable or method `a' for main:Object
  from (irb):3
irb(main):004:0> a = 5 unless defined? a
=> nil
irb(main):005:0> a
=> nil
irb(main):006:0> defined? a
=> "local-variable"
irb(main):007:0> 
  1. crack open an irb console and try to access the variable a. It hasn’t been defined yet, so irb appropriately complains.
  2. use the defined? keyword to check what the interpreter currently thinks of that variable. nil is returned. The interpreter has never seen it. Undefined as expected.
  3. lets check a again to see if calling defined? on it did antything. Nope. Still undefined. So far so good.
  4. Now, I write some code that I think means “Hey Ruby, if you have never seen a before, then set it equal to 5.
  5. … a is nil… wtf?
  6. ?

Thought I knew Ruby. Let’s rewrite this using conditionals instead of modifiers.

irb(main):007:0> b
NameError: undefined local variable or method `b' for main:Object
  from (irb):7
irb(main):008:0> defined? b
=> nil
irb(main):009:0> unless defined? b
irb(main):010:1>   b = 5
irb(main):011:1> end
=> 5
irb(main):012:0> b
=> 5
irb(main):013:0> defined? b
=> "local-variable"
irb(main):014:0> 

Which is what you’d expect. So what’s going on here? My first thought was that maybe there was an operator binding precedence thing going on. Like maybe it was evaluating: a = (5 unless defined?(a)). But even that should assign 5 to a. Then I tried (a = 5) unless defined?(a). No luck. Same behavior. a gets assigned nil.

I think the explanation goes something like this: In the first example, the interpreter sees a and realizes right away that it’s an lvalue. As such, it adds a to the local variable list. Then, it continues on and sees an assignment operation (=), an expression (5) and a statement modifier (unless defined? a). It delays evaluating the expression and executing the assignment because of the statement modifier. Once it evaluates that modifier, it decides to not execute the expression or assignment.

So I think about this for a while wondering how I could test the theory and it dawns on me that there is a much simpler way to illustrate this problem:

irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
	from (irb):1
irb(main):002:0> a = 5 unless (res = defined?(a))
=> nil
irb(main):003:0> a
=> nil
irb(main):004:0> res
=> "local-variable"
irb(main):005:0> 

I’m not sure if I’m missing something super obvious, something super obscure, or if this is a bug in Ruby. But I know that res should be nil and a == 5.

Just something to keep in mind down the road. Statement modifiers behave differently than conditional statements.

    • #Nerdalert
    • #ruby
    • #lies
  • 2 years ago
  • 1
  • Comments
  • Permalink
  • Share
    Tweet

1 Notes/ Hide

  1. angilly posted this

Recent comments

Blog comments powered by Disqus
← Previous • Next →

Portrait/Logo

About

Hi, I'm Ryan, and I build stuff on the internet. I'm currently building Signal Genius.

I blog about my failed startup, MessageSling, at The Day Series.

Things I used to do:

  • Built and launched FourthSegment
  • Hacked at Punchbowl.com.
  • Founded MessageSling.com.
  • Spent several years at EMC

Me, Elsewhere

  • @angilly on Twitter
  • Facebook Profile
  • angilly on Flickr
  • angilly on Foursquare
  • My Skype Info
  • ryana on github

Twitter

loading tweets…

Following

I Dig These Posts

  • Photo via tmills

    A serious bath-taking bear.

    [via]

    Photo via tmills
  • Photo via tmills

    I read this thing on Vice tonight about how girls hate girls even when they’re friends and while all things women are forever hermetically sealed...

    Photo via tmills
  • Link via graysky
    cdixon.org – chris dixon's blog / Best practices for raising a VC round

    (via Instapaper)

    Link via graysky
  • Photo via dancroak

    Puppy.

    Photo via dancroak
See more →
  • RSS
  • Random
  • Archive
  • Ask me anything
  • Mobile

Effector Theme by Carlo Franco.

Powered by Tumblr