Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options

Why are closures cool, continued?

by mr.dunstan (Monk)
on Jan 19, 2002 at 02:17 UTC ( #139973=perlquestion: print w/replies, xml ) Need Help??
mr.dunstan has asked for the wisdom of the Perl Monks concerning the following question:

This has been a hot topic at my workplace and my boss came up with this example for showing how closures work.
#!/usr/local/bin/perl use strict; my $dave = new Foo ("dave", "oranges", "grapefruit"); $dave->("debug message"); package Foo; sub new { my ($class, @stuff) = @_; return sub { doit(@stuff, @_) }; # <-- closure here? } sub doit { print shift, ": ", join(",", @_), "\n"; }
Prepare to correct me if I'm wrong - I think what we've got here is a constructor (sort of) that returns a reference to an anonymous subroutine. When we call new() we're also setting $class and @stuff in the namespace of that anonymous subroutine to be "dave" and ("oranges", "grapefruit") respectively.

When I run this thing I get:

dave: oranges,grapefruit,debug message

Ok, so this is kind of neat, now I understand how you can use one constructor and some properties in order to respond with a reference to an anonymous subroutine of your choosing, but what I'm curious about is - is this -really- a closure?

I'm not completely versed in how long values like $class and @stuff would live inside a sub - only for the duration of the execution of the sub, right?

The closure (or rather using those values in the anonymous sub) makes these values live on past the execution of the sub, right?

Super Confused, -mr.dunstan

Replies are listed 'Best First'.
Re: Why are closures cool, continued?
by perrin (Chancellor) on Jan 19, 2002 at 03:10 UTC
    That's a very confusing example because it looks like an object but isn't. That example would still work if you remove the OO looking stuff:
    #!/usr/local/bin/perl use strict; my $dave = subref("dave", "oranges", "grapefruit"); $dave->("debug message"); sub subref { my (@stuff) = @_; return sub { doit(@stuff, @_) }; # <-- closure here? } sub doit { print shift, ": ", join(",", @_), "\n"; }
    Yes, it is a closure, but it's also an anonymous sub ref, so it's showing multiple things. Here's a much simpler closure:
    my $foo; sub bar { $foo++; }
    After execution, bar() has a private copy of $foo. TheDamian uses this in his book to do private variables for classes.
Re: Why are closures cool, continued?
by AidanLee (Chaplain) on Jan 19, 2002 at 03:04 UTC

    It really is a closure :)

    The key is that the array @stuff are only supposed to exist while new() is the current scope. But by declaring a subroutine reference that uses @stuff you are forcing perl to not garbage-collect it at the end of new()'s scope. However, the only way you can now touch this array which is in limbo is through the subroutine reference which you returned from the subroutine new().

