2

I just started learning Moose, and I've created a very basic class. Here is my code:

Person.pm

package Person;

use Moose;

has fname => (
  is => 'rw',
  isa => 'Str',
  reader => 'getFirstName',
);

has lname => (
  is => 'rw',
  isa => 'Str',
  reader => 'getLastName',
  writer => 'setLastName',
);

sub printName {
  my $self = shift;
  print $self->getFirstName() . " " . $self->getLastName(), "\n";
}

no Moose;
__PACKAGE__->meta->make_immutable;

person.pl

#!/usr/bin/env perl

use strict;
use warnings;
use Person;

my $person = Person->new(fname => 'jef', lname => 'blah',);
print $person->fname, $person->lname, "\n";
$person->setLastName('bleh');
$person->getName();

Where this code dies is line 8. It will print out the first name attribute, but it will whine about lname Can't locate object method "lname" via package "Person" at ./person.pl line 8. Now, if I take out the writer in lname, all is fine, but how does that make sense? I realize I can use the getters that I created, but I'm curious why a writer would then deny me access to the attribute itself? I guess I'm not understanding something...

incutonez
  • 3,241
  • 9
  • 43
  • 92
  • I'm going to venture a guess that once you define both a reader and writer, you shouldn't have to access the attribute directly, so access is denied; you should be able to write `print $person->fname, $person->getLastName(), "\n";` – Barton Chittenden Sep 26 '13 at 02:30
  • That's a little confusing though... is that in the documentation? – incutonez Sep 26 '13 at 02:49
  • @incutonez: http://search.cpan.org/dist/Moose/lib/Moose/Manual/Attributes.pod#Accessor_methods – Axeman Sep 26 '13 at 20:19
  • @Axeman: Ah, ok. Thanks for pointing that out. If you include the proper way of accessing my attributes without the custom getter/setter (see my comment on your answer), and then hint at this documentation for when I have custom getters/setters set, then I'll accept your answer. – incutonez Sep 26 '13 at 20:38

1 Answers1

2

lname is not "the attribute itself" because fname is also not "the attribute itself". It, too, is a function that returns the attribute. By writing a reader and writer you are choosing what you prefer these subs to be named, that's all.

Calling a sub by the wrong name fails earlier. The old Perl OO way of blessed hashes, and member fields as hash keys lead to survivable runtime errors when attribute names were typed wrong. The idea behind making subs for accessors is to fail early and completely. As a hash can store any string whatsoever, a blessed object can only call a certain set of functions, either defined for the class or inherited.

According to the Manual,

Each attribute has one or more accessor methods. An accessor lets you read and write the value of that attribute for an object.

By default, the accessor method has the same name as the attribute. If you declared your attribute as ro then your accessor will be read-only. If you declared it as rw, you get a read-write accessor. Simple.

Given our Person example above, we now have a single first_name accessor that can read or write a Person object's first_name attribute's value.

If you want, you can also explicitly specify the method names to be used for reading and writing an attribute's value. This is particularly handy when you'd like an attribute to be publicly readable, but only privately settable. [italics mine]

Community
  • 1
  • 1
Axeman
  • 29,660
  • 2
  • 47
  • 102
  • I'm confused by "is also not the attribute itself"... does this mean I shouldn't be able to call `fname` like I am? And why would adding a writer all of a sudden force me to use the getters/setters instead of accessing the attribute directly? Is this explained somewhere in the documentation? – incutonez Sep 26 '13 at 02:41
  • 1
    `$person->lname` is actually a combined reader/writer created for you by Moose. The attribute itself is stored in `$person->{'lname'}`. As soon as you declare both a reader and a writer, Moose decides that you no longer need a combined reader/writer, and doesn't create the `lname` method. – AKHolland Sep 26 '13 at 14:04
  • Oh wow! That was really really stupid of me... so my getter/setter is set by default, and it's using the same name as my attribute, so if I didn't actually have a custom getter/setter name set, I can call it by `$person->lname()` and `$person->lname('bleh')`. However, these go away when I set my own getter/setter. That's cute. Thanks for the help! – incutonez Sep 26 '13 at 20:36
  • @AKHolland that should've been an answer, not comment. – Dallaylaen Sep 27 '13 at 15:51