Yenya's World

Thu, 22 Sep 2005

Inside-out objects in Perl

In Perl, objects are represented as blessed references. The traditional approach is to use the hash reference, with object attributes represented as members of this hash:

package Beverage::Whisky;

sub new {
	my $class = shift;
	my %args = @_;
	my $self = bless { }, $class;

	$self->{name} = $args{name};
	$self->{age} = $args{age};

	return $self;

sub get_age {
	my $self = shift;
	return $self->{aqe};

# ... etc ..., and then somewhere else

my $bottle = Beverage::Whisky->new(name => 'Laphroaig', age => 10);
print 'This bottle is aged ', $bottle->get_age(), " years\n";

The main problem with this approach is that when you make a typo (did you notice that I accidentally wrote aqe instead of age in the get_age() method?), new hash member is silently added and no error is reported. Another problem is that nobody can stop the module/class user to access the attributes directly and possibly mess up the internal state of the object.

In Perl Best Pracices Damian Conway suggests that "inside-out" objects should be used instead. I.e. the objects where the attributes are not stored in the object itself, but referenced from the global hash defined in the class itself:

package Beverage::Whisky:

use Class:Std::Utils;

   my (%name_of, %age_of);

   sub new {
	my $class = shift;
	my %args = @_;
	my $self = bless \do { my $anon_scalar; }, $class;

	$name_of{ident $self} = $args{name};
	$age_of{ident $self} = $args{age};

	return $self;

   sub get_age {
	my $self = shift;
	return $aqe_of{ident $self};

Note that this approach is not any longer than the previous version, and a similar typo in get_age() method above leads to an immediate compile-time failure. The object itself is a blessed reference to an empty scalar, which is hard to make a mess of. The per-class attribute hashes are in a local block, so they cannot be accessed directly from the outside of this block.

However, there is something missing from the "inside-out" code, and adding this voids Damian Conway's claim about the same code size:

    sub DESTROY {
	my $self = shift;
	delete $name_of{ident $self};
	delete $age_of{ident $self};

Without the destructor each object would leak memory used for storing its attributes. But with the destructor the code is longer and thus more error-prone. The "inside-out" approach has also serious drawback in threaded code - while the traditional approach requires no locking in the methods itself, provided that one object is accessed by one thread only, the "inside-out" approach requires access to the per-class global hashes, which implies locking and a potential performance penalty.

In my opinion the inside-out approach is interesting, but it has serious drawbacks which prevent me from using it instead of the traditional "blessed hash reference" approach. I don't need to enforce the encapsulation of objects anywhere else than in the documentation. I just want the typos in the attribute name show up either immediately (preferred) or in the run-time. Is there a better approach to objects in Perl, which would solve this problem? Maybe locked hash keys (as provided by the Hash::Util module) is what I need. Any other recommendations?

Section: /computers (RSS feed) | Permanent link | 4 writebacks

4 replies for this story:

Honza Holčapek wrote:

Pseudohashes, either via use fields or as is.

Yenya wrote: Pseudohashes are deprecated

In Perl BP, Damian Conway wrote: "Pseudohashes were a mistake.", and continues that it slows down access to normal hashes, and that it is a deprecated feature in 5.8 which will be removed in 5.10.

Jerry D. Hedden wrote: Object::InsideOut

Object::InsideOut is a new module that also provides comprehensive support for inside-out objects. It is faster, more flexible, supports threads including the sharing of objects between threads using threads::shared, and is usable under mod_perl.

Honza Holcapek wrote: fields pragma

man fields, description of 'new' function

Reply to this story:

URL/Email: [http://... or mailto:you@wherever] (optional)
Title: (optional)
Key image: key image (valid for an hour only)
Key value: (to verify you are not a bot)


Yenya's World: Linux and beyond - Yenya's blog.


RSS feed

Jan "Yenya" Kasprzak

The main page of this blog



Blog roll:

alphabetically :-)