http://qs1969.pair.com?node_id=1204168

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

Hi,

This isn't a problem so much as me trying to understand something, but I hope someone might be kind enough to help explain.

So, I have some code, which was sourced from elsewhere (it's from a Mojo example), which uses a callback, but I don't really quite get the idea of callbacks, and particularly I don't understand the syntax.

$ua->get($url => \&get_callback);

So, sure enough there is a subroutine called get_callback, but I don't really understand what's happening. I did find an O'Reilly doc on callbacks, but the syntax was different.

sub get_callback { my (undef, $tx) = @_; .... .... }

I don't really get the first line of the get_callback routine either, when nothing appears to be being passed across

If someone might explain in kiddie-steps what's happening, and why, then it would be much appreciated. Many thanks!

Replies are listed 'Best First'.
Re: Trying to Understand Callback(s)
by holli (Abbot) on Nov 23, 2017 at 22:33 UTC
    # just as you can have a variable that contains strings and numbers, # you can have a variable that contains subroutines. my $subroutine = sub { print "Hello\n" }; # you can call this subroutine like so: $subroutine->(); &$subroutine(); # you can also put a named subroutine into a variable sub hello { print "Hi there!\n"; } my $hello = \&hello; &$hello(); # if you use a variable that is lexically bound to the scope # the subroutine is defined in, you get what's called a closure my @subs; foreach my $adress ( "Sir", "Ma'am" ) { #each instance of the sub gets its own $adress push @subs, sub { print "Good day $adress\n" }; } $subs[0]->(); $subs[1]->(); # if you call a subroutine that returns a subroutine, # you have what's called a factory sub make_greeter { my $timeoday = shift; return sub { print "Good $timeoday\n" }; } my $greeter = make_greeter("morning"); $greeter->(); # if you pass a subrouine off to some other code, # which promises to CALL that subroutine BACK # at some later point in time / when some event happens, # you have what's called a callback. # Depending on the calling code, # the callback might or might not receive arguments. sub handle_get_response { my $response = shift; # ... } my $get_response_handler = \&handle_get_response; # When the request is done SomeWebClient will call $get_response_handl +er->(); SomeWebClient->get( url => $get_response_handler );


    holli

    You can lead your users to water, but alas, you cannot drown them.
      Hi holli,

      congratulations for these very nice explanations.

      You really achieve to show quite a lot, and fairly clearly in my opinion, in a relatively short post. I really wish I could upvote your post a few more times. ;-)

      Your post has definitely given me a much clearer idea. I really appreciate your time and effort in responding to me. I'll need to re-read the posts on here a few times more, but I'm very grateful for your reply, and the others of course too. Thank you very much!
