Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

closures: anonymous subs vs function templates?

by 5haun (Scribe)
on Dec 21, 2014 at 07:51 UTC ( [id://1110934]=perlquestion: print w/replies, xml ) Need Help??

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

I was reading through perlsub and perlref again on metacpan and it occurred to me creating a closure using function templates is a lot like using an anonymous subroutine, only the syntax is a little different (using local *name = sub {}; name('arg'); versus my $name = sub {}; $name->('arg');).

Is this true or am I miss understanding something? What about compile time vs runtime?

Thanks,
-Shaun

use warnings; use v5.14; my $y = 2014; sub outer1 { my $x = $_[0] + 35; local *inner = sub { my $a = shift; local *__ANON__ = ( caller( 1 ) )[3] . "->inner"; say( ( caller( 0 ) )[3] ); return $y - $x * 19 + $a; }; return $x + inner( $_[0] / 2 ); } sub outer2 { my $x = $_[0] + 35; my $inner = sub { my $a = shift; local *__ANON__ = ( caller( 1 ) )[3] . "->inner"; say( ( caller( 0 ) )[3] ); return $y - $x * 19 + $a; }; return $x + $inner->( $_[0] / 2 ); } say outer1( 8 ); say outer2( 8 ); __END__ $ perl subtest.pl main::outer1->inner 1244 main::outer2->inner 1244

Replies are listed 'Best First'.
Re: closures: anonymous subs vs function templates?
by Athanasius (Archbishop) on Dec 21, 2014 at 13:11 UTC

    Hello 5haun,

    As Laurent_R says, neither of your two inner functions is a closure. From the Glossary:

    closure
    An anonymous subroutine that, when a reference to it is generated at runtime, keeps track of the identities of externally visible lexical variables, even after those lexical variables have supposedly gone out of scope.

    In both your examples, the inner subroutine references the lexical variable $y, but that is never out of scope. A better example is given in the perlfaq7 entry, “What’s a closure?”:

    use strict; use warnings; my $f1 = make_adder( 20); my $f2 = make_adder(555); printf "%d\n", $f1->(10); printf "%d\n", $f2->(10); sub make_adder { my $addpiece = shift; print "\$addpiece = $addpiece\n"; my $inner = sub { return shift() + $addpiece; }; return $inner; }

    Output:

    22:42 >perl 1102_SoPW.pl $addpiece = 20 $addpiece = 555 30 565 22:42 >

    It is apparent that the $inner sub is constructed when sub make_adder is called, but not run until called explicitly via the returned function pointer. But if we attempt to do this with a function template, the warning we get:

    Use of uninitialized value in addition (+) at...

    shows that the inner sub is actually run at the time when the outer sub is called.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hi Athanasius

      Thank you for your very interesting post.

      One comment is that I sort of disagree in part with the Perldoc glossary entry you are quoting: to me, a closure does not necessarily have to be an anonymous function, you can do a closure with a named function, as shown in the following trivial (and not terribly useful) example:

      use strict; use warnings; use v5.14; { my $add = 20; sub add_20 { my $c = shift; return ($c + $add);} } say add_20(30);
      which duly prints 50. The $add variable has supposedly fallen out of scope when the sub is called, but it still works, because the add_20 sub is closing over the $add variable.

      Having said that, closures are mostly anonymous functions, because they are mostly useful as anon subs.

        Hello Laurent_R,

        I think you are correct: your example code does seem to fulfil the requirements for a closure according to the Wikipedia article Closure_(computer_programming). And at the start of the tutorial Closure on Closures, broquaint takes a similar view:

        However, I believe this isn't entirely accurate as a closure in perl can be any subroutine referring to lexical variables in the surrounding lexical scopes.

        But, as you point out, this type of closure isn’t very useful, since there is no way to change the value of $add from outside the block. And if we try to return a reference to a named function from within another function, it doesn’t work as required:

        #! perl use strict; use warnings; use v5.14; sub make_add { my $addpiece = shift; sub add { my $c = shift; return ($c + $addpiece); } return \&add; } my $f1 = make_add(20); my $f2 = make_add(35); say $f1->(10); say $f2->(10);

        Output:

        2:40 >perl 1102_SoPW.pl Variable "$addpiece" will not stay shared at 1102_SoPW.pl line 9. 30 30 2:40 >

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hi Athanasius
      But if we attempt to do this with a function template, the warning we get:
      Use of uninitialized value in addition (+) at...
      shows that the inner sub is actually run at the time when the outer sub is called.

      I may have misunderstood this part of your post, but I interpreted it to mean that it is not possible to build a closure with a function template. That surprised me quite a bit, so I decided to try, modifying your sample code.

      When I run this code:

      use strict; use warnings; local *f1 = make_adder( 20); printf "result f1 = %d\n", f1(10); local *f2 = make_adder(555); printf "result f2 = %d\n", f2(10); sub make_adder { my $addpiece = shift; print "\$addpiece = $addpiece\n"; *inner = sub { return shift() + $addpiece; }; return *inner; }
      I get a warning, but it seems to work nonetheless (at least to print the correct values):
      $ perl func_template2.pl $addpiece = 20 result f1 = 30 $addpiece = 555 Subroutine main::inner redefined at func_template2.pl line 15. result f2 = 565
      Fair enough, the warning says that the inner function is being redefined, which is of course not a very good idea in this context (and, as we know and as you have shown with your example, we would not have that with lexical code refs, which is a good reason to think that lexical code refs are superior to function templates for that kind of things). But, yet, the functions f1 and f2 can still print the right values, meaning that they apparently can close on these values. And I don't get any warning if I use only f1 (and not f2).

      I would tend to think that it is likely to be my fault and that I probably did not understand correctly what you meant to say, but, if such is the case, I would like to ask you to explain further what you meant.

        Hello Laurent_R,

        You understood me correctly, and it appears I was wrong: you have shown that it is possible to build a closure using function templates. But I note that, to get the syntax to work, you had to remove the local from the definition of *inner within sub make_adder, and that makes *inner a package global sub. In fact, what sub make_adder returns is simply the string *main::inner. So we can dispense with make_adder’s return value and the assignments to *f1 and *f2 altogether:

        #! perl use strict; use warnings; use v5.14; make_adder(20); say inner(10); make_adder(25); say inner(10); sub make_adder { my $addpiece = shift; print "\$addpiece = $addpiece\n"; *inner = sub { return shift() + $addpiece; }; }

        Output:

        13:02 >perl 1102_SoPW.pl $addpiece = 20 30 $addpiece = 25 Subroutine main::inner redefined at 1102_SoPW.pl line 15. 35 13:02 >

        This approach is inferior to the standard technique (references to anonymous subroutines) in at least two ways:

        1. Each call to make_adder creates a new closure, and the previous closure is no longer available (hence the warning message).
        2. The memory allocated to $addpiece will never be garbage collected as long as the script is running.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Athanasius,

      Your Closure definition post helped A LOT! Thank you. Your analysis after the example put an important difference into perspective.

      -Shaun

Re: closures: anonymous subs vs function templates?
by Laurent_R (Canon) on Dec 21, 2014 at 10:49 UTC
    Both syntaxes use anonymous subs (but they are not really closures in your examples). The first syntax is writing the sub identifier into the symbol table, so that the sub can be called as a normal subroutine (without the -> dereferencing operator), while the second one is using a lexical variable which may have a limited scope and lifespan.

    Personally, I would tend to use only the lexical form (with scope) unless I had a good specific reason to do otherwise.

      Thank you for the description. When combined with the example from Anonymous Monk, it was helpful in demonstrating the extent of scope of each of the two forms. The lexical form is certainly more limited.
        The lexical form is certainly more limited.
        I don't really agree with that sentence. Being able to limit the scope is usually extremely useful. It not only reduces the risk of errors associated with global variables (and other global identifiers), but it also makes it possible to build very powerful constructs.

        Perl 5.0 introduced lexical variables, Perl 5.6 introduced lexical file and directory handles, Perl 5.18 introduced lexical subroutines, the tendency over the years has been to go more and more for lexical "things". Because they add a lot of expressive power to the language.

Re: closures: anonymous subs vs function templates?
by Anonymous Monk on Dec 21, 2014 at 09:53 UTC
    What is a 'function template?' local *name = sub {}; versus my $name = sub {}; are both created using anonymous subs. The assignment operator makes them go to different places though (sym table and lexpad, resp.). Then the difference between them is the same as the difference between all other local/my variables...
    use 5.020; use warnings; outer1(); exit 0; sub outer1 { my $hello = 'hello'; local *inner = sub { say $hello; }; outer2(); } sub outer2 { inner(); } __END__ hello

      Thank you for that example. When edited as shown below, it clearly highlighted the differences:

      use warnings; use v5.14; outer1(); exit 0; sub outer1 { my $hello = 'hello'; # local *inner = sub { my $inner = sub { say $hello; }; outer2(); } sub outer2 { # inner(); $inner->(); } __END__ Global symbol "$inner" requires explicit package name at /tmp/subtst.p +l line 20. Execution of /tmp/subtst.pl aborted due to compilation errors.

      Compared with your example, we can show the more limited scope of the lexical form, as recommended by Laurent_R. Thank you both!

Re: closures: anonymous subs vs function templates?
by Anonymous Monk on Dec 21, 2014 at 08:47 UTC

    Is this true or am I miss understanding something?

    Its more or less true , naturally named subroutines live in the symbol table (globals), my vars don't live in the symbol table

    What about compile time vs runtime?

    Yes, those things exist :) BeginCheckInitEndUnitcheck.pl

    <code>

    Um, yeah, I'd get rid of that code, esp the maths parts :) .oO( wheel? If I add multiply and divide these wheels, where is my question? )

Re: closures: anonymous subs vs function templates?
by builat (Monk) on Dec 21, 2014 at 08:14 UTC
    Im sorry, is use strict; sets up?

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (7)
As of 2024-04-16 16:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found