I think that you really want allowed_values be a separate data structure with the desired performance and ordering properties. Since this is not like you care about the order, why not:
has 'allowed_values' => ( traits => ['Hash'], isa => HashRef[Bool], default => sub { +{} }, handles => { _add_allowed_value => 'set', remove_allowed_value => 'delete', value_is_allowed => 'exists', allowed_values => 'keys', }, ); method add_allowed_value(Str $value){ $self->_add_allowed_value( $value, 1 ); }
In general, anything that does not correspond to a particular class should be implemented elsewhere. Creating arrays has a faster search time - this is not the work of any class that you write, so it must be implemented elsewhere, and this class must use this class. (In the simple case, like the hash above, it might be OK to ignore this rule. But if it were more complicated, you would definitely want to justify it.)
Edit:
If you want the user to think this is a list, how about:
use MooseX::Types::Moose qw(Bool ArrayRef HashRef); use MooseX::Types -declare => ['ListHash']; subtype ListHash, as HashRef[Bool]; coerce ListHash, from ArrayRef, via { +{ map { $_ => 1 } @$_ } }; has 'allowed_values' => (
Now you can set allowed_values as:
my $instance = Class->new( allowed_values => [qw/foo bar/] ); $instance->set_allowed_values([qw/foo bar baz/]);
And refer to them as follows:
my @allowed_values = $instance->allowed_values; ... if $instance->value_is_allowed('foo');
And change them:
$instance->remove_allowed_value('foo'); $instance->add_allowed_value('gorch');
This hides any underlying implementation data from the user.
By the way, does it actually build a hash and use it much faster than a linear scan of 3 elements?