Re: Trying to Understand Callback(s)
by ikegami (Patriarch) on Nov 23, 2017 at 22:53 UTC

    \&get_callback doesn't call get_callback; it returns a reference to it. This reference will be used by get to invoke the sub at a later time (e.g. when the request succeeds or fails). When get invokes it, it will pass parameters to it.

    sub get { my ($ua, $url, $cb) = @_; ... $cb->($foo, $tx); # Call the referenced sub. ... }
Re: Trying to Understand Callback(s)
by GrandFather (Saint) on Nov 23, 2017 at 23:22 UTC

    Callbacks are a way of getting some user supplied code run in a provider context. The "user" is your code. The "provider" is some fancy pants module or sub written by someone else (or by you being clever). I'm sure you are happy to pass parameters into a sub so the sub can do some work with them. Well, a callback is just some code (a reference to a sub) that you pass as a parameter so the called sub can do some work with it.

    Why would you want to do that? In the example you give (I'm guessing) you are providing a sub that will get called to handle web page that has been fetched. In other cases you might provide a callback to do some math on data points as part of an algorithm that searches for patterns in a data set. The search algorithm remains the same for different match conditions - you are passing in the bit that is tuned for a specific type of match.

    So the parameter you are passing is just a reference to a sub (that's the \& bit). That can be provided in a bunch of different ways and may often use closures to pass some of your calling context into the callback - that is deep magic indeed! Let's take a look:

    use strict; use warnings; my $helloWorld = sub{return "Hello world 1"}; process($helloWorld); process(sub{return "Hello world 2"}); process(\&helloWorld); process(sub{return $_}) for 1 .. 5; process(sub{mul10($_)}) for 1 .. 5; sub helloWorld { return "Hello World 3"; } sub mul10 { my ($x) = @_; return $x * 10; } sub process { my ($callback) = @_; print $callback->(), "\n"; }

    Prints:

    Hello world 1 Hello world 2 Hello World 3 1 2 3 4 5 10 20 30 40 50

    The process calls with (sub...) are all creating a sub on the fly. sub returns a reference to the subroutine. You can see that with $helloWorld. The \& variant calls a sub defined somewhere else. The last two with the for loop modifiers use closures to pass the default variable into the called subroutine. Closures are subtle and can do astounding things - in fact quite often what they do is astounding in a disconcerting way!

    As a matter of course I use a line of the form my ($p1, $p2, $p3, ...) = @_; to provide local variables containing the values of passed in parameters. @_ is the list of aliases to the parameters passed to the sub.  my (...) declares a list of variables that get assigned values from the @_ parameter list. You can use undef as a place holder for a parameter that you aren't interested in - i.e.: to ignore the parameter.

    Premature optimization is the root of all job security
Re: Trying to Understand Callback(s)
by shmem (Chancellor) on Nov 24, 2017 at 00:29 UTC
    ...but I don't really quite get the idea of callbacks, and particularly I don't understand the syntax.

    Well, it's not just quite like with a phone - you ring up somebody, they hang up and call you - but rather a way to parametrize a subroutines execution chain via a subroutine reference (like a function pointer). The called sub may have been compiled into any context, and the compilation rules of that context apply. And what ikegami said.

    I don't really get the first line of the get_callback routine either, when nothing appears to be being passed across

    It gets something passed across but discards it.

    A sub routine called as a method ($thingy->routine(@args)) by something ($thingy - irrelevant whether $thingy is a literal, a blessed reference, a scalar holding a string, or an expression which resolves to a package or a blessed reference) gets passed the invocant as its first parameter in @_.

    So, having this preamble

    sub routine { my($thingy, $tx) = @_; # $thingy is not used print "routine: $tx\n"; } my $ref = \&routine; # take a reference to the sub my $pkg = "main"; my $obj = bless do { \my $x }; # we could also say... # my $pkg = __PACKAGE__; # ...to get any package we are in in compile time

    the following call expressions are all equivalent:

    # method calls main->routine("foo"); main->$ref ("foo"); $pkg->routine("foo"); $pkg->$ref ("foo"); $obj->routine("foo"); $obj->$ref ("foo"); # or even (join'',map{chr}(109,97,105,110))->routine("foo"); __PACKAGE__->$ref("foo"); (bless \my $x)->$ref("foo"); # function calls routine ('main', "foo"); $ref -> ('main', "foo"); routine ($pkg, "foo"); $ref -> ($pkg, "foo"); &$ref ($pkg, "foo"); routine ($obj, "foo"); # etc.

    More on differences between subroutine calls either as function or method: perlsub, perlmod, perlref.

    The expression

    my (undef, $thing) = @_;

    is just a fancy way of saying

    shift @_; # drop first element of argument list my ($thing) = @_;

    The subroutine get_callback you quoted just seems not to care at all about it's invocant, but nonetheless expects to be called as a method. Hmm, code smell...

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

      Thank you all so much for your replies. I sincerely appreciate your time and efforts in making things clearer for me. I'll need to re-read a couple of times at least, but I have at least a much better understanding now. Thank you again.

        Well, it is a bit difficult to understand if you don't practice. I think you really need to start using these things in your code and make your own experiments to really grasp them. Once you get a working knowledge on them, you'll probably wonder how you could live without them before.

        If you're interesting in digging into these techniques further, please read this excellent book: https://hop.perl.plover.com/book/ by dominus. You can read it for free on-line, but I should warn you that you might end up willing to buy the paper copy. At least, that's what happen to me when I first read it about ten years ago.

Re: Trying to Understand Callback(s)
by Anonymous Monk on Nov 23, 2017 at 23:25 UTC
    When Mojo needs to (say ...) "get a URL," it will "call back" to a subroutine that you provided (within your own application, not Mojo) for that purpose. To accomplish this, you are providing a reference to the particular subroutine within your application that you want to be called. Mojo then calls your subroutine by means of that reference, as other Monks have already shown.