in reply to Seeking advice for OO-related strategy

An alternate design is to derive from Foo::Parse.

package Foo::Parse; #... sub parse { my $self = shift; if (my $func = $self->can('action')) { $self->$func(@_); } } #... package Foo::Parse::Splitter use base 'Foo::Parse'; sub action { my $self = shift; # ... } package main; my $fp = Foo::Parse::Splitter->new('FooParser'); $fp->parse($_) while <>;
This is the way I normally do it - but I do use callbacks sometimes, and the code you have above seems fine to me, albeit quite simplified. Note that $self in the callback doesn't make much sense - it's a Foo::Parse object, and the callback is not a method of Foo::Parse. Better to name it $obj or $fp. In fact, if you split it a bit, you don't need to pass back the object at all:
my $fp; $fp = Foo::Parse->new('FooParser', sub { local $_ = shift; s/^\w+:// or $fp->warn("something wrong!"); split /,/; } );
This makes it into a closure, meaning that $fp remains visible. Even if $fp were itself no longer in scope, this closure can still see it. This is even more normal to me.

Replies are listed 'Best First'.
Re^2: Seeking advice for OO-related strategy
by Rhandom (Curate) on Mar 03, 2005 at 18:08 UTC
    Note that $self in the callback doesn't make much sense

    Well - yes and no. Without the $self certainly works as you have it, but it is much easier to use $self. $self in this case is simply the topic of discussion - the guy who called the method - even if he isn't the one who owns the method. Using the callback as a method also allows the following to work without the predeclared "my $fp".

    my $fp = Foo::Parse->new('Fooparser', \&Some::Other::Class::Method);

    You can call the Some::Other::Class::Method with any object that "does" everything that Method requires. This will be easier to validate for in Perl 6 as even the anonymous method can have a signature that can make sure that whatever gets passed to it has the appropriate capabilities.

    my @a=qw(random brilliant braindead); print $a[rand(@a)];
Re^2: Seeking advice for OO-related strategy
by blazar (Canon) on Mar 04, 2005 at 08:02 UTC
    An alternate design is to derive from Foo::Parse.
    Interesting approach, but I was specifically interested in callbacks in this case. And -FWIW- in my real world program it's not to be a single sub, but a dispatch table...
    Note that $self in the callback doesn't make much sense - it's a Foo::Parse object, and the callback is not a method of Foo::Parse. Better to name it $obj or $fp.
    Very good point, in fact I've been lucubrating for a while as for what a good name for what I eventually called $self may have been. Incidentally in my real world program it will be a more descriptive name, like $log, so no problem here. As far as generic use is concerned, $obj is fine IMHO.
    my $fp; $fp = Foo::Parse->new('FooParser', sub { local $_ = shift; s/^\w+:// or $fp->warn("something wrong!"); split /,/; } );
    This makes it into a closure, meaning that $fp remains visible. Even if $fp were itself no longer in scope, this closure can still see it. This is even more normal to me.
    Very good point too! I like closures, I just hadn't thought of this possibility...

      I realised you were looking for callbacks, but given the number of questions here that are of the form "I want to do X, here's my solution for Y - what's wrong?", I like to give MTOWTDI - just incase what you really want is another WTDI.

      Now, on to that last code snippet you quoted. I often mix my inheritance with callbacks. For example, the code I posted in Re: String expansion script is in one of my base objects from which many other object types are derived. In general usage, I call it like this:

      my @strings = $self->expand_string($foo);
      In this module, I'll have my very own expand_variable function which expands the way I want (and usually it defers to $self->SUPER::expand_variable if it can't figure it out). However, in certain cases, I want access to variables that I don't want generic access to - or, perhaps, I can't have generic access to. So I put it my own closure:
      my @strings = $self->expand_string($foo, sub { /^VAR1$/ && return $blah; /^VAR2$/ && return @some_list; $self->expand_variable() } );
      This works pretty much everywhere. $blah, @some_list, and $self are all scoped variables for the closure. And even if the current type doesn't have an expand_variable, that's ok since it is derived off the base with default variable handling.

      So, from here, there's another minor point I would make with the code you started with: rather than passing in the variable, is it unreasonable to use $_? You may notice my expand_string code does that with do {local $_ = $var; $self->$func()}. I'm not sure which is faster, localising a global, or passing in a parameter. However, that's not why I'm doing it. I'm doing it because it allows your closure to be smaller because so many of perl's functions assume $_ when not given anything else - exactly why you were using "local $_ = shift" in the first place. I'm just suggesting generalising it so that all closures can gain from the behaviour.

        I realised you were looking for callbacks, but given the number of questions here that are of the form "I want to do X, here's my solution for Y - what's wrong?"
        Indeed, "X-Y", it's called...

        Actually it would have been more cleaner if I had called them callbacks from start, which I didn't for it simply didn't occur to me it was the right term, even though of course I knew about it having read it e.g. in Tk's docs.

        However, in certain cases, I want access to variables that I don't want generic access to - or, perhaps, I can't have generic access to. So I put it my own closure:
        my @strings = $self->expand_string($foo, sub { /^VAR1$/ && return $blah; /^VAR2$/ && return @some_list; $self->expand_variable() } );
        Another interesting technique: seems obvious now that you point it out, but just like most obvious things one can easily overlook it...
        So, from here, there's another minor point I would make with the code you started with: rather than passing in the variable, is it unreasonable to use $_? You may notice my expand_string code does that with do {local $_ = $var; $self->$func()}.
        Yes and no: although I try to take advantage of Perl's topicalization as often as possible in conjunction with builtins, but for some reason I am not so keen on doing so with subs I write myself; however I have no argument against doing so, I simply generally don't feel like following this approach systematically...