dcsimg

Fit To Be Tied, Part One

You may have heard the term tied variable before, especially if you’ve accessed a DBM- based hash, but what is a tied variable and why would you use one? This month and next, let’s take a look.
You may have heard the term tied variable before, especially if you’ve accessed a DBM- based hash, but what is a tied variable and why would you use one? This month and next, let’s take a look.
A tied variable is a variable that presents a relatively normal outward appearance to the rest of your Perl program but has additional wiring on the inside. Specifically, nearly all of the operations on the variable (fetching its value, updating a hash element, getting the length of an array) trigger subroutine calls that replace Perl’s normal internal operations on variables. The subroutines can perform anything you wish, although each one must return an appropriate value if the corresponding outward action is looking for one.
For example, you could make $x tied to make it persist: every time you stored a value into $x, the value would be written to an external file, and every time you fetched its value, the external file would be consulted. By the end of this article, you should be able to write that very code from scratch, but for now, let’s borrow Tie::Persistent, an implementation of the same idea found in the CPAN, as an illustration.
This code persists changes to $x into myfile:
use Tie::Persistent;
tie my $x, ’Tie::Persistent’, ’myfile’;
$x = 35; # writes to myfile
my $y = $x; # reads from myfile, returning 35

Tying One On

A tied variable is implemented using Perl objects, so if you’re not familiar with those, go study up. It’s OK, I’ll wait here. Alright then.
You can implement a class like Tie::Persistent by consulting the perltie man page. There, you’ll find that to tie a scalar variable, you need to implement the TIESCALAR(), FETCH(), and STORE() methods, at a minimum.
How does tie work? First, Perl turns the initial tie operation, such as…
tie $x, MyTie, $a, $b, @c;
… into the equivalent class method call of:
MyTie->TIESCALAR($a, $b, @c);
That means that you need to write a TIESCALAR() method for your package. But what should the method return? For a tied variable to work, the TIESCALAR() method must return a Perl object (a blessed reference). And any object will do — it doesn’t need to be of the same “type” as the variable being tied.
But for grins, let’s make MyTie create a hash-based object, initializing the key of Value to the first parameter passed to TIESCALAR(), if any:
package MyTie;
sub TIESCALAR {
my $class = shift; # probably MyTie
my %self;
$self{Value} = shift; # first parameter to tie
return bless \%self, $class;
}
But where does this object go? It’s returned as the result of the tie operator, although that value is rarely captured. But it also gets magically associated with the scalar variable being tied. So, if you tie $x, you have a “secret object” associated with $x until you untie the variable (or if it goes out of scope, which is discussed shortly).
You can get to the secret object with the tied operator:
my $secret_object = tied $x;
Given that, you can probably guess what happens when you try to fetch the current value of $x. Perl transforms…
$y = $x;
… into:
$y = tied($x)->FETCH;
In other words, the FETCH() instance method is called on the secret object behind $x. Let’s write a simple FETCH() method that simply returns the Value in our secret hash object:
package MyTie;
sub FETCH {
my $self = shift; # the secret object
return $self->{Value};
}
And now, you have the beginnings of a tied mechanism:
use MyTie;
tie $x, MyTie, 45; # calls MyTie->TIESCALAR(45)
$y = $x; # calls tied($x)->FETCH returning 45
The other thing a scalar variable can do is get a new value. When you store something, as in…
$x = 28;
… Perl turns that into:
tied($x)->STORE(28);
In other words, you’re calling yet another method on the secret object, passing it the updated value. Let’s write a simple method to update the Value element:
package MyTie;
sub STORE {
my $self = shift; # the secret object
my $newvalue = shift;
$self->{Value} = $newvalue;
}
The value returned by a STORE() is ignored.
So far, this new scalar does what all scalars do, albeit a bit slower, and with a lot more storage required. But let’s take it a step further. Let’s modify the variable so that it keeps track of every value it has ever been assigned and then dumps that history when the variable goes out of scope. To do that, hook into the DESTROY() method for the object, which gets called as a variable is reverting back to its former style as part of going out of scope.
First, just add the debug message:
package MyTie;
sub DESTROY {
my $self = shift;
warn "deleting with value $self->{Value}\n";
}
That gives a notification as the variable is going away. You can verify that with a simple program. Next, let’s modify the STORE() method to keep a history of the values, and modify TIESCALAR() to initialize the history as well as the initial value:
package MyHistoricalTie;
sub TIESCALAR {
my $class = shift; # probably MyHistoricalTie
my $self = { Value => shift, History => [] };
return bless $self, $class;
}
sub STORE {
my $self = shift; # the secret object
my $newvalue = shift;
push @{$self->{History}}, $self->{Value};
$self->{Value} = $newvalue;
}
Here, an additional History element is added in the hash, initialized to an empty arrayref. As each new value is stored into the tied variable, the previous Value element of this hash is shoved onto the end of that array, and the new value is put in its place. Finally, update the DESTROY() method to show the results of the new handiwork:
package MyHistoricalTie;
sub DESTROY {
my $self = shift;
my @history = @{$self->{History}};
warn "deleting with value $self->{Value}",
(@history ? " and history @history" : ""), "\n";
}
And now, the following program…
tie $x, MyHistoricalTie, 32;
print $x, "\n";
$x = 45;
$x = 60;
$x = 95;
print $x, "\n";
… yields output that looks like:
32
95
deleting with value 95 and history 32 45 60

