Peter's Blog

Redefining the Impossible

Items filed under readthesmallprint


I was trying to use blocks in ruby and had code of this form:

   1  
   2  def wangle
   3      while true
   4          nValue = rand(10)
   5          if yield( nValue)
   6              return
   7          end
   8          print "THIS IS NEVER PRINTED"
   9      end
  10  end
  11  
  12  def wibble
  13      wangle do |nValue|
  14          return nValue == rand(10)
  15      end
  16  end
  17  
  18  wibble()

The method 'wibble' calls 'wangle' passing it a block of code to execute. Wangle is supposed to keep calling this block until two random numbers match. if the numbers match it will return true and cause the loop to terminate. However, the loop will always terminate immediately, the string "THIS IS NEVER PRINTED" is never printed.

The reason for this is blindingly obvious in hindsight and after analysing the small print describing the yield command. This says "the value of the last expression evaluated in the block is passed back to the method as the value of the yield". Notice how it doesn't mention the 'return' keyword? This is because the return keyword is being applied to the outer method 'wibble' and that is returning to whatever called it, completely bypassing 'wangle' on it's way up the call tree. If 'wibble' is changed to:

def wibble
    wangle do |nValue|
        nValue == rand(10)
    end
end

then it will pass the results of the comparison "nValue == rand(10)" back to wangle to deal with as I originally intended.

I can imagine that this subtlety was permitted as there may be occasions when you want wibble to be able to return directly to it's caller. In my ruby noobyness I'm getting blocks mixed up with methods. Unfortunately I like using the return keyword, it makes code easier to skim read, it waves a big flag to me that says THIS IS THE RETURN VALUE.


Filed under: noob readthesmallprint ruby