2

I would like to set up an attribute that is an array of arrayrefs with coercion of nonarrayrefs to array refs. eg.

[ 0, [ 0, 0, 0 ], [1,2,3] ] into [ [0], [ 0, 0, 0 ], [1,2,3] ]

also, I'd like to be able to push or set elements to the AoA with coercion as well. Here is my attempt:

{
    package MyArray;
    use namespace::autoclean;
    use Moose::Util::TypeConstraints;
    use Moose;

    subtype 'My::ArrayRef' => as 'ArrayRef';
    coerce  'My::ArrayRef'
      => from 'Num|Str'
         => via {[$_]};

    has 'ents' => (
        traits  => ['Array'],
        is      => 'rw',
        isa     => 'ArrayRef[My::ArrayRef]',
        default => sub { [] },
        handles => {
            push      => 'push',
            get       => 'get',
            set       => 'set',
            elements  => 'elements',
            count     => 'count',
        },
        coerce => 1,
    );

    __PACKAGE__->meta->make_immutable;

}

use Modern::Perl;

my $a0 = MyArray->new( ents => [ 0, [ 0, 0, 0 ], [1,2,3] ] ) ;

use Data::Dumper;

print Dumper $a0;

$a0->set(0,'cat');
print Dumper $a0;
$a0->push(1.0);
print Dumper $a0;
Demian
  • 215
  • 2
  • 9
  • This was [crossposted](http://www.perlmonks.org/?node_id=917344) on PerlMonks. – ikegami Jul 28 '11 at 21:51
  • is it ok to crosspost? the interface here is a little cleaner here, and seems to load a little faster at work. I don't want to break any norms though! – Demian Jul 29 '11 at 00:37
  • it's good for to crosslink so duplicate work can be avoided and so both groups can benefit from the responses. – ikegami Jul 29 '11 at 08:27

1 Answers1

4

The type needs to fail to match before the coercion, but succeed after.

This does the trick (tested):

my $array_ref = Moose::Util::TypeConstraints::find_type_constraint('ArrayRef');

# Create an alias so we don't affect others with our coercion.
subtype 'My::Data::Structure'
   => as 'ArrayRef[ArrayRef[Str|Num]]';

coerce 'My::Data::Structure'
   => from 'ArrayRef[ArrayRef[Str|Num]|Str|Num]'
      => via { [ map $array_ref->check($_) ? $_ : [ $_ ], @$_ ] };
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • @Demian, updated to do check for Str|Num. Much cleaner than original too. – ikegami Jul 28 '11 at 20:51
  • Where in his code would you put your code to make it work? I've tried various places, and under 2.0202 I get the same coercion errors. – Axeman Jul 28 '11 at 21:40
  • @Axeman, it only works without the trait, oops. (`has ents => (is=>'rw',isa=>'My::Data::Structure',coerce=>1);`). My [answer](http://www.perlmonks.org/?node_id=917352) to his post on PerlMonks does handle traits. – ikegami Jul 28 '11 at 21:50
  • @ikegami, that monks post is a pretty good clinic on type coercion! – Axeman Jul 29 '11 at 23:35