Ties for Advanced Sailors

What if you wanted to access that history before the end of the program? You can add additional methods into the tie class. Let’s add get_history() to return the historical values, including the current value, as a list:
package MyHistoricalTie;
sub get_history {
my $self = shift;
@{$self->{History}}, $self->{Value};
}
To call this method on an instance, speciifically the secret object, use tied again:
tie $x, MyHistoricalTie, 32;
$x = 45;
$x = 60;
$x = 95;
print join(", ", tied($x)->get_history), "\n"
And, in fact, this prints:
32, 45, 60, 95
In this way, you can create additional methods on the secret object, and perform” meta” operations on the tied variable.
You can disassociate the variable from all of its magical properties using untie:
untie $x;
At this point, the secret object is called with an UNTIE() method, if it exists. There’s no harm if it doesn’t. Then, the secret object is disconnected from the tied variable. If that was the last reference for the object, the DESTROY() method is also called. However, if there is still another reference to the secret object, you get a warning like:
untie attempted while 1 inner references still exist
This happens if you save the result of tied (or the initial tie) somewhere else, as in:
tie my $x ...;
my $secret_object = tied $x;
untie $x;
Here, a warning is issued because the reference in $secret_object lives on after the variable has been untied.
When a variable goes out of scope, Perl performs an implicit untie without calling the UNTIE() method. So, if you have any last requests for your object’s data, you should put it into the DELETE() method instead.
The new scalar created as a tied scalar is now sensitive to fetches and stores and going out of scope. But Perl tries really hard to make this work transparently.
For example:
tie my $x, ...;
some_sub(\$x); # pass a reference into the subroutine
sub some_sub {
my $scalarref = shift;
my $y = $$scalarref; # triggers the FETCH on $x
$$scalarref = 34; # triggers the STORE on $x
}
Anything that would have been a fetch or store on the original variable is properly translated. Even something like…
$_ = 19 for $x, $y, $z;
… causes the store method to be triggered on $x (and $y and $z if they also are tied).

Persistence Is A Virtue

Let’s implement that persistent variable now, given what’s been shown so far. Let’s have it fetch the old value on getting tied, and store the new value when the secret object is deleted. Other fetches and stores will work against an in-memory copy for speed. Furthermore, let’s use Storable to ensure that the data is written as accurately as possible.
package MyPersist;
sub TIESCALAR {
my $class = shift;
my $file = shift or die "missing filename";
require Storable;
my $self = {
File => $file,
Value => (-r $file and Storable::retrieve($file)),
};
return bless $self, $class;
}
To use the new tie, call it as tie my$p, MyPersist,’somefile’;. The filename is saved into $file, and the hash is constructed with File and Value elements. To write the value back out, hook in to the DELETE() method:
package MyPersist;
sub DELETE {
my $self = shift;
Storable::nstore($self->{Value}, $self->{File});
}
And now, just make fetches and stores do the right thing:
package MyPersist;
sub FETCH {
return shift->{Value};
}
sub STORE {
shift->{Value} = shift;
}
These are the same as the versions for MyTie, but have been shortened by eliminating the extra variables (for grins). Now, you have a persistent value, albeit only a scalar:
use MyPersist;
tie my $p, MyPersist, ’somefile’;
print ++$p;
If you run this program repeatedly, it prints 1, 2, 3, 4, and so on.
Well, I’ve run out of room this month, and I didn’t even get to talk about all the other things that can be tied: arrays, hashes, and even filehandles. So, look forward to that for next month.
Until then, enjoy!

Randal Schwartz is the chief Perl guru at Stonehenge Consulting.

Comments are closed.