Skip to content

Perl Moo attributes: hash and array

I am using modern Oriented Object approaches based on Moo – Minimalist Object Orientation (with Moose compatibility) to build a complex API framework integrated with cached systems, event queues, etc. Moo is a subset/fork/smaller/light version of Moose – A postmodern object system for Perl 5 – and is very fast, compared to Moose footprint.

Gabor Szabo, from PerlMaven.org, wrote a very cool tutorial on how to use Moo, and Ricardo Signes (Moose and many different modules developer) has an amazing 3-hour tutorial on how to use Moose. Both of them helped me a lot to understand and to migrate my old OO Perl code to this new approach.

To be honest, first time I used Moo, I found really awkward  how Hash or Array are created as Object’s attribute. Moo has no formal Type (Int, String, etc..), as Moose does. Thus, to create a hash or arrays as attributes, we need to establish a Anonymous Function to it, kind of lambda in Pyhon.

Defining Arrays:

has os_array => (
 is => 'rw',
 default => sub { [] },
);

We have defined an attribute named as `os_array` that its `default` value is a anonymous function to a array reference. I assume that, if you have a better explanation, please let me know.

I found two ways out of billion (TMTOWTDI  –  ;p) to initiate this attribute. First is providing an array reference when Object is created:

package MooArray;

use Moo;
use feature 'say';
use Data::Dumper;

has os_array => (
 is => 'rw',
 default => sub { [] },
);

1;
no Moo;

#### Main code

my @array_ref = qw(Linux FreeBSD OpenBSD);

my $z = MooArray->new( 
 os_array => \@array_ref
);

say Dumper $z->os_array;

# Array operations
push @{$z->os_array}, 'Solaris';

foreach my $el ( @{$z->os_array} ) {
 say "Neo knows: ", $el;
}

say Dumper $z->os_array;

Which give us:

$VAR1 = [
 'Linux',
 'FreeBSD',
 'OpenBSD'
 ];

Neo knows: Linux
Neo knows: FreeBSD
Neo knows: OpenBSD
Neo knows: Solaris
$VAR1 = [
 'Linux',
 'FreeBSD',
 'OpenBSD',
 'Solaris'
 ];

Second approach is to access Object’s attribute directly:

package MooArray;

use Moo;
use feature 'say';
use Data::Dumper;

has os_array => (
 is => 'rw',
 default => sub { [] },
);

1;
no Moo;

#### Main code

my @array_ref = qw(Linux FreeBSD OpenBSD);

my $z = MooArray->new( );

say Dumper $z->os_array;

# Array operations
push @{$z->os_array}, 'Solaris';
foreach my $el ( @{$z->os_array} ) {
 say "Neo knows: ", $el;
}

say Dumper $z->os_array;

Providing us the following output:

$VAR1 = [];

Neo knows: Solaris
$VAR1 = [
 'Solaris'
 ];

Seems to be easy? Let’s see an example when we use array reference inside a function block:

package MooArray;

use Moo;
use feature 'say';
use Data::Dumper;

has os_array => (
 is => 'rw',
 default => sub { [] },
);

1;
no Moo;

#### Main code

my $z = MooArray->new( );
say Dumper $z->os_array;

sub my_scope {
 my @array_ref = qw(Linux FreeBSD OpenBSD);
 $z->os_array(\@array_ref);
}

my_scope();
say Dumper $z->os_array;

# Array operations
push @{$z->os_array}, 'Solaris';
foreach my $el ( @{$z->os_array} ) {
 say "Neo knows: ", $el;
}

say Dumper $z->os_array;

In both Moo or Moose, setters and getters are defined by default, which means that when we use $z->os_array(\@array_ref); we are using `os_array` setter  to set `array_ref` content.

Defining Hashes

When defining hashes, syntax gets bit difference:

package MooHash;

use Moo;
use feature 'say';
use Data::Dumper;

has os_hash => (
 is => 'rw',
 default => sub { {} },
);

sub set_me {
 my $self = shift;

my %age = (
 Linux => '1998',
 FreeBSD => '2010',
 );

$self->os_hash(\%age);
}

1;
no Moo;

#### Main code
my $z = MooHash->new();

$z->set_me();

say Dumper $z->os_hash;

#Hash operations
$z->os_hash->{Solaris} = 2004;
say Dumper $z->os_hash;

while ( my ($key, $value) = each %{$z->os_hash} ) {
 say "Running " . $key . " since: " . $value;
}

Showing the following output:

$VAR1 = {
 'FreeBSD' => '2010',
 'Linux' => '1998'
 };

$VAR1 = {
 'FreeBSD' => '2010',
 'Linux' => '1998',
 'Solaris' => 2004
 };

Running FreeBSD since: 2010
Running Linux since: 1998
Running Solaris since: 2004

When we use settters  $self->os_hash(\%age); to set attribute’s content, we have 3 different ways to declare hash attributes:

using {} operator:

has os_hash => (
 is => 'rw',
 default => sub { {} },
);

Using ( ) operator:

has os_hash => (
 is => 'rw',
 default => sub { () },
);

and

has os_hash => (
 is => 'rw',
 default => sub { },
);

In case we do not use setter, we can define an attribute:

package MooHash;

use Moo;
use feature 'say';
use Data::Dumper;

has os_hash => (
 is => 'rw',
 default => sub { {} },
);

1;
no Moo;

#### Main code
my $z = MooHash->new();

say Dumper $z->os_hash;

#Hash operations
$z->os_hash->{Solaris} = 2004;
say Dumper $z->os_hash;

while ( my ($key, $value) = each %{$z->os_hash} ) {
 say "Running " . $key . " since: " . $value;
}

Final words

If you need a stronger Type validation, please use Moose.

In Moo, since we are using an anonymous function (a pointer to something), any reference given to that attribute will behave as its reference. For instance, If my reference is an array, my attribute is array, however, when my reference is an hash, than I will have a hash attribute. I am not sure if this is a bug or not! (let me know).

The following code declares a hash attribute, but when Object is created an array is given as reference.

package MooHash;

use Moo;
use feature 'say';
use Data::Dumper;

has os_hash => (
 is => 'rw',
 default => sub { {} },
);

1;
no Moo;

#### Main code
my @a = qw(foo bar);

my $z = MooHash->new(
 os_hash => \@a,
);

say Dumper $z->os_hash;
say $z->os_hash->[0];

 

Which gives us:

$VAR1 = [
 'foo',
 'bar'
 ];

foo

References:

1 – Anonymous Functions:

http://modernperlbooks.com/books/modern_perl/chapter_05.html

 

2 Comments

  1. choroba choroba

    See Type::Tiny on how to get Moose-like types into Moo. Also, for attributes that behave like arrays or hashes in Moose, see Moose::Meta::Attribute::Native.

    • Thanks,

      Since I have been coding in Perl OO old-style, using external modules such as Type::Tiny is kind of new to me. Sometimes it takes me a lot of time to figure out how to glue them together.

      I will read more about this Moose::Meta::Attribute::Native module.

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload the CAPTCHA.