Search code examples
perlmoose

In Moose, how do I uniq an arrayref setter?


I have a Moose class that

has 'unique_ints' => (
    is              => 'rw',
    isa             => 'ArrayRef[Int]',
    default         => sub { [] },
);

Which is the best way to guarantee unique_int's uniqueness?


Solution

  • Moose attributes can have a trigger property which is called whenever that attribute is changed. The property is a reference to a method that is passed the object and the new and old versions of the attribute. So we can write something like this:

    package UniqInt;
    
    use Moose;
    
    use List::Util 'uniqint';
    
    has 'unique_ints' => (
        is              => 'rw',
        isa             => 'ArrayRef[Int]',
        default         => sub { [] },
        trigger         => \&_ensure_uniq,
    );
    
    sub _ensure_uniq {
      my $self = shift;
      my ($new) = @_;
    
      $self->{unique_ints} = [ uniqint @$new ];
    }
    
    1;
    

    Note that in the trigger method, we're using direct hash access to the attribute's value, as calling the mutator again would call the trigger method again and get us into an infinite pit of recursion.

    We can test the method like this:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use feature 'say';
    
    use UniqInt;
    
    my $obj = UniqInt->new;
    
    $obj->unique_ints([1 .. 5]);
    
    say join ',', @{$obj->unique_ints};
    
    $obj->unique_ints([1 .. 5, 1 .. 5]);
    
    say join ',', @{$obj->unique_ints};
    

    Which, as expected, produces this output:

    1,2,3,4,5
    1,2,3,4,5
    

    Update: Note also that as I have no use for the third parameter to the trigger method (which is the previous value of the attribute), I'm ignoring it.