http://qs321.pair.com?node_id=607530

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

In Net::IMAP and others, a module will pass you data by requiring that you pass a subroutine reference that the module will execute when the time comes to pass back the data.

Then I have to use uncomfortable globals to get the data out of that sub and it's harder to debug because I don't know what's calling my sub and what my sub returns.

Am I missing the beauty of callbacks? What are they for? Why would someone use them instead of just returning a hash ref or something like that?

Replies are listed 'Best First'.
Re: Why callbacks?
by Fletch (Bishop) on Mar 30, 2007 at 17:10 UTC

    No need for globals, just have the coderef use lexicals in the scope it's declared in (i.e. use a closure).

    { my $returned_data = [ ]; sub my_fetch_callback { push @{ $returned_data }, $_[0] } routine_wanting_callback( \&my_fetch_callback ); for my $datum ( @{ $returned_data } ) { munge_it( $datum ); } }

    Update: Moved bracket after for so it would, you know, actually work. Duurr. Stupid trees making pollen affecting sinuses with their pollen making brane numbing congestion stuff.

    Another example: You could also pass a coderef which stores the results by calling methods on an object you provide.

    my $line_holder = Line::Holding->new( ); routine_wanting_callback( sub { $line_holder->hold( $_[0] ) } ); $line_holder->process_held_lines;

    Nothing says the callback has to call a named subroutine sitting in the symbol table (unless the callback wanting routine says it takes the name of a subroutine of course; but that'd just be bad design :).

      Woah... doesn't my_fetch_callback go out of scope before you can call it?

        Named subroutines never go out of scope; they're global - bound to a package just like global variables.

        Anonymous subrefs can go out of scope, just like any other variable, but, just like any other variable, you can pass them around:

        sub call { my ($arg1,$arg2) = @_; my $callback = sub { my ($arg3) = @_; return $arg1 + $arg2 + $arg3; }; call_func_needing_callback($callback); } # or even sub call { my ($arg1,$arg2) = @_; call_func_needing_callback( sub { my ($arg3) = @_; return $arg1 + $arg2 + $arg3; }); }

        Also note that there are scoping issues with defining named closures within other subroutines (this probably has to do with named subroutines being global), while unnamed subroutines/closures will work as real lexical closures.

        I presume this was asked before I moved the brace, but even then no. The scalar would have (which is why I made the correction), but the subroutine sticks around (they're not lexically declared). Had I done this:

        { my $returned_data = []; my $callback = sub { push @{ $returned_data }, $_[0] }; } something_wanting_callback( $callback );

        Then yes, that coderef would have gone out of scope.

Re: Why callbacks?
by ysth (Canon) on Mar 30, 2007 at 17:29 UTC
    It's part of a different way of thinking. Consider reading Higher-Order Perl, and, as you go through it, think about how things that use callbacks might work differently or not work at all without callbacks.

      Yup. Whereas the normal calling sequence for an API is "Don't call us, we'll call you", using callbacks is more "If something interesting happens, call me at this number (coderef)". One good way to get your head into the callback-driven mindset is to use one of the GUI toolkits which are pretty much all event driven. When something interesting happens (the user hits the mouse button; the environment needs part of a window redrawn) the framework needs a chunk of code (the callback) which it calls into in order to handle the event.

        ++!

        Okay, that makes sense! Events. Ah...

        And it suddenly reminds me of where I've seen callbacks that didn't annoy me: signal handlers.

        Would I be going out on a limb if I said the use of callbacks in Net::IMAP is a little .. er.. funny?

        For those of you who haven't had the (dis)pleasure of working with that particular module, if you want to know the size of an email message, you send a query to the IMAP server and get the results via callback.

      Can you elaborate a little? I'll try and check that book out. Can you give a tiny example or something?

Re: Why callbacks?
by Joost (Canon) on Mar 30, 2007 at 17:51 UTC
    Am I missing the beauty of callbacks? What are they for? Why would someone use them instead of just returning a hash ref or something like that?
    A simple example of the beaty of callbacks is the sort function: sort() can sort a list of any kind of data as long as the elements in the list can be compared to each other as being "less", "the same" or "more":
    sub revcmp { # note that for brevity sort() does not use the normal way # of passing variables to its callback, but sets # global variables $a and $b reverse($a) cmp reverse($b); } my @strange = sort \&revcmp @list;
    You cannot really do this any other way without either losing flexibility in the sort order or having to write your own sorting algorithm.

    You can also view map {}, grep {}, foreach {} and most other looping constructs as being using callbacks, except that you can't directly pass in another subroutine, you need to specify a literal code block or expression. Perl just treats these specially. For example in Ruby and newish JavaScript versions, map, grep/filter and foreach ARE just functions accepting callbacks.

Re: Why callbacks?
by philcrow (Priest) on Mar 30, 2007 at 17:52 UTC
    Consider the alternative. You need to supply an action for the helper module to trigger at the right moment. The alternative to a code ref callback is to construct an object which responds to a particular method name. This has gained you nothing but a class for producing an object for the one external user that needs it. And it cost you more than typing in a package definition and a blessing constructor. Now your helper knows the name of one of your methods.

    If you already had a handy object, coding up a new method for the foreigner to call seems great... Until you need the same helper to do two different things. Then you realize that both of them need a method and you can only name one. Now your method must do something to figure out which of the actions a particular callback needs, like a cascaded if or ternary. It's precisely this type of case testing object orientation was meant to cure.

    By using callbacks, the helper module is completely decoupled from the caller. It doesn't even know the name of one method in the caller. All it has is a code reference that meets a documented API (well, the API should be documented). That is strong decoupling indeed and one that OO snobs frowning on callbacks as a technique either don't understand or refuse to admit as a benefit.

    Phil

Re: Why callbacks?
by exussum0 (Vicar) on Mar 30, 2007 at 17:51 UTC
    Callbacks are great for injecting behaviour into something - ruby calls them mixins.

    Instead of having variables referenced within the function, outward, to something lexically scoped or globally is really weird. I would expect the callback retaining the data and you querying it for the result. Hrm..

        You can do mixins in Perl as well, by using Exporter to import methods into an OO class. CGI::Application uses this technique to add methods via plugins.
        Right right, was using the wrong terminology for what i meant. tnx.
        A reply falls below the community's threshold of quality. You may see it by logging in.
    A reply falls below the community's threshold of quality. You may see it by logging in.