Re (tilly) 1: Why are closures cool, continued?
by tilly (Archbishop) on Jan 19, 2002 at 03:39 UTC
    If you come from OO land, a good model of a closure is an arbitrary object with one method and no class.

    At this moment you may wonder why not just use an object, then you aren't just limited to one method, and it is not like a class is a lot of overhead.

    The answer is that someone can turn around and say, "Oh, I need a new kind of closure here" and inline it very easily within a function. (Frequently you parametrize different kinds of functions.) Creating a class is (relatively) a lot more infrastructure and verbiage.

    The key words for most of the basic uses are "callback" and "handler". The callback idea is that you have people pass you some anonymous functions into your function, and then you call them at appropriate moments (sort of what you use method hooks for in OO design but you don't need to set up a class, an object, etc). The handler idea is that you set up various functions that will handle various specific cases, and when you encounter the cases you just call the handler and let it take care of things.

    (It is not uncommon for what you think of as callbacks you pass into a function be handlers inside of it.)

    A specific example to look at is ReleaseAction which very clearly shows how, once you have a little infrastructure code, you can do something you would normally do with a class without writing a class each time.

(jeffa) Re: Why are closures cool, continued?
by jeffa (Bishop) on Jan 19, 2002 at 03:20 UTC
    If you wanted a 'real' constructor, just add bless:
    sub new { my ($class, @stuff) = @_; return bless sub { doit(@stuff, @_) },$class; }

    Now, if you want what i consider to be a good, everyday, pratical solution that uses a closure (not just a crazy way to make eyes go cross-eyed), then check out (the recently renamed) (jeffa) Practical Use For What jeffa Thought A Closure Was (3Re: Style Question on Closures). There are, of course, many more practical uses for closures ... this one has served me well, and i really think that it is easy to understand.

    UPDATE: nope - i am wrong again. Sorry mr.dunstan. Thanks for the catch perrin.


    (the triplet paradiddle with high-hat)
      That's a good example of using code refs, but it's not a closure. There are no private variables (unless I missed something there).
        That's a good example of using code refs, but it's not a closure. There are no private variables (unless I missed something there).

        The anonymous routine (blessed or not) refers to a lexically-scoped variable in its enclosing scope. That lexically-scoped variable is magically kept alive (in the anonymous subroutine) after that enclosing scope has been exited. That is what makes an anonymous subroutine a closure.

        sub makeAnonymousSub { sub { print "I am not a closure\n" } } sub makeClosure { my $x = shift; sub { print "I keep $x alive, thus I'm a closure\n" } }

        Update: This is in agreement with perrin, btw. I'm just filling in some details...

        Hey! I was thinking something along those lines, just because the variables themselves are not declared -within- the closure ... only near it?

        I am thinking this means:
        1) There is no spoon, I mean closure.
        2) That's a closure, but not an academic example of a closure.
        3) Mr. Dunstan, you are mincing words to gain karma.

        Zuh? -mr.dunstan
Re: Why are closures cool, continued?
by trs80 (Priest) on Jan 19, 2002 at 03:19 UTC
    From my understanding of closures, yes, the above sample is
    a closure. I reread the Camel (2nd ed. pg. 253) and the
    example given there is similar, but the one above has added
    an additional sub (doit) to the mix. It also states that
    "By and large, closures are not something you need to trouble
    yourself about."
    The purpose however is to allow for something to run in the
    in the context it was initially called outside of that
    context. Which means you are creating an anonymous sub
    that will retain its initial values and when you access
    it at a later point it will "remember" it had a value.

    In the above example $dave is equal to a sub, it
    appears that Data::Dumper doesn't handle closures since
    I ran $dave through the Dumper and it gave me a value of
    sub { "DUMMY" }

    Any way interesting chunck of code.
      Data::Dumper is going to give out "DUMMY" as the value for any sub, not just a closure. The contents of a subroutine are represented internally with a variety byte code I believe, which would look like so much nonsense if it were printed in the contents of a Data::Dumper dump. Contrast to scalars (or hash keys or hash/array values made from scalars) and to references. Scalars eventually turn out to be some sequence of characters, and references inside a data structure can be represented with brackets and braces.

      'perldoc Data::Dumper' has more info on this in the BUGS section.

        As to "[t]he contents of a subroutine [being] represented internally with a variety [of] byte code", you're both right and wrong (as is so often the case when talking about internals). The internals do deal with coderefs as pointing to a bunch of bytecode, but that bytecode is not inherently serializable; it has lots of pointers in it. Moreover (without resorting to Devel:: or B:: modules), it isn't accessable with Perl code.

        The perl6 analog of Data::Dumper will be able to dump coderefs as Parrot assembler (pasm); one of the cool side-effects of going to an explicit VM.

        James Mastros,
        Just Another Perl Scribe

Re: Why are closures cool, continued?
by mr.dunstan (Monk) on Jan 19, 2002 at 04:11 UTC
    Ok how about this: true or false (the author of the very first example in this thread and I were talking thru what constitutes this) ...

    "A closure is different from an anonymous subroutine in that (instead of having private variables) it -saves- the variables passed into it in its scope!"

    This seems like a reasonable enough and perfectly clear statement, but to be honest anytime I think something is reasonable and clear, that's when I'm about to start being REALLY wrong! :)

      Closure and anonymous sub -- two totally different things! An anonymous sub is a ref to a sub without a name. A closure is any sub (named or not) that refers to lexical variables defined outside of its scope.
        Nearly. An anonymous sub is a sub without a name that can only be accessed via a reference (to that sub).

        g r i n d e r
        print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u';