Skip to content

Latest commit

 

History

History
273 lines (197 loc) · 7.67 KB

closures.pod

File metadata and controls

273 lines (197 loc) · 7.67 KB

Closures

Every time control flow enters a function, that function gets a new environment representing that invocation's lexical scope (scope). That applies equally well to anonymous functions (anonymous_functions). The implication is powerful. The computer science term higher order functions refers to functions which manipulate other functions. Closures show off this power.

Creating Closures

A closure is a function that uses lexical variables from an outer scope. You've probably already created and used closures without realizing it:

If this code seems straightforward to you, good! Of course the get_filename() function can see the $filename lexical. That's how scope works!

Suppose you want to iterate over a list of items without managing the iterator yourself. You can create a function which returns a function that, when invoked, will return the next item in the iteration:

Even though make_iterator() has returned, the anonymous function, stored in $cousins, has closed over the values of these variables as they existed within the invocation of make_iterator(). Their values persist (reference_counts).

Because every invocation of make_iterator() creates a separate lexical environment, the anonymous sub it creates and returns closes over a unique lexical environment:

Because make_iterator() does not return these lexicals by value or by reference, no other Perl code besides the closure can access them. They're encapsulated as effectively as any other lexical encapsulation, although any code which shares a lexical environment can access these values. This idiom provides better encapsulation of what would otherwise be a file or package global variable:

Be aware that you cannot nest named functions. Named functions have package global scope. Any lexical variables shared between nested functions will go unshared when the outer function destroys its first lexical environmentIf that's confusing to you, imagine the implementation..

Uses of Closures

Iterating over a fixed-sized list with a closure is interesting, but closures can do much more, such as iterating over a list which is too expensive to calculate or too large to maintain in memory all at once. Consider a function to create the Fibonacci series as you need its elements. Instead of recalculating the series recursively, use a cache and lazily create the elements you need:

Every call to the function returned by gen_fib() takes one argument, the nth element of the Fibonacci series. The function generates all preceding values in the series as necessary, caches them, and returns the requested element--even delaying computation until absolutely necessary. Yet there's a pattern specific to caching intertwined with the numeric series. What happens if you extract the cache-specific code (initialize a cache, execute custom code to populate cache elements, and return the calculated or cached value) to a function generate_caching_closure()?

Now gen_fib() can become:

The program behaves as it did before, but the use of function references and closures separates the cache initialization behavior from the calculation of the next number in the Fibonacci series. Customizing the behavior of code--in this case, gen_caching_closure()--by passing in a function allows tremendous flexibility.

Closures and Partial Application

Closures can also remove unwanted genericity. Consider the case of a function which takes several parameters:

Myriad customization possibilities might work very well in a full-sized ice cream store, but for a drive-through ice cream cart where you only serve French vanilla ice cream on Cavendish bananas, every call to make_sundae() passes arguments that never change.

A technique called partial application allows you to bind some of the arguments to a function so that you can provide the others later. Wrap the function you intend to call in a closure and pass the bound arguments.

Consider an ice cream cart which only serves French Vanilla ice cream over Cavendish bananas:

Instead of calling make_sundae() directly, invoke the function reference in $make_cart_sundae and pass only the interesting arguments, without worrying about forgetting the invariants or passing them incorrectlyYou can even use Sub::Install from the CPAN to import this function into another namespace directly..

This is only the start of what you can do with higher order functions. Mark Jason Dominus's Higher Order Perl is the canonical reference on first-class functions and closures in Perl. Read it online at http://hop.perl.plover.com/.

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 3:

A non-empty Z<>

Around line 99:

Deleting unknown formatting code N<>

Around line 215:

A non-empty Z<>

Around line 262:

Deleting unknown formatting code N<>

Around line 270:

Deleting unknown formatting code U<>