examples/oop/person06/script/person.pl
use strict;
use warnings;
use v5.10;

use Person;

my $student = Person->new( fname => 'Foo', lname => 'Bar' );
say $student->fname; # Foo
say $student->lname; # Bar

$student->lname('Bar-Yosef');
say $student->lname; # Bar-Yosef

$student->fname('Zorg');
say $student->fname; # Foo   (did not change!)
examples/oop/person06/lib/Person.pm
package Person;
use strict;
use warnings;

sub new {
    my ($class, %args) = @_;

    my $self = \%args;

    bless $self, $class;

    return $self;
}

sub lname {
    my ($self, $value) = @_;
    if (@_ == 2) {
        $self->{lname} = $value;
    }

    return $self->{lname};
}

sub fname {
    my ($self) = @_;

    return $self->{fname};
}


1;

The read-only getter could actually throw an exception when it is used as a setter.