use v5.14;

# Moose + multidimensional TypedArray example courtesy of TOBYINK:
#  http://www.perlmonks.org/?node_id=1052124

package Cell {
  use Moose;
  use Types::Standard -types;
  
  has name => (is => 'rw', isa => Str);
  
  __PACKAGE__->meta->make_immutable;
}

package Grid {
  use Moose;
  use Types::Standard -types;
  use List::Objects::Types -types;
  
  my $CellType = (InstanceOf['Cell'])->plus_coercions(
      Str, sub { 'Cell'->new(name => $_) },
  );
  
  has cells => (
      is      => 'ro',
      isa     => TypedArray[TypedArray[$CellType]],
      coerce  => 1,
      handles => {
          get_row   => 'get',
          set_row   => 'set',
          all_rows  => 'all',
          add_row   => 'push',
      },
  );
  
  sub get_cell {
      my $self = shift;
      my ($row, $col) = @_;
      
      $self->get_row($row)->get($col);
  }
  
  sub set_cell {
      my $self = shift;
      my ($row, $col, $value) = @_;
      
      $self->get_row($row)->set($col, $value);
  }
  
  sub all_cells {
      my $self = shift;
      map { $_->all } $self->all_rows;
  }
  
  sub get_col {
      my $self = shift;
      my ($col) = @_;
      
      map { $_->get($col) } $self->all_rows;
  }

  sub set_col {
      my $self = shift;
      my ($col, $values) = @_;
      
      my @rows = $self->all_rows;
      for my $i (0 .. $#rows) {
          $rows[$i]->set($col) = $values->[$i];
      }
  }

  sub add_col {
      my $self = shift;
      my ($values) = @_;
      
      my @rows = $self->all_rows;
      for my $i (0 .. $#rows) {
          $rows[$i]->push($values->[$i]);
      }
  }

  sub all_cols {
      my $self = shift;
      
      my $col_count   = $self->get_row(0)->count;
      my $return_type = TypedArray[$CellType];
      
      return
          map { $return_type->coerce($_); }
          map { [ $self->get_col($_) ]; }
          0 .. $col_count-1;
  }
  
  sub to_string {
      my $self = shift;
      join "\n", 
        map(join("\t", map($_->name, $_->all)), $self->all_rows);
  }

  __PACKAGE__->meta->make_immutable;
}

my $grid = Grid->new(
  cells => [
      [ 'foo1', 'bar1' ],
      [ 'foo2', 'bar2' ],
  ]
);

$grid->add_col(['baz1', 'baz2']);
$grid->get_cell(1, 1)->name('QUUX');

say $grid->to_string;