this presumes a hash-based object instead of a field of a hash-based object, but it can have blessed members unlike Chris Dolan's draft; it also takes multiple args to be slice keys rather than consecutive accesses into a deep AoA. Wrapping in an import function and the niftiness with closing over $fieldname has been left out,
UNTESTED. package autoaccessor::ReadOnly; sub new { shift; # lose package name bless shift #return what we were given, it had better be a hashref } sub AUTOLOAD{ our $AUTOLOAD; $AUTOLOAD =~ /::DESTROY$/ and return; my ($fn) = $AUTOLOAD =~ m/([^:]+)\z/; my $obj = shift; # closed-over fieldname trick goes here my @args = @_; # without args, presume hash lookup of $fn @args or return $obj->{$fn}; # try access styles to see what works my @retval; if (wantarray){ eval [EMAIL PROTECTED] = @{$obj->[EMAIL PROTECTED];1} and return @retval; eval [EMAIL PROTECTED] = @{$obj->[EMAIL PROTECTED];1} and return @retval; }else{ eval [EMAIL PROTECTED] = @{$obj->[EMAIL PROTECTED];1} and return $retval[0]; eval [EMAIL PROTECTED] = @{$obj->[EMAIL PROTECTED];1} and return $retval[0]; } Carp::croak( "Cannot figure out how to access $fn member of $obj") } 1; __END__ =pod Andy A's initial example would become package main; my $my_foo = new autoaccessor::ReadOnly { foo => [ 1, 2, 3 ], bar => { eat => 'drink', sleep => 'wake' }, }; my $two = $my_foo->foo( 1 ); my $drink = $my_foo->bar->eat; =cut