http://qs321.pair.com?node_id=1221954

Trigger

This meditation was triggered by Re^3: How to tie a hash to a class.

The Problem

Why can we do this ...

package My::Hash; use strict; use warnings; use parent 'Tie::Hash'; 1;

... but not this?

package My::Hash; use strict; use warnings; use parent 'Tie::StdHash'; 1;

Why do we have to write this instead?

package My::Hash; use strict; use warnings; use Tie::Hash (); our @ISA='Tie::StdHash'; 1;

Or, even worse, this, following the documentation found in Tie::Hash?

package My::Hash; # NO use strict; or things will break! use warnings; require Tie::Hash; @ISA='Tie::StdHash'; 1;

A little bit of history

Well, at some point in time, someone must have thought it was clever to put all three classes Tie::Hash, Tie::StdHash, and Tie::ExtraHash into the same file, Tie/Hash.pm. Looking at https://perl5.git.perl.org/perl.git/history/HEAD:/lib/Tie/Hash.pm, that must have happened before 1996-02-03. So this oddity is embedded deep in Perl's history.

At that time, Tie::Hash was still named TieHash, and Tie::StdHash was still named TieHash::Std. Tie::ExtraHash did not exist. And for reasons that are not clear to me, TieHash::Std inherited from TieHash. TieHash::Std reimplemented each and every method, and so only new() was really inherited from TieHash. That is the most useless method in the entire file, because tie never calls it. The TieHash::TIEHASH() constructor adds some extra work just to check for a new() implementation, and that's still there.

Three days after the first version, the classes were renamed to their current names.

Six years later, Tie::ExtraHash appeared in the same file, and it did NOT inherit from Tie::Hash. At the same time, Tie::StdHash stopped inheriting the nonsense new() constructor from Tie::Hash:

# @ISA = qw(Tie::Hash); # would inherit new() only

In all of the following versions up to the latest from 2013, Tie::ExtraHash never started inherited from Tie::Hash, and the @ISA line in Tie::StdHash was never commented back in.

Cleaning up the mess

First, Tie::ExtraHash

As shown, Tie::ExtraHash never inherited from Tie::Hash, so it is completely safe to move it into a separate file. Just cut the last 13 lines from Tie/Hash.pm and paste them into a new file Tie/ExtraHash.pm:

package Tie::ExtraHash; sub TIEHASH { my $p = shift; bless [{}, @_], $p } sub STORE { $_[0][0]{$_[1]} = $_[2] } sub FETCH { $_[0][0]{$_[1]} } sub FIRSTKEY { my $a = scalar keys %{$_[0][0]}; each %{$_[0][0]} } sub NEXTKEY { each %{$_[0][0]} } sub EXISTS { exists $_[0][0]->{$_[1]} } sub DELETE { delete $_[0][0]->{$_[1]} } sub CLEAR { %{$_[0][0]} = () } sub SCALAR { scalar %{$_[0][0]} } 1;

Then, Tie::StdHash

For the last 16 years, it seems that nobody complained about Tie::StdHash no longer inheriting the useless new() from Tie::Hash. So I am very sure that we can also move that class safely out of Tie/Hash.pm. From the edited Tie/Hash.pm, cut the last 17 lines and paste them into a new file Tie/StdHash.pm. Add a trailing 1; and remove the @ISA line completely:

# The Tie::StdHash package implements standard perl hash behaviour. # It exists to act as a base class for classes which only wish to # alter some parts of their behaviour. package Tie::StdHash; sub TIEHASH { bless {}, $_[0] } sub STORE { $_[0]->{$_[1]} = $_[2] } sub FETCH { $_[0]->{$_[1]} } sub FIRSTKEY { my $a = scalar keys %{$_[0]}; each %{$_[0]} } sub NEXTKEY { each %{$_[0]} } sub EXISTS { exists $_[0]->{$_[1]} } sub DELETE { delete $_[0]->{$_[1]} } sub CLEAR { %{$_[0]} = () } sub SCALAR { scalar %{$_[0]} } 1;

Third, Tie::Hash

The most obvious problem first: Append a trailing 1; or else loading Tie::Hash will fail.

Now that we have cleaned up everything, legacy knocks at the door, complaining about modules failing to inherit from Tie::StdHash after loading only Tie::Hash. So, we have to load the two modules, just to avoid legacy trouble. Adding two use lines is easy:

use Tie::StdHash; use Tie::ExtraHash;

A little bit of cleaning up

Both Tie::StdHash and Tie::ExtraHash lack a version number, so add our $VERSION = '1.06' to both files, and bump $VERSION in Tie::Hash to 1.06, too.

All three classes should use strict; use warnings;.

The POD

Tie::StdHash and Tie::ExtraHash have no POD at all. We could work around with something like this:

=head1 NAME Tie::StdHash =head1 SYNOPSIS See L<Tie::Hash> =head1 DESCRIPTION See L<Tie::Hash> =cut

A Patch

Instead of adding workarounds, we could clean up the POD in Tie::Hash, move the parts documenting Tie::StdHash and Tie::ExtraHash into the respective files, and clean up the examples to 21st century Perl.

Here is a diff, based on the current HEAD:

Should anyone get paranoid about legal stuff: This diff is free software, use it under the same license as Perl (perlartistic).

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)