Closures, Late Binding, and Abstractions

One of my client projects has suffered from running code too quickly (go Perl!) and too much in parallel (go Unix processes!) and has kept me tuning the transactional model of the backend storage.

I'm not worried about inconsistencies, but rather detecting and avoiding lock contention where possible, rescheduling transactions where lock contention does occur, and above all, wrapping transactions in the smallest units possible.

The fantastic (but not for all projects) KiokuDB has been very useful for this project. If you use it with a transactional backend, it provides a DBI-inspired method to invoke a function reference in its own transaction:

$dir->txn_do( sub { $dir->delete( @args ) } );

I was glad of the ability to pass around closures in a lightweight manner; delaying computation until KiokuDB has set up the transaction is very useful. Even so, I was less pleased with all of the syntactic noise littering my code in several places.

That's because I wasn't taking full advantage of the abstraction possibilities of late binding.

Now I have instead:

use Try::Tiny;

sub do_txn
    my ($self, $method, @args) = @_;

    my $dir    = $self->dir;
    my $sub    = sub { $dir->$method( @args ) };

    while ( ... )
        try   { $dir->txn_do( $sub ) }
        catch { ... }


... and I can call it with:

$self->do_txn( add    => $new_obj  );
$self->do_txn( delete => @args     );
$self->do_txn( update => $invocant );

... to remove visual clutter from other parts of the code. Better yet, all of the retrying semantics are in one place, and I can add logging or tuning there.

I hoisted the creation of the closure passed to txn_do() out of the while loop for two reasons. Primarily, I believe doing so makes the code within the loop clearer. It's also slightly more efficient (slightly) to create the closure once than on every trip through the loop. (If efficiency were of greater concern—and lock contention is much more troublesome here—I could pre-resolve the method and bind to the function reference representing the candidate method first instead of the name of the method, but that would add at least one line of code and possibly a few more for error checking, and it's not worthwhile yet.)

Despite Perl 5's support for pervasive and relatively lightweight closures, sometimes they're not the best abstraction to use if your primary concern is code clarity. I believe the resulting code is much clearer (even if do_txn() isn't the right name).

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide



About this Entry

This page contains a single entry by chromatic published on October 21, 2010 12:33 PM.

When You Know Most about What You Need Most was the previous entry in this blog.

Reinventing the Axle is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Powered by the Perl programming language

what is programming?