http://www.perlmonks.org?node_id=1052095

paulymer has asked for the wisdom of the Perl Monks concerning the following question:

Dear all,

I would like to use a 2d multidimensional array in Moose, but have a few questions. What is the proper way to build a two-dimensional array? In particular, I would like an array-of-arrays-of-objects which has a type 'ArrayRef[ArrayRef[Object]]'. Manipulating the outer dimension is easy enough through the native 'Array' methods such as push, pop, exists, etc. But the inner dimension is a little more tricky and I haven't found any information in my searches how to properly handle the inner arrays of objects. Here is what I have found:

#!/opt/local/bin/perl -w { package Obj; use Moose; has 'name' => ( is => 'rw', isa => 'Str', default => '', ); sub print { my $self = shift; print $self->name; } no Moose; __PACKAGE__->meta->make_immutable; } { package AoAoObj; use Moose; use Moose::Util::TypeConstraints; has 'arr2d' => ( traits => ['Array'], is => 'ro', isa => 'ArrayRef[ArrayRef[Obj]]', required => 1, default => sub { [] }, handles => { _push => 'push', }, ); sub addAoObj { my ($self, @obj) = @_; $self->_push([ @obj ]); } sub addInnerObj { my ($self, $index, @obj) = @_; #assert validity of objects pushed #onto inner arrays-of-objects my $constrAoAoObj = find_type_constraint('Obj'); for my $o (@obj) { $constrAoAoObj->assert_valid($o); } push @{ $self->arr2d->[$index] }, @obj; } sub print { my $self = shift; my $aref = $self->arr2d; for my $x (@$aref) { for my $y (@$x) { $y->print; print "\t"; } print "\n"; } } no Moose; no Moose::Util::TypeConstraints; __PACKAGE__->meta->make_immutable; } use strict; my $struct1 = AoAoObj->new; for my $x (0 .. 4) { for my $y ("A" .. "E") { my $myobj = Obj->new( name => "$y$x" ); $struct1->addInnerObj($x, $myobj); } } $struct1->print; $struct1->addInnerObj(3, 'Whammi'); # succeeds unless data validation +code is added to the method $struct1->print; print "END of struct1\n\n"; my $struct2 = AoAoObj->new; my $o1 = Obj->new( name => 'X1' ); my $o2 = Obj->new( name => 'Y1' ); my $o3 = Obj->new( name => 'Z1' ); $struct2->addAoObj($o1, $o2, $o3); # success; pushes [X1 Y1 Z1] $struct2->print; $struct2->addAoObj( qw/Whammi1 Whammi2/ ); # fails validation $struct2->print; print "END of struct2\n\n";

There is a lot going on here, so I will try to explain as simply as I can. I have two methods for pushing data on the 'arr2d' attribute. First, I have 'addAofObj'. This takes an array-of-objects and pushes it onto the outer array. Data validation works perfectly in this case, without any extra work. If I try to push an array-of-strings, for instance, the script fails as expected. This is illustrated at the bottom of the script with the $struct2 operations. The downside to this method is that I cannot extend rows; I can add more pre-formed rows, but they can't be extended.

The second method I use for pushing data is intended to overcome the limitation of the first method shown above. This method is 'addInnerObj'. In this case, I can extend any given row, but now there is no type validation unless I code it into the method. If I comment out the type validation steps, and just push the data provided by the caller:

sub addInnerObj { my ($self, $index, @obj) = @_; #assert validity of objects pushed #onto inner arrays-of-objects #my $constrAoAoObj = find_type_constraint('Obj'); #for my $o (@obj) { #$constrAoAoObj->assert_valid($o); #} push @{ $self->arr2d->[$index] }, @obj; }

no warnings or errors are triggered until further downstream when methods which don't exist are called on the elements of the array. This is illustrated with the $struct1 operations. In this case, if the caller provides a 'Str' instead of an 'Obj' type, the string is pushed onto the end of the row with no complaint from the class. It appears that using the builtin 'push' circumvents the Moose type validation infrastructure. I can correct for this by adding my own data validation, but I fear that this will come back to bite me if I subclass, or do other complex operations. So, back to my original question: what is the proper way to work with multidimensional arrays/structures with Moose? Thank you in advance for your help.