Writing to read-only attributes inside the Perl Moose class

Using Perl and Moose , object data can be accessed in two ways.

$self->{attribute} or $self->attribute()

Here is a simple example demonstrating both:

 # Person.pm package Person; use strict; use warnings; use Moose; has 'name' => (is => 'rw', isa => 'Str'); has 'age' => (is => 'ro', isa => 'Int'); sub HAPPY_BIRTHDAY { my $self = shift; $self->{age}++; # Age is accessed through method 1 } sub HAPPY_BIRTHDAY2 { my $self = shift; my $age = $self->age(); $self->age($age + 1); # Age is accessed through method 2 (this will fail) } 1; 

 # test.pl #!/usr/bin/perl use strict; use warnings; use Person; my $person = Person->new( name => 'Joe', age => 23, ); print $person->age()."\n"; $person->HAPPY_BIRTHDAY(); print $person->age()."\n"; $person->HAPPY_BIRTHDAY2(); print $person->age()."\n"; 

I know that when you are outside the Person.pm file, it is better to use the version of $person->age() , as it prevents you from making dumb mistakes and will not allow you to overwrite a read-only value, but my question is ...

Inside from Person.pm it best to use $self->{age} or $self->age() ? Is it a bad practice to overwrite the read-only attribute inside the module itself?

Should this attribute be changed to a read / write attribute if its value is ever expected to be changed or is considered acceptable to override the read-only attribute aspect with $self->{age} in the HAPPY_BIRTHDAY function?

+6
source share
3 answers

When using Musa, the best practice is to always use the generated access methods, even if inside the object’s own class. Here are a few reasons:

  • Access methods can be overloaded with a child class that does something special. Calling $self->age() ensures that the correct method will be called.

  • There may be method modifiers, such as before or after , attached to an attribute. Access to the hash value will be skipped directly.

  • A predicate or a clearer method (for example, has_age ) can be applied to an attribute. Messaging with a hash value will directly confuse them.

  • Hash keys are subject to typos. If you accidentally say $self->{aeg} , the error will not be detected immediately. But $self->aeg will die, since the method does not exist.

  • The consistency is good. There is no reason to use one style in one place and another style in another place. This makes it easier to understand the code for newbs.

In the specific case of the read-only attribute, here are a few strategies:

  • Make your objects truly immutable. If you need to change the value, create a new object that is a clone of the old with the new value.

  • Use the read-only attribute to store real age and specify the method of a private author.

For instance:

 package Person; use Moose; has age => ( is => 'ro', isa => 'Int', writer => '_set_age' ); sub HAPPY_BIRTHDAY { my $self = shift; $self->_set_age( $self->age + 1 ); } 

Update

Here is an example of how you can use the lazy builder to set one attribute based on another.

 package Person; use Moose; has age => ( is => 'rw', isa => 'Int', lazy => 1, builder => '_build_age' ); has is_baby => ( is => 'rw', isa => 'Bool', required => 1 ); sub _build_age { my $self = shift; return $self->is_baby ? 1 : 52 } 

The lazy builder is not called until age is available, so you can be sure that is_baby will be is_baby .

Setting a hash element directly will, of course, skip the build method.

+7
source

I do not think that $self->{age} is a documented interface, so it is not even guaranteed to work.

In this case, I would use a private writer, as described in https://metacpan.org/pod/Moose::Manual::Attributes#Accessor-methods :

 has 'weight' => ( is => 'ro', writer => '_set_weight', ); 

You can even automate this with 'rwp' from https://metacpan.org/pod/MooseX::AttributeShortcuts#is-rwp :

 use MooseX::AttributeShortcuts; has 'weight' => ( is => 'rwp', ); 
+4
source

The perl source code is not type safe and does not have a lot of encapsulation, so it’s easy to do reckless things. Moose imposes some civilization on your perl object, exchanging security and stability for some freedom. If Mus becomes too stuffy, basic Perl still exists, so there are ways to get around any laws that Mus’s iron fist is trying to catch.

After you wrapped your head in the fact that you declared the attribute read-only, but want to change it, although you also said that you want it to be read-only, and in most universes you declare something to read only because you don’t want to change it, then be sure to update $person->{age} . In the end, you know what you are doing.

+1
source

All Articles