|Problems? Is your data what you think it is?|
Closure on Closuresby broquaint (Abbot)
|on Jun 25, 2003 at 16:08 UTC||Need Help??|
Closure on Closures
Before we get into this tutorial we need to define what a closure is. The Camel (3rd edition) states that a closure is
when you define an anonymous function in a particular lexical scope at any particular momentHowever, 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.
Now with that (simple?) definition out of the way, we can get on with the show!
Before we get started ...
For one to truely understand closures a solid understanding of the principles of lexical scoping is needed, as closures are implemented through the means of lexical scoping interacting with subroutines. For an introduction to lexical scoping in perl see Lexical scoping like a fox, and once you're done with that, head on back.
Right, are we all here now? Bueller ... Bueller .. Bueller? Good.
Now as we all know, lexical variables are only active for the length of the surrounding lexical scope, but can be kept around in an indirect manner if something else references them e.g
The above example illustrates that $bar isn't cleaned up until $foo, which references it, leaves the surrounding lexical scope (the file-level scope in this case). So from that we can see lexical variables only stick around for the length of the surrounding scope or until they're no longer referenced.
But what if we were to re-enter a scope where a variable is still visible, but the scope has already exited - will the variable still exist?
As we can see the answer is categorically 'No'. In retrospect this is quite obvious as $foo has gone out of scope and there is no longer a reference to it.
A bit of closure
However, the last example just used a simple bareblock, now let's try it with a subroutine as the inner block
"Hold on there cowboy - $foo has already gone out of scope at the time of the first call to inner() let alone the second, what's going on there?!?", or so one might say. Now hold your horses, there is a very good reason for this behaviour - the subroutine in the example is a closure. "Ok, so it's a closure, but why?", would be a good question at this point. The reason is that subroutines in perl have what's called a scratchpad which holds references to any lexical variables referred to within the subroutine. This means that you can directly access lexical variables within subroutines even though the given variables' scope has exited.
Hmmm, that was quite a lot of raw info, so let's break it down somewhat. Firstly subroutines can hold onto variables from higher lexical scopes. Here's a neat little counter example (not counter-example ;)
While not immediately useful, the above example does demonstrate a subroutine counter() (line 3) holding onto a variable $cnt (line 2) after it has gone out of scope. Because of this behaviour of capturing lexical state the counter() subroutine acts as a closure.
Now if we look at the above example a little closer we might notice that it looks like the beginnings of a basic iterator. If we just tweak counter() and have it return an anonymous sub we'll have ourselves a very simple iterator
Now instead of counter() being the closure we return an anonymous subroutine (line 3) which becomes a closure as it holds onto $cnt (line 2). Every time the newly created closure is executed the $cnt passed into counter() is returned and decremented (this post-return modification behaviour is due to the nature of the post-decrement operator, not the closure).
So if we further apply the concepts of closures we can write ourselves a very basic directory iterator
In the code above dir_iter() (line 3) is returning an anonymous subroutine (line 6) which is holding $dir (line 4) from a higher scope and therefore acts as a closure. So we've created a very basic directory iterator using a simple closure and a little bit of help from IO::Dir.
Wrapping it up
This method of creating closures using anonymous subroutines can be very powerful. With the help of Richard Clamp's marvellous File::Find::Rule we can build ourselves a handy little grep like tool for XML files
Disclaimer: the above isn't thoroughly tested and isn't nearly perfect so think twice before using in the real world
The code above contains 3 simple examples of closures using anonymous subroutines (in this case acting as callbacks). The first closure can be found on in the exec parameter (line 24) of the find call. This is wrapping around the $callback variable generated by the gensub() function. Then within the gensub() (line 41) there are 2 closures which wrap around the $opts lexical, the second of which also wraps around $self which is a reference to the callback which is returned.
So let's bring it altogether now - a closure is a subroutine which wraps around lexical variables that it references from the surrounding lexical scope which subsequently means that the lexical variables that are referenced are not garbage collected when their immediate scope is exited.
There ya go, closure on closures! Hopefully this tutorial has conveyed the meaning and purpose of closures in perl and hasn't been too confounding along the way.
 see. chip's Re: Toggling between two values for a more technical
definition (and discussion) of closures within perl
Disclaimer: Not everyone will agree with the terminology (I imagine) so as long as you don't find the descriptions wildly off the mark or generally misleading then they're likely to stay as they are.
Update - revised the second and third sections by dropping any references to 'reference counting'