Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Sub prototypes...

by vladb (Vicar)
on Dec 21, 2001 at 21:23 UTC ( [id://133807]=perlquestion: print w/replies, xml ) Need Help??

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

Hello,

I'm having some problem understanding the exact workings of the following code:
sub try (&@) { print "try();\n"; my($try,$catch) = @_; eval { &$try }; if ($@) { local $_ = $@; &$catch; } } sub catch (&) { print "catch();\n"; $_[0] } try { die "foobar\n"; print "OK"; } catch { print "FAILED!\n"; };

OUTPUT:
catch(); try(); FAILED! did ok
-------------------

My question is why does the catch() sub gets called prior to the try() sub? My understanding was that two anonymous code references will be passed to try() and than catch() would be invoked from within try() therefore printing "try()" prior to "catch()".

Appreciate your help ;).

"There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith

Replies are listed 'Best First'.
Re: Sub prototypes...
by Fastolfe (Vicar) on Dec 21, 2001 at 21:40 UTC
    This code:
    try { die "foobar\n"; print "OK"; } catch { print "FAILED!\n"; };
    Executes like this:
    try( sub { die "Foobar"; print "OK"; }, catch(sub { print "FAILED!\n"; }) );
    In other words, there are two arguments to try(): a coderef and whatever catch() returns (normally a coderef, its own first argument). So catch() has to be called before try() can be called.
Re: Sub prototypes...
by converter (Priest) on Dec 22, 2001 at 04:21 UTC

    The print statment in the catch subroutine definition at line #10 is confusing you (and probably everybody else).

    The catch subroutine is only called once, and its only purpose is to return the sub ref passed to it, which is then passed as an argument to try in @_. An anonymous subroutine composer could be used in place of the call to catch for the same effect:

    try { ... } sub { print "FAILED!\n"; }

    The subroutine reference passed to try in @_ is evaluated at line #4. The eval block catches the exception caused by the die statement in line #13 and sets $@ to the exception string, "foobar\n" (it's worth noting that if the argument to die includes a newline, line number information will not be included in the exception string).

    Line #5 checks for an eval exception and if true, localizes $_ and sets it to the value of the eval exception, then calls the anonymous subroutine returned by catch, which just happens to be stored in a lexical variable named "$catch". Since the anonymous subroutine is called from the same scope as the localized $_, that copy of $_ is visible to the anonymous subroutine, not the global $_.

    This is where you've missed the purpose of this code: the anonymous subroutine returned by catch should check the value in $_ for the specific exception set by the anonymous subroutine executed by eval, then take the appropriate action if the "tried" code failed.

    Think of "try/catch" as "try something, and if it fails, recover gracefully (if possible)".

    Now that you've read this and hopefully understand the code a little better, go back and read the example code in the perlsub manpage "Prototypes" section. It should make a lot more sense now.

    1 sub try (&@) { 2 print "try();\n"; 3 my($try,$catch) = @_; 4 eval { &$try }; 5 if ($@) { 6 local $_ = $@; 7 &$catch; 8 } 9 } 10 sub catch (&) { print "catch();\n"; $_[0] } 11 12 try { 13 die "foobar\n"; 14 print "OK"; 15 16 } catch { 17 print "FAILED!\n"; 18 };

    Lines 16-18 should probably be written:

    16 } catch { 17 /^foobar$/ and print "FAILED!\n"; 18 };

    conv

      converter your reply helped me understand this to every tiny detail, numerous thanks for your effort my fellow monk! ;-)

      I didn't expect someone to put up so much effort and explain an answer to my question so thoroughly.

      I feel a little dumb now though.. that I failed to understand the concept heh. Actually, it was my first try at subroutine prototypes.

      Say, I figure the code could be rewritten as follows:
      try({die "foobar\n"; print "OK";12}, catch {print "FAILED!\n";} );
      *laugh* I guess my struggle for Java-like OO-ish exception handling hit back and confused me beyound disbelief. lol ;-)

      cheers, vladb

      "There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith
Re: Sub prototypes...
by bmccoy (Beadle) on Dec 21, 2001 at 23:38 UTC
    Think about the order of execution when you have a subroutine executing as an argument to another subroutine. If you have a sub that is expecting an array as argument, and instead you give it a sub that returns an array, like this:

    sub1(sub2()); sub sub1 { my @array = @_; ... } sub sub2 { ... return @arr; }
    In what order do you expect them to be executed? I think this is what is happening here, the catch is being executed first since it is an argument to try.

    -- Brett

    Go not to the Elves for counsel, for they will say both no and yes

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://133807]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (3)
As of 2024-04-20 12:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found