Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Re: closures: anonymous subs vs function templates?

by Athanasius (Archbishop)
on Dec 21, 2014 at 13:11 UTC ( [id://1110954]=note: print w/replies, xml ) Need Help??


in reply to closures: anonymous subs vs function templates?

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,

Replies are listed 'Best First'.
Re^2: closures: anonymous subs vs function templates?
by Laurent_R (Canon) on Dec 21, 2014 at 15:56 UTC
    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,

        Well, there are cases where you can actually do something useful with named closures. Consider this example, a very simplified version of part of a module I wrote at work a few years ago. I wanted the module to "remember" about error buffer pointer, connection id and transaction id returned by a database application, without the user of the module having to worry about these internals. I used something like this to build two closures acting as a mutator and an accessor:
        { my ($error_buff_p, $connection_id, $transaction_id); my $init_done = 0; sub get_val {return ($error_buff_p, $connection_id, $transaction_id +, $init_done);}; sub set_val { ($error_buff_p, $connection_id, $transaction_id, $ini +t_done) = @_;}; }
        Now, somewhere else in the same module, I had an init_connection function, which called the application API to open the connection to the database and to get these identifiers. That init_connection sub called the set_val function, passing it the parameters. Once this is done, the parameters are stored within the two closures. And other subs in my module could call the get_val sub to obtain the stored parameters when needed. This enabled me to make those pieces of data persistent and relatively strongly encapsulated within the module.

        It could be made in many other ways (anonymous closures, objects, even lexical variables global to the module, etc.), and I would probably do it differently today (probably with anonymous subs), but I found this to be a quite lightweight solution to my needs at the time.

        Having said that, I think that we both agree the anonymous closures are most of the time more useful and more practical. Especially when you want your function to return a code_ref to the caller, as your example shows.

Re^2: closures: anonymous subs vs function templates?
by Laurent_R (Canon) on Dec 21, 2014 at 18:46 UTC
    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,

        In my function template example, I put the local in front of *inner for just that reason - to limit the scope of inner to the block in which it is defined for the purposes of both limiting access and improving garbage collection.

        I appreciated the conversation all of these examples has created. I've learned a few things in the process. I thank all of you who have contributed this analysis.

        I of course did not want to promote function templates for such use cases, but just tried to see how they could work out. I definitely agree that the approach with references to anoonymous subroutines is vastly superior, especially in view of your first remark: a new closure is destroying the previous one.
Re^2: closures: anonymous subs vs function templates?
by 5haun (Scribe) on Dec 22, 2014 at 04:15 UTC

    Athanasius,

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

    -Shaun

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (5)
As of 2024-04-19 14:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found