Beefy Boxes and Bandwidth Generously Provided by pair Networks RobOMonk
Syntactic Confectionery Delight
 
PerlMonks  

A Real Closure

by japhy (Canon)
on Jul 12, 2001 at 16:03 UTC ( #95990=note: print w/ replies, xml ) Need Help??


in reply to (tye)Re: Unusual Closure Behaviour
in thread Unusual Closure Behaviour

A while back I'd talked to Randal about closures, and he told me that a closure needn't be a code reference:

>>>>> "Jeff" == Jeff Pinyan <jeffp@crusoe.net> writes: Jeff> A closure is an ANONYMOUS function (constructed via $x = sub { Jeff> ... }) that contains LEXICAL variables that have been defined in Jeff> a scope visible to the closure itself. leave out the word ANONYMOUS there. ANONYMOUS and CLOSURE are orthogonal. in "BEGIN { my $x; sub foo { ... $x ... } }", foo is a CLOSURE and is not ANONYMOUS.


japhy -- Perl and Regex Hacker


Comment on A Real Closure
Download Code
Re: A Real Closure
by John M. Dlugosz (Monsignor) on Jul 12, 2001 at 18:32 UTC
    The docs are pretty clear that using sub to create a ref to an anonomous sub will do closures and that a normal named sub will not.

    So I tried it. It gives me a warning that "$x will not stay shared", but the result seems to work! That is, a created named function seems to reference the same variable as a standard closure created in the same scope, and running the creator again (which makes a different local $x) keeps distinct identities.

    So what's going on here? Are the docs outdated? Is this working by accident or happenstance? Does the presence of a regular closure somehow make it work?

    —John

    use v5.6.1; # Active State build 626 use strict; use warnings; sub outer { my $x= shift; my $name= shift; my $closure= sub { return $x++; }; eval "sub $name { return \$x++; }"; return $closure; } my $r1= outer (1, 'f1'); my $r2= \&f1; print $r1->(), $r2->(), "\n"; my $r3= outer ('A', 'f2'); my $r4= \&f2; print $r3->(), $r4->(),$r3->(), $r4->(), "\n"; print $r1->(), $r2->(),$r1->(), $r2->(), "\n";
      Err, the warning is due to defining a function (that ends up acting as a closure, due to its use of lexicals defined in an upper scope) inside a function. But if you write:
      { my $x; sub count { ++$x } }
      Then $x is private to &count, and because &count refers to a lexical that "should" have "gone away", it is a closure. At least, that is what I have understood from Randal.

      japhy -- Perl and Regex Hacker
        That's not the same thing. $x is not in the package symbol table, but it still has a persistant identity. The compiler looks up the SV and points to it, and it stays that way. It's just like any other variable that's bound at compile time. Not being in the symbol table but in a lex table instead doesn't change the generated code.

        The "closure" issue refers to a specific instance of a variable that keeps changing its identity. In a function, $x is a different SV each time it is called. The creation point of a closure uses the current one. The creation of the closure takes place after the my has its run-time effects. Your example creates count at compile-time

        the warning is due to defining a function (that ends up acting as a closure, due to its use of lexicals defined in an upper scope) inside a function
        You'll get that warning no matter how you contrive to create a named sub refering to a lexical variable that gets re-bound with each pass through the code.
(tye)Re2: A Real Closure
by tye (Cardinal) on Jul 13, 2001 at 00:46 UTC

    That is an interesting case BEGIN { my $x; sub foo { ... $x ... } }
    Perl could decide to implement that as a closure (and probably does because "BEGIN" blocks are implemented as subroutines). But lets find out:

    #!/usr/bin/perl -w use strict; use Devel::Peek qw(Dump); BEGIN { my $x; sub begin { ++$x; } } sub justmy { my $x; ++$x; } sub ifmy { my $x if 0; ++$x; } { my $x; sub static { ++$x; } } sub nest { my $x; sub inner { ++$x } } sub gen { my $x; return sub { ++$x }; } *insert= gen(); Dump $_ for( \&begin, \&justmy, \&ifmy, \&static, \&inner, gen(), \&insert + );
    The "cleaned up" output is:
    Variable "$x" will not stay shared at closure.pl line 27. begin: SV = RV(0x1a83a20) at 0x1a65068 SV = PVCV(0x1a8340c) at 0x1a62144 GVGV::GV = 0x1a7b6c0 "main" :: "begin" PADLIST = 0x1a7b690 1. 0x1a621b0 (FAKE "$x" 0-57) OUTSIDE = 0x1a620fc (UNIQUE) justmy: SV = RV(0x1a83a2c) at 0x1a65098 SV = PVCV(0x1a8345c) at 0x1a620f0 GVGV::GV = 0x1a7b72c "main" :: "justmy" PADLIST = 0x1a7b708 1. 0x1a7b714 ("$x" 59-60) OUTSIDE = 0x1a6f124 (MAIN) ifmy: SV = RV(0x1a83a30) at 0x1a650b0 SV = PVCV(0x1a7adbc) at 0x1a7b750 GVGV::GV = 0x1a7b798 "main" :: "ifmy" PADLIST = 0x1a7b774 1. 0x1a7b780 ("$x" 61-62) OUTSIDE = 0x1a6f124 (MAIN) static: SV = RV(0x1a839dc) at 0x1a650e0 SV = PVCV(0x1a7ada4) at 0x1a7b76c GVGV::GV = 0x1a7b7a8 "main" :: "static" PADLIST = 0x1a7b790 1. 0x1a620f0 (FAKE "$x" 0-64) OUTSIDE = 0x1a6f124 (MAIN) inner: SV = RV(0x1a83a34) at 0x1a650c8 SV = PVCV(0x1a7ae5c) at 0x1a7b81c GVGV::GV = 0x1a7b858 "main" :: "inner" PADLIST = 0x1a7b840 1. 0x1a7b7ec (FAKE "$x" 0-64) OUTSIDE = 0x1a7b7bc (nest) gen(): SV = RV(0x1a83a38) at 0x1a65158 SV = PVCV(0x1a7af9c) at 0x1a650e0 FLAGS = (ANON,CLONED) GVGV::GV = 0x1a7b924 "main" :: "__ANON__" PADLIST = 0x1a65128 1. 0x1a65080 (FAKE "$x" 0-67) OUTSIDE = 0x1a7b894 (gen) SV = PVCV(0x1a7aeac) at 0x1a7b894 GVGV::GV = 0x1a7b93c "main" :: "gen" PADLIST = 0x1a7b8b8 1. 0x1a65170 ("$x" 66-68) 2. 0x1a7b8e8 ("&" 1--1) OUTSIDE = 0x1a6f124 (MAIN) insert: SV = RV(0x1a83a3c) at 0x1a65188 SV = PVCV(0x1a7af4c) at 0x1a6f01c FLAGS = (ANON,CLONED) GVGV::GV = 0x1a7b924 "main" :: "__ANON__" FLAGS = 0x6 PADLIST = 0x1a6f0f4 1. 0x1a7b8c4 (FAKE "$x" 0-67) OUTSIDE = 0x1a7b894 (gen) SV = PVCV(0x1a7aeac) at 0x1a7b894 GVGV::GV = 0x1a7b93c "main" :: "gen" PADLIST = 0x1a7b8b8 1. 0x1a65170 ("$x" 66-68) 2. 0x1a7b8e8 ("&" 1--1) OUTSIDE = 0x1a6f124 (MAIN)
    or just consider
    begin: 1. 0x1a621b0 (FAKE "$x" 0-57) justmy: 1. 0x1a7b714 ("$x" 59-60) ifmy: 1. 0x1a7b780 ("$x" 61-62) static: 1. 0x1a620f0 (FAKE "$x" 0-64) inner: 1. 0x1a7b7ec (FAKE "$x" 0-64) gen(): 1. 0x1a65080 (FAKE "$x" 0-67) insert: 1. 0x1a7b8c4 (FAKE "$x" 0-67)
    which seems to indicates that "justmy" and "ifmy" are not implemented as closures but all of the rest are implemented as closures. So I'll certainly be more lenient in what I let other people call closures. (:

    But it also indicates that the padlist is carried around for ordinary subroutines, which makes that aspect of the implementation less important to me.

    I think that the important thing about closures is being able to call the same code but have it use different variables (without passing them in as arguments). Above, only the anonymous subroutine and "insert" meet that criterion. So those are what I'll call closures. The other 3 cases I'll call "static variables that Perl implements via closures" if pushed. :)

            - tye (but my friends call me "Tye")
      I think that the important thing about closures is being able to call the same code but have it use different variables (without passing them in as arguments). Above, only the anonymous subroutine and "insert" meet that criterion. So those are what I'll call closures.

      What do you mean by this? I think I'm missing something when it comes to closures...

        A "classic" example of closures goes something like this:

        sub generateSequencer { my( $start, $inc )= @_; $start -= $inc; return sub { return $start += $inc; }; } my $countByTwos= generateSequencer( 0, 2 ); print $countByTwos->(), $/ for 1..3; my $countByThrees= generateSequencer( 1, 3 ); print $countByTwos->(), " ", $countByThrees->(), $/ for 1..4;
        which generates the output:
        0 2 4 6 1 8 4 10 7 12 10

        So we have $countByTwos and $countByThrees are both references to the code: sub { return $start += $inc; };
        except that the code reference $countByTwos has hidden inside of it references to the $start and $inc that were created when generateSequencer was called the first time while $countByThrees has hidden inside of it references to the other $start and $inc (that were created when generateSequencer was called the second time).

        So both of those code references end up using the same code, but they each end up using different variables even though we don't pass any variables at all as arguments in here: print $countByTwos->(), " ", $countByThrees->(), $/

                - tye (but my friends call me "Tye")
        Ok, I'll try (though see John M. Dlugosz's answer just above):
        > perl -lwe'sub k{my $x; sub j{++$x}; return sub{++$x} } k(); $a=\&j; k(); $b=\&j; $c=k(); $d=k(); print"a b c d"; $,=$"; print &$a, &$b, &$c, &$d for 0..2' Variable "$x" will not stay shared at -e line 1. a b c d 1 2 1 1 3 4 2 2 5 6 3 3 >
        That is a and b are just two references to the same function and its (compiled-in) variable. c and d refer to different functons (with the same code) each with its own (run-time declared) variable.

        c and d are closer to what is referred to elsewhere as closures. (Not everywhere else, the original usage in set theory is yet another thing.)

        update Obviously I didn't reload the page before submitting. I thought tye had left, so it would be safe to try to answer without being compared to his much more complete answers. I'll leave mine up anyway, in the hope that repetition aids understanding.

          p

      I think that the important thing about closures is being able to call the same code but have it use different variables (without passing them in as arguments).

      No. You are just confusing a useful use of closures for a definition of what they are.

      A closure is simply a function that refers to a free lexical variable. In the code { my $x=0; sub foo { $x } }
      foo() is a closure; it just isn't a very useful one. In Perl, named closures are good for simulating static variables, but not much else (that I can think of.)

      -sauoq
      "My two cents aren't worth a dime.";
      

        I've also used that behaviour to get less-global shared variables. I was working around an even uglier problem and this isn't the sort of thing I'd recommend for general usage but it is another useful usage.

        { my $x=0; sub foo { $x } sub bar { $x } }
        I think that the important thing about closures is being able to call the same code but have it use different variables (without passing them in as arguments).
        No. You are just confusing a useful use of closures for a definition of what they are.

        "No"? So I don't think that that is the important thing about closures? Then what do I think? I'd like to know. q-:

        Find me a web page that contains a definiton for "closure" in terms of generic computer programming (best if it is not specific to just one programming language). I've looked for such a couple of times before and again recently and I haven't found anything even close. [ I'm quite familiar with the term "closure" from set theory (mathematics) -- as well as from popular "psychology" and for zippered plastic, food-storage bags. (: ]

        I've heard several definitions of "closure" and been told that several of them were wrong (including the one that I based my assertion several layers further up in this thread upon). But I've never seen any kind of authoritative definition of the term.

        If your language either doesn't have lexical variables or it doesn't have nested subroutines, then it doesn't make sense to talk about closures in regard to that language. For example, Perl 4 doesn't have lexical variables.

        If your language doesn't have lexical variables outside of functions, then your definition makes sense. But based on your definition, C has closures:

        #include <stdio.h> // Can't use x here since isn't available in this scope. // Hence, x is lexically scoped. static int x; void closure() { return x; }
        But I think people would be shocked to hear that C has closures. *shrug* Perhaps some would counter this by saying "x isn't really a lexical variable". Whatever. I don't care to split hairs on either definition.

        So I could see defining closures as you have. I don't find that definition very satisfying.

        If you were really replying to my post from a couple of years ago (further up in this thread), then you should know that I don't assume the original definition of "closure" that I was given is correct anymore.

        However, I do still think that there are a lot of things the people call "closures" for which the term isn't very meaningful. Among these I include:

        • Perl CODE references that refer to a subroutine that doesn't use any lexical variables from outside of itself
        • Subroutines that use lexical variables from an outer non-subroutine scope

        And I do still think that the important thing about closures is that you can use them to hide different lexicals into seperate refences to the same subroutine. And with your definition, I can have closures without being able to do this (if I have a language that doesn't have nested subroutines or a language without code references/pointers, for example).

        If one is implementing a language, then one probably thinks about closures in different ways than I usually do. Certainly, some of the more interesting statements I've heard about what is a closure have come from people digging into the internals for Perl.

        So I don't really care what the definition of "closure" is (or even if there is even a solid consensus on what that is). I'm glad I know a lot more (than a couple of years ago) about how far apart different people seem to draw their lines in the sand between what is and what isn't a closure. Perhaps a lot of these people were just misinformed Perl hackers feeding off of each other (I certainly was for a while).

        Perhaps you'll feel better if you just think "Perl closure" when you see that I have written "closure" on PerlMonks.

        Best of all, maybe I'll get a link to some authoritative material on the term "closure" out of this. :)

                        - tye

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (8)
As of 2014-04-17 22:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (458 votes), past polls