HASH::UTIL::FIELDHASH(3perl)
π Hash::Util::FieldHash π
okay bestie, this is the Hash::Util::FieldHash page from the Perl Programmers Reference Guide, and it's about Support for Inside-Out Classes. yes, the vibes are immaculate and the object model is doing a little something different. sit with me. π«Ά
β¨ NAME β¨
Hash::Util::FieldHash β support for inside-out classes. periodt.
π SYNOPSIS π
### Create fieldhashes
use Hash::Util qw(fieldhash fieldhashes);
# Create a single field hash
fieldhash my %foo;
# Create three at once...
fieldhashes \ my(%foo, %bar, %baz);
# ...or any number
fieldhashes @hashrefs;
### Create an idhash and register it for garbage collection
use Hash::Util::FieldHash qw(idhash register);
idhash my %name;
my $object = \ do { my $o };
# register the idhash for garbage collection with $object
register($object, \ %name);
# the following entry will be deleted when $object goes out of scope
$name{$object} = 'John Doe';
### Register an ordinary hash for garbage collection
use Hash::Util::FieldHash qw(id register);
my %name;
my $object = \ do { my $o };
# register the hash %name for garbage collection of $object's id
register $object, \ %name;
# the following entry will be deleted when $object goes out of scope
$name{id $object} = 'John Doe';
π§ FUNCTIONS π§
Hash::Util::FieldHash hands you a whole lil squad of functions in support of "The Inside-out Technique" of class construction. iconic behaviour, honestly.
β id
id($obj)
Returns the reference address of a reference $obj. And if $obj is not a reference? it just returns $obj back to you unchanged. no notes. π
This function is a stand-in replacement for Scalar::Util::refaddr, meaning it returns the reference address of its argument as a numeric value. The only difference is that refaddr() returns undef when you hand it a non-reference, while id() returns the argument unchanged. lowkey a glow up.
id() also uses a caching technique that makes it faster when the id of an object is requested often, but slower if it's only needed once or twice. so like, benchmark if it matters to you, bestie.
πΈ id_2obj
$obj = id_2obj($id)
If $id is the id of a registered object (peep "register" below), it returns the object. otherwise you get an undefined value. for registered objects this is literally the inverse function of id(). slay. β¨
π register
register($obj)
register($obj, @hashrefs)
In the first form, it registers an object to work with for the function id_2obj(). In the second form, it additionally marks the given hashrefs down for garbage collection. What that means: when the object goes out of scope, any entries in the given hashes under the key of id($obj) will be deleted from the hashes. goodbye, data. gone. π
β οΈ It is a fatal error to register a non-reference $obj. any non-hashrefs among the following arguments are silently ignored, no drama.
It is not an error to register the same object multiple times with varying sets of hashrefs. any hashrefs that aren't registered yet will be added, others ignored. very chill of it.
Registry also implies thread support. When a new thread is created, all references are replaced with new ones β including all objects. If a hash uses the reference address of an object as a key, that connection would be broken. but with a registered object, its id will be updated in all hashes registered with it. no cap, that's the save. π₯
π idhash
idhash my %hash
Makes an idhash from the argument, which must be a hash.
An idhash works like a normal hash, except that it stringifies a reference used as a key differently. A reference is stringified as if the id() function had been invoked on it β that is, its reference address in decimal is used as the key. cute, right?
π¦ idhashes
idhashes \ my(%hash, %gnash, %trash)
idhashes \ @hashrefs
Creates many idhashes from its hashref arguments. Returns those arguments that could be converted, or their number in scalar context. mass production era. π
π fieldhash
fieldhash %hash;
Creates a single fieldhash. The argument must be a hash. Returns a reference to the given hash if successful, otherwise nothing.
A fieldhash is, in short, an idhash with auto-registry. When an object (or, indeed, any reference) is used as a fieldhash key, the fieldhash is automatically registered for garbage collection with the object β as if register $obj, \ %fieldhash had been called. it's giving main character energy and it's giving self-care. π
π fieldhashes
fieldhashes @hashrefs;
Creates any number of field hashes. Arguments must be hash references. Returns the converted hashrefs in list context, their number in scalar context. boom. done. β¨
π DESCRIPTION π
A word on terminology, bestie: I shall use the term field for a scalar piece of data that a class associates with an object. Other terms that have been used for this concept are "object variable", "(object) property", "(object) attribute" and more. Especially "attribute" has some currency among Perl programmers, but that clashes with the attributes pragma. The term "field" also has some currency in this sense and doesn't seem to conflict with other Perl terminology. so we're going with field. periodt.
In Perl, an object is a blessed reference. The standard way of associating data with an object is to store the data inside the object's body β that is, the piece of data pointed to by the reference.
In consequence, if two or more classes want to access an object they must agree on the type of reference and also on the organization of data within the object body. Failure to agree on the type results in immediate death when the wrong method tries to access an object. Failure to agree on data organization may lead to one class trampling over the data of another. π actual chaos.
This object model leads to a tight coupling between subclasses. If one class wants to inherit from another (and both classes access object data), the classes must agree about implementation details. Inheritance can only be used among classes that are maintained together, in a single source or not.
In particular, it is not possible to write general-purpose classes in this technique β classes that can advertise themselves as "Put me on your @ISA list and use my methods". If the other class has different ideas about how the object body is used, there is trouble. π
For reference, "Name_hash" in "Example 1" shows the standard implementation of a simple class Name in the well-known hash based way. It also demonstrates the predictable failure to construct a common subclass NamedFile of Name and the class IO::File (whose objects must be globrefs).
Thus, techniques are of interest that store object data not in the object body but some other place. and that's where we're headed. π
π The Inside-out Technique π
With inside-out classes, each class declares a (typically lexical) hash for each field it wants to use. The reference address of an object is used as the hash key. By definition, the reference address is unique to each object, so this guarantees a place for each field that is private to the class and unique to each object. See "Name_id" in "Example 1" for a simple example.
In comparison to the standard implementation where the object is a hash and the fields correspond to hash keys, here the fields correspond to hashes, and the object determines the hash key. Thus the hashes appear to be turned inside out. hence the name, bestie, iykyk. π§
The body of an object is never examined by an inside-out class β only its reference address is used. This allows for the body of an actual object to be anything at all while the object methods of the class still work as designed. This is a key feature of inside-out classes. iconic.
π Problems of Inside-out π
Inside-out classes give us freedom of inheritance, but as usual there is a price. β¨ nothing is free.
Most obviously, there's the necessity of retrieving the reference address of an object for each data access. It's a minor inconvenience, but it does clutter the code. meh.
More important (and less obvious) is the necessity of garbage collection. When a normal object dies, anything stored in the object body is garbage-collected by perl. With inside-out objects, Perl knows nothing about the data stored in field hashes by a class, but these must be deleted when the object goes out of scope. Thus the class must provide a DESTROY method to take care of that.
In the presence of multiple classes it can be non-trivial to make sure that every relevant destructor is called for every object. Perl calls the first one it finds on the inheritance tree (if any) and that's it. and that's it!! π
A related issue is thread-safety. When a new thread is created, the Perl interpreter is cloned, which implies that all reference addresses in use will be replaced with new ones. Thus, if a class tries to access a field of a cloned object, its (cloned) data will still be stored under the now invalid reference address of the original in the parent thread. A general CLONE method must be provided to re-establish the association. no cap, this is a whole situation.
π Solutions π
Hash::Util::FieldHash addresses these issues on several levels.
The id() function is provided in addition to the existing Scalar::Util::refaddr(). Besides its short name it can be a little faster under some circumstances (and a bit slower under others). Benchmark if it matters. The working of id() also allows the use of the class name as a generic object as described further down.
The id() function is incorporated in id hashes in the sense that it is called automatically on every key that is used with the hash. No explicit call is necessary. it just does it. silently. respectfully. π«Ά
The problems of garbage collection and thread safety are both addressed by the function register(). It registers an object together with any number of hashes. Registry means that when the object dies, an entry in any of the hashes under the reference address of this object will be deleted. This guarantees garbage collection in these hashes. It also means that on thread cloning the object's entries in registered hashes will be replaced with updated entries whose key is the cloned object's reference address. Thus the object-data association becomes thread-safe. slay. π₯
Object registry is best done when the object is initialized for use with a class. That way, garbage collection and thread safety are established for every object and every field that is initialized.
Finally, field hashes incorporate all these functions in one package. Besides automatically calling the id() function on every object used as a key, the object is registered with the field hash on first use. Classes based on field hashes are fully garbage-collected and thread safe without further measures. the way that just works. π
π More Problems π
Another problem that occurs with inside-out classes is serialization. Since the object data is not in its usual place, standard routines like Storable::freeze(), Storable::thaw() and Data::Dumper::Dumper() can't deal with it on their own. Both "Data::Dumper" and "Storable" provide the necessary hooks to make things work, but the functions or methods used by the hooks must be provided by each inside-out class. yeah. every single one. good luck bestie. π
Okay so a general solution to the serialization problem would need a whole other level of registry β one that pairs up classes with fields. And rn the functions in Hash::Util::FieldHash genuinely do not know about classes at all, which I lowkey think is a feature?? So Hash::Util::FieldHash just... doesn't deal with the serialization problems. Not its job bestie π
π The Generic Object π
So classes built on the id() function (and like, by extension, classes built on idhash() and fieldhash()) have this kinda quirky behavior where the class name itself can be used like an object. Fr fr. Methods that set or read data tied to an object will just keep working as class methods, exactly as if the class name were an object all of its own, separate from every other object, with its own lil data stash. We call this guy the generic object of the class β¨
And why does this slay? Because field hashes respond to keys that aren't references the way a normal hash would β they just use the string you handed them as the hash key. So when a method gets called as a class method, the field hash gets handed the class name instead of an object... and it just blithely uses it as a key, no questions asked π Since the keys of real actual objects are decimal numbers, there's zero conflict, and that slot in the field hash works like any other slot. The id() function behaves the same way when it gets non-reference arguments.
Two possible uses come to mind (besides just, y'know, ignoring the whole thing):
- A singleton class could be implemented using the generic object. If you need to, an
init()method coulddieor flat-out ignore calls with actual objects (references), so the generic object is the only one that will ever exist π - The generic object could also be a template β a super convenient place to stash class-specific defaults for whatever fields, so you can use them when initializing actual objects.
Usually tho? You can ignore this feature entirely. Calling object methods as class methods normally just leads to an error and nobody uses it routinely anywhere. The only lil gripe is that for a class with a generic object, that error won't get indicated. π
π How to use Field Hashes π
Traditionally, defining an inside-out class means you've got a bare block, and inside that block you declare a bunch of lexical hashes and define your basic accessor methods, usually via Scalar::Util::refaddr. More methods can live outside that block. And you gotta have a DESTROY method, plus a CLONE method if you want thread support.
Now when you use field hashes, the whole basic structure stays the same. But:
- Each lexical hash gets made into a field hash πΈ
- The call to
refaddrcan be yeeted right out of your accessor methods - DESTROY and CLONE methods are not necessary anymore π
If you've already got an inside-out class, just making all the hashes field hashes β with zero other changes β should make no difference at all. Because of those calls to refaddr or its equivalent, the field hashes never actually get to see a reference, so they just act like normal hashes. Your DESTROY (and CLONE) methods are still needed in that case, bestie.
To actually make the field hashes kick in, the easiest move is to redefine refaddr like this:
sub refaddr { shift }
instead of importing it from Scalar::Util. And now it should be possible to disable DESTROY and CLONE π¦ Just note that while it's not disabled, DESTROY will get called before the garbage collection of the field hashes, so it'll be invoked with a functional object and it will continue to function. So like, it works either way, no cap.
It is not desirable to import the functions fieldhash and/or fieldhashes into every single class that's gonna use them. They only get used once, to set up the class. When the class is up and running, these functions serve no more purpose at all. Periodt.
If you've only got a few field hashes to declare, the simplest thing is:
use Hash::Util::FieldHash;
...early on, and then call the functions with full qualification:
Hash::Util::FieldHash::fieldhash my %foo;
Otherwise, import the functions into some convenient package like HUF or, more generally, Aux:
{
package Aux;
use Hash::Util::FieldHash ':all';
}
...and then just call:
Aux::fieldhash my %foo;
...whenever you need it π
π§ Garbage-Collected Hashes π§
Garbage collection in a field hash means entries will "spontaneously" just... vanish when the object that created them disappears. Poof. Gone. π
And you have to keep that in mind, especially when you're looping over a field hash. Because if anything you do inside the loop could cause an object to go out of scope, a random key may get deleted from the hash you're looping over. And that can totally throw off your loop iterator, so the move is to cache a consistent snapshot of the keys and/or values and loop over that instead. You'll still have to check that a cached entry actually still exists when you get to it, tho π
Garbage collection gets extra confusing when keys are created in a field hash from normal scalars AND references. Here's the tea:
Once a reference is used with a field hash, the entry will be collected β even if it was later overwritten with a plain scalar key (and every positive integer is a candidate for that). This is true even if the original entry was deleted in the meantime. π
In fact, deletion from a field hash, and also a test for existence, both count as use in this sense β and they create a liability to delete the entry when the reference goes out of scope. If you happen to create an entry with an identical key from a string or an integer, then that one will get collected instead. So yeah β mixed use of references and plain scalars as field hash keys is not entirely supported. π
β¨ EXAMPLES β¨
okay bestie, these examples are honestly so cute. they build one tiny little class that justβ¦ has a name?? like a first name and a last name, no middle initial (keep it simple, we're not doing middle names today π ). and that class ships with four methods:
init()β an object method that sets the first and last name from its two arguments. if you call it as a class method instead,init()makes you a fresh object in the given class and initializes that one. slay.first()β grab the first name π«Άlast()β grab the last namename()β grab the whole name, first and last joined by a blank space. iconic.
the examples show this exact class implemented with different levels of support by Hash::Util::FieldHash. every supported combo is in here β no gatekeeping β and honestly the difference between the implementations is often tiny. the implementations are:
Name_hashβ a conventional (i.e. not inside-out) implementation where the object is a hash storing the field values, with zero support fromHash::Util::FieldHash. π¨ this one does not allow arbitrary inheritance. rip.Name_idβ inside-out implementation based on theid()function. it needs aDESTROYmethod. for thread support you'd also need aCLONEmethod (not shown here). and btw, instead ofHash::Util::FieldHash::id()you could useScalar::Util::refaddrwith very little functional difference. this is the basic pattern of an inside-out class, the blueprint fr fr.Name_idhashβ idhash-based inside-out implementation. likeName_idit needs aDESTROYmethod and would needCLONEfor thread support.Name_id_regβ inside-out implementation based on theid()function with an explicit object registry. no destructor needed and the objects are thread safe β¨Name_idhash_regβ idhash-based inside-out implementation with explicit object registry. no destructor needed and objects are thread safe.Name_fieldhashβ fieldhash-based inside-out implementation. object registry happens automatically. no destructor needed and objects are thread safe. the glow-up, honestly π
these examples are realized in the code below, which could be copied to a file Example.pm. yes, literally copy-paste it, she's ready to go.
π§ͺ Example 1
use strict; use warnings;
{
package Name_hash; # standard implementation: the
# object is a hash
sub init {
my $obj = shift;
my ($first, $last) = @_;
# create an object if called as class method
$obj = bless {}, $obj unless ref $obj;
$obj->{ first} = $first;
$obj->{ last} = $last;
$obj;
}
sub first { shift()->{ first} }
sub last { shift()->{ last} }
sub name {
my $n = shift;
join ' ' => $n->first, $n->last;
}
}
{
package Name_id;
use Hash::Util::FieldHash qw(id);
my (%first, %last);
sub init {
my $obj = shift;
my ($first, $last) = @_;
# create an object if called as class method
$obj = bless \ my $o, $obj unless ref $obj;
$first{ id $obj} = $first;
$last{ id $obj} = $last;
$obj;
}
sub first { $first{ id shift()} }
sub last { $last{ id shift()} }
sub name {
my $n = shift;
join ' ' => $n->first, $n->last;
}
sub DESTROY {
my $id = id shift;
delete $first{ $id};
delete $last{ $id};
}
}
{
package Name_idhash;
use Hash::Util::FieldHash;
Hash::Util::FieldHash::idhashes( \ my (%first, %last) );
sub init {
my $obj = shift;
my ($first, $last) = @_;
# create an object if called as class method
$obj = bless \ my $o, $obj unless ref $obj;
$first{ $obj} = $first;
$last{ $obj} = $last;
$obj;
}
sub first { $first{ shift()} }
sub last { $last{ shift()} }
sub name {
my $n = shift;
join ' ' => $n->first, $n->last;
}
sub DESTROY {
my $n = shift;
delete $first{ $n};
delete $last{ $n};
}
}
{
package Name_id_reg;
use Hash::Util::FieldHash qw(id register);
my (%first, %last);
sub init {
my $obj = shift;
my ($first, $last) = @_;
# create an object if called as class method
$obj = bless \ my $o, $obj unless ref $obj;
register( $obj, \ (%first, %last) );
$first{ id $obj} = $first;
$last{ id $obj} = $last;
$obj;
}
sub first { $first{ id shift()} }
sub last { $last{ id shift()} }
sub name {
my $n = shift;
join ' ' => $n->first, $n->last;
}
}
{
package Name_idhash_reg;
use Hash::Util::FieldHash qw(register);
Hash::Util::FieldHash::idhashes \ my (%first, %last);
sub init {
my $obj = shift;
my ($first, $last) = @_;
# create an object if called as class method
$obj = bless \ my $o, $obj unless ref $obj;
register( $obj, \ (%first, %last) );
$first{ $obj} = $first;
$last{ $obj} = $last;
$obj;
}
sub first { $first{ shift()} }
sub last { $last{ shift()} }
sub name {
my $n = shift;
join ' ' => $n->first, $n->last;
}
}
{
package Name_fieldhash;
use Hash::Util::FieldHash;
Hash::Util::FieldHash::fieldhashes \ my (%first, %last);
sub init {
my $obj = shift;
my ($first, $last) = @_;
# create an object if called as class method
$obj = bless \ my $o, $obj unless ref $obj;
$first{ $obj} = $first;
$last{ $obj} = $last;
$obj;
}
sub first { $first{ shift()} }
sub last { $last{ shift()} }
sub name {
my $n = shift;
join ' ' => $n->first, $n->last;
}
}
1;
to exercise the various implementations, the script below can be used πββοΈ
it sets up a class Name that is a mirror of one of the implementation classes Name_hash, Name_id, β¦, Name_fieldhash. that's the thing that decides which implementation actually runs.
the script first verifies the function of the Name class. vibe check β
in the second step, the free inheritability of the implementation (or lack thereof π) gets demonstrated. for this purpose it constructs a class called NamedFile, which is a common subclass of Name and the standard class IO::File. this puts inheritability to the test because objects of IO::File must be globrefs. objects of NamedFile should behave like a file opened for reading and also support the name() method. this class juncture works with exception of the Name_hash implementation, where object initialization fails because of the incompatibility of object bodies. yeah. that one just doesn't work here π
π§ͺ Example 2
use strict; use warnings; $| = 1;
use Example;
{
package Name;
use parent 'Name_id'; # define here which implementation to run
}
# Verify that the base package works
my $n = Name->init(qw(Albert Einstein));
print $n->name, "\n";
print "\n";
# Create a named file handle (See definition below)
my $nf = NamedFile->init(qw(/tmp/x Filomena File));
# use as a file handle...
for ( 1 .. 3 ) {
my $l = <$nf>;
print "line $_: $l";
}
# ...and as a Name object
print "...brought to you by ", $nf->name, "\n";
exit;
# Definition of NamedFile
package NamedFile;
use parent 'Name';
use parent 'IO::File';
sub init {
my $obj = shift;
my ($file, $first, $last) = @_;
$obj = $obj->IO::File::new() unless ref $obj;
$obj->open($file) or die "Can't read '$file': $!";
$obj->Name::init($first, $last);
}
__END__
π«§ GUTS π«§
Okay bestie, buckle up, because this is the lore. β¨ To make Hash::Util::FieldHash exist at all, perl itself had to get two little glow-ups:
PERL_MAGIC_uvarbecame available for hashes π- weak references now call uvar
getmagic after a weakref has been cleared
Feature one is what lets field hashes intercept their keys the moment you touch them. Feature two is the one that actually triggers garbage collection. No cap, they need each other. π«Ά
πΈ The PERL_MAGIC_uvar interface for hashes πΈ
PERL_MAGIC_uvar get magic gets called from hv_fetch_common and hv_delete_common through the function hv_magic_uvar_xkey β that function is literally where the interface is defined, it's the main character. π«
The call happens for hashes with uvar magic if the ufuncs structure has equal values in the uf_val and uf_set fields. And hashes are totally unaffected if (and for as long as) those two fields hold different values. So like, mismatch = no vibes, no interference. π
When the call goes down, the mg_obj field will hold the hash key that's being accessed. When it comes back, the SV* value sitting in mg_obj gets used in place of the original key in the hash access. And the integer index value in the first parameter is the action value from hv_fetch_common, or -1 if the call is coming from hv_delete_common. π
Here's a template for a function suitable for the uf_val field in a ufuncs structure for this call. The uf_set and uf_index fields? Irrelevant. Ignore them, they're just there. π
IV watch_key(pTHX_ IV action, SV* field) {
MAGIC* mg = mg_find(field, PERL_MAGIC_uvar);
SV* keysv = mg->mg_obj;
/* Do whatever you need to. If you decide to
supply a different key newkey, return it like this
*/
sv_2mortal(newkey);
mg->mg_obj = newkey;
return 0;
}
π¦ Weakrefs call uvar magic π¦
So when a weak reference gets stored in an SV that's got uvar magic, set magic gets called after the reference has gone stale. Which is genuinely so useful β you can hook onto that to trigger further garbage-collection activities all tied to the referenced object. Obsessed with a good hook. π₯
π How field hashes work π
The three features of key hashes β key replacement, thread support, and garbage collection β are all supported by one data structure called the object registry. It's a private hash where every single object is stored. And an "object" here means any reference (blessed or unblessed) that's been used as a field hash key. Periodt. π
The object registry keeps track of references that have been used as field hash keys. The keys get generated from the reference address, same as in a field hash (though bestie, the registry itself is not a field hash, just so we're clear π). Each value is a weak copy of the original reference, stored in an SV that is itself magical (PERL_MAGIC_uvar again, iconic behavior honestly).
That magical structure holds a list (another hash, really) of the field hashes that the reference has been used with. And when the weakref becomes stale, the magic activates and uses that list to delete the reference from all the field hashes it's been used with. After that, the entry gets removed from the object registry itself. And implicitly β that frees the magic structure and all the storage it's been using. Slay, memory reclaimed. β¨
Whenever a reference gets used as a field hash key, the object registry is checked and a new entry is made if necessary. Then the field hash gets added to the list of fields this reference has used. Simple as that. π
The object registry is also what repairs a field hash after thread cloning. In that case, the entire object registry gets processed β for every reference found in there, the field hashes it's used get visited and the entry gets updated. π
π§ Internal function Hash::Util::FieldHash::_fieldhash π§
# test if %hash is a field hash
my $result = _fieldhash \ %hash, 0;
# make %hash a field hash
my $result = _fieldhash \ %hash, 1;
_fieldhash is the internal function that creates field hashes. It takes two arguments: a hashref and a mode. π«
If the mode is boolean false, the hash isn't changed at all β it's just tested to see if it's a field hash. If it isn't a field hash, the return value is boolean false. If it is, the return value tells you the mode of the field hash. And when you call it with a boolean true mode, it turns the given hash into a field hash of that mode, returning the mode of the field hash it created. Important: _fieldhash does not erase the given hash. Your data stays, bestie. π«Ά
Currently there's only one type of field hash, and only the boolean value of the mode makes any difference β but that may change. So like, lowkey watch this space. π
π AUTHOR π
- Anno Siegel (ANNO) wrote the xs code and the changes in perl proper π
- Jerry Hedden (JDHEDDEN) made it faster β‘
π COPYRIGHT AND LICENSE π
Copyright (C) 2006-2007 by (Anno Siegel)
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.7 or, at your option, any later version of Perl 5 you may have available. π«Ά
and that's the whole page, bestie!! πβ¨π this gorgeous thing is from perl v5.44.0, dated 2026-10-17, and it is literally the Hash::...eldHash(3perl) manual page. read it, love it, use it responsibly. mwah π
πΈπ¦
π¦ where this page comes from
- arch rolling: perl 5.44.0-1 Β· Artistic-1.0-Perl AND GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms