Crackers2 has asked for the wisdom of the Perl Monks concerning the following question:

I have a class, and I want to be able supply it with a debug sub for printing debug messages, on a per-object basis.

My first implementation looks something like this:

package test; use strict; use warnings; sub new { my ($class, %params) = @_; my $self = { debug => sub {}, %params }; bless $self, $class; return $self; } sub method1 { my ($self) = @_; $self->{'debug'}->("Entering method1"); } package main; use strict; use warnings; my $t1 = test->new( debug => sub { print STDERR "debug: @_\n"; } ); my $t2 = test->new( debug => sub { print STDERR "DEBUG: @_\n"; } ); $t1->method1; $t2->method1;

This works fine, but I'm rather lazy and would prefer to avoid the long $self->{'debug'}->("text") invocation for each debug message. I'd prefer to be able to just say debug("text") and have it DWIM.

So I changed the code to this:

package test; use strict; use warnings; our $self; sub debug { $self->{'debug'}->(@_); } sub new { my ($class, %params) = @_; my $self = { debug => sub {}, %params }; bless $self, $class; return $self; } sub method1 { local ($self) = @_; debug "Entering method1"; } package main; use strict; use warnings; my $t1 = test->new( debug => sub { print STDERR "debug: @_\n"; } ); my $t2 = test->new( debug => sub { print STDERR "DEBUG: @_\n"; } ); $t1->method1; $t2->method1;

Which seems to do the trick.

So now I have two questions:

  1. What are the downsides to doing it this way? One obvious issue is that the passed subroutine can now access and modify $self. I suspect there may be some issues once inheritance comes into play, but I haven't done any testing on that yet.
  2. Is there a better way to do this? (or should I just forget about the whole thing and use the long invocation)

Replies are listed 'Best First'.
Re: Short invocation for per-object methods
by perrin (Chancellor) on Jan 04, 2007 at 05:33 UTC

    You did all this to avoid typing a couple of arrows? I think your lazy-meter is going in the wrong direction here.

    In short, putting an object into a global variable is only a good idea if you want a singleton. Also, use of local() is kind of a last resort these days. I use it in a couple of situations where it's dramatically easier than the alternative, but there aren't many of those. It's likely to trip you up at some point.

    You could easilly simplify to $self->debug($text) with no risk, just by making a debug() method that calls $self->{'debug'}. If you really want the debug code ref to differ by object, that's as good as you're likely to get without doing risky things.

      You did all this to avoid typing a couple of arrows? I think your lazy-meter is going in the wrong direction here.

      Well, it's not the arrows that bother me, just the number of keystrokes. Especially during testing I tend to have a debug call almost every other line, so they do add up.

      But you're probably right that convenience now is not worth the extra maintenance headaches my convoluted solution would cause.

      In short, putting an object into a global variable is only a good idea if you want a singleton.

      The way I did it I don't think the global scope of that global variable will ever contain an object, so I wasn't too worried about that part

      Also, use of local() is kind of a last resort these days. I use it in a couple of situations where it's dramatically easier than the alternative, but there aren't many of those. It's likely to trip you up at some point.

      Using my obviously wouldn't work since debug wouldn't be able to get to it, and I think just omitting the local would cause re-entrancy issues. So local seemed like the only solution.

      You could easilly simplify to $self->debug($text) with no risk, just by making a debug() method that calls $self->{'debug'}. If you really want the debug code ref to differ by object, that's as good as you're likely to get without doing risky things.

      I thought of doing that but didn't think it went far enough.

      Based on your reply, I think I'll settle on using $self->debug() because of the extra flexibility over the $self->{'debug'}->() call, and just adding a shortcut to my editor to paste it into the code.

      Thanks for knocking some sense into me :)

        The way I did it I don't think the global scope of that global variable will ever contain an object, so I wasn't too worried about that part

        You know, I think you're right about that, which makes me think this never would have worked anyway. The $self referred to by your debug method is the our $self, which never has anything in except in a local scope.

        Using my obviously wouldn't work since debug wouldn't be able to get to it, and I think just omitting the local would cause re-entrancy issues. So local seemed like the only solution.

        That's kind of what I was getting at: when local is the answer, it usually means you're asking the wrong question. It is very useful in a couple of specific cases though, so I can't say it shouldn't ever be used.

Re: Short invocation for per-object methods
by ikegami (Patriarch) on Jan 04, 2007 at 06:40 UTC

    Is $self->debug(...); sufficiently worse than debug ...; to warrant this?

    If you do go ahead with this, at least limit the scope of $self.

    sub debug { our $self; # Must be set by caller! $self->{'debug'}->(@_); } sub method1 { (local our $self) = @_; debug "Entering method1"; }

    (If there are args: (local our $self, my $arg1, my $arg2) = @_;.)

      Is $self->debug(...); sufficiently worse than debug ...; to warrant this?

      I'd vastly prefer debug ....

      If you do go ahead with this, at least limit the scope of $self.

      That looks cleaner all right.

      But based on both perrin and your answers I'll just go with $self->debug() after all.

Re: Short invocation for per-object methods
by osunderdog (Deacon) on Jan 04, 2007 at 12:52 UTC

    If you're looking for pure lazyness, I would recommend using the Log::Log4perl. Rather than rolling your own. Log::Log4perl has been pretty well tested and would allow you to change the destination of your traces with a configuration change rather than a code change.

    Hazah! I'm Employed!

Re: Short invocation for per-object methods
by tinita (Parson) on Jan 04, 2007 at 11:03 UTC
    but I'm rather lazy and would prefer to avoid the long $self->{'debug'}->("text") invocation for each debug message. I'd prefer to be able to just say debug("text")
    i don't know about your editor, but mine (vim) and others like emacs let you define macros or aliases.
Re: Short invocation for per-object methods
by pajout (Curate) on Jan 04, 2007 at 13:02 UTC
    TIMTOWDI, of course, but from my point of view it is not very clear to have object oriented and "non-object" subroutines in the module. I am lazy too, but I prefer $self->debug() in this case, because potential problems related with unclean architecture could occur.