ruby picture

RCR 314: concise expression debug/trace method

Submitted by eric_mahurin (Sun Sep 11 13:10:51 UTC 2005)

Abstract

Provide a Kernel method in the core or std libs to evaluate an expression, possibly print out as much info about the expression as possible, and return the expression result.

Problem

Kernel#p doesn't provide a very concise way of printing out debug info for an expression. It doesn't print file/line info (see related RCR 255), returns nil instead of the expression value (can't embed in an expression), and it doesn't condition whether this is being printed based on a debug mode/level.

Proposal

Provide a Kernel method that does this:

 1. takes an expression.  There are several options:
    a. as a block. i.e. {i+1}
    b. as an argument (caller evaluates). i.e. (i+1)
    c. as a string argument. i.e. ('i+1')
    d. as a string within a block. i.e. {'i+1'}
 2. evaluates the expression in the caller context
 3. if some debug/trace condition, prints out as much info as possible about the expression - file, line, expression string, expression result.
 4. returns the expression result

Analysis

Say you have this code:

  x = 1
  y = 2 + x/3

And you want to debug/trace x/3. Right now, you'd do something like this:

  x = 1
  y = 2 + (tmp=x/3)
  puts("#{__LINE__}: #{tmp.inspect}") if $DEBUG

With this proposal (using 1.a. option above and a method name of "d"), you'd have this:

  x = 1
  y = 2 + d{x/3}

Regarding the various expression options above, here are the disadvantages (nothing is perfect):

a. block: can't get the expession string and a little slower than a direct argument. Hopefully some mechanism will be put in place in a future ruby to get this from a proc/lambda/block. The main info missing to get this now is a token or column number (we have file and line number now).

b. argument: can't get the expession string. Although possible with a token or column number (in Kernel#caller) like the expression in a block, it is harder and getting a string from a proc/lambda/block may have another solution in the future.

c. string argument: very slow, expression highlighted as a string in text editors, and hard to get the binding of the caller to evaluate the expression. Florian has a solution for getting the caller binding, but it uses continuations and would be significantly slower. Also, the eval will be slow relative to a block yield (even with simple expressions I find it to be 20-30X).

d. string in a block: slow, expression highlighted as a string in text editors. The binding can be easily retrieved since the string is passed in a block, but the slowness of eval still reveals itself.

I recommend the 1.a. solution hoping for a robust Proc/lambda/block to code string method in the future.

Here are some related RCR's:

https://rcrchive.net/rcr/show/174 (legacy)

https://rcrchive.net/rcr/show/255

This RCR combines features from both but uses a different method name rather than changing Kernel#p.

Implementation

Here is an implementation using the 1.a. (expression as block) option. I'm allowing the debug condition to be passed in (defaults to $DEBUG), but there may be a better mechanism. A specifically formatted message gets printed to $stderr, but maybe it would be better to have more control over that too.

  module Kernel
    def d(debug=$DEBUG,&block)
      if debug
        result = yield
        $stderr.printf("DEBUG: %s #=> %s\n",
          caller[0],result.inspect)
        result
      else
        yield
      end
    end
  end
ruby picture
Comments Current voting
I like your overall idea, but how about 'trace' instead of 'd'. I'm not a fan of one-letter method names.


Strongly opposed 0
Opposed 0
Neutral 1
In favor 2
Strongly advocate 3
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .