Eavesdropping on Expressions

Update: Slightly more complete examples.

I found a nice little technique for debugging Ruby code today.

Ever had a situation where you wanted to insert some debugging code in the middle of an expression? The usual way is to break up the expression and use intermediate variables to get at the value, but it turns out that’s really not necessary in Ruby.

Check this out:

class Object
  def tap
    yield self
    self
  end
end

Then, you can insert your debugging tap just about anywhere without disturbing the flow of data. Let’s look at some common cases.

First, let’s look a “pipeline” of sorts.

blah.sort.grep( /foo/ ).map { |x| x.blah }

Let’s imagine that there’s a bug here somewhere — we’ve verified that x.blah does the right thing, but the values coming from upstream are suspect. Here’s a “traditional” way of modifying this to add a debugging print:

xs = blah.sort.grep( /foo/ )
p xs

# do whatever we had been doing with the original expression
xs.map { |x| x.blah }

With Object#tap, this becomes much easier — you can just slip it in without radically modifying the code:

blah.sort.grep( /foo/ ).tap { |xs| p xs }.map { |x| x.blah }

Similarly, let’s say we’re suspicious of a component, ( q - t ), in an arithmetic expression:

( k + 1 ) / ( ( q - t ) / 2 )

The traditional approach:

i = ( q - t )
p i
( k + 1 ) / ( i / 2 )

Admittedly, it may be wise to break long arithmetic expressions up like this for comprehensibility anyway.

Regardless, here’s how you could do the same thing using Object#tap:

( k + 1 ) / ( ( q - t ).tap { |i| p i } / 2 )

Object#tap is also useful when you’re directly using the result of an expression as the result of a method. For example:

def blah
  @things.map { |x|
    x.length
  }.inject( 0 ) { |a, b|
    a + b
  }
end

The traditional way:

def blah
  sum = @things.map { |x|
    x.length
  }.inject( 0 ) { |a, b|
    a + b
  }
  p sum
  sum
end

The Object#tap way:

def blah
  @things.map { |x|
    x.length
  }.inject( 0 ) { |a, b|
    a + b
  }.tap { |sum| p sum }
end

If you think about it, Object#tap is a bit like tee in Unix, really. Ever used tee while debugging a shell pipeline, to make sure that the intermediate results were sane? Same thing.

Anyone else discovered this trick?