NAME
List::Objects::WithUtils - List objects, kitchen sink included
SYNOPSIS
## A small sample; consult the description, below, for links to## extended documentation# Import all object constructor functions:# array immarray array_of immarray_of# hash immhash hash_of immhash_ofuse List::Objects::WithUtils;# Import all of the above plus autoboxing:use List::Objects::WithUtils ':all';# Same as above, but shorter:use Lowu;# Most methods returning lists return new objects; chaining is easy:array(qw/ aa Ab bb Bc bc /)->grep(sub { /^b/i })->map(sub { uc })->uniq->all; # ( 'BB', 'BC' )# Useful utilities from other list modules are available:my $want_idx = array(+{ id => '400', user => 'bob' },+{ id => '600', user => 'suzy' },+{ id => '700', user => 'fred' },)->first_index(sub { $_->{id} > 500 });my $itr = array( 1 .. 7 )->natatime(3);while ( my @nextset = $itr->() ) {...}my $meshed = array(qw/ a b c d /)->mesh( array(1 .. 4) )->all; # ( 'a', 1, 'b', 2, 'c', 3, 'd', 4 )my ($evens, $odds) = array( 1 .. 20 )->part(sub { $_[0] & 1 })->all;my $sorted = array(+{ name => 'bob', acct => 1 },+{ name => 'fred', acct => 2 },+{ name => 'suzy', acct => 3 },)->sort_by(sub { $_->{name} });# array() objects are mutable:my $mutable = array(qw/ foo bar baz /);$mutable->insert(1, 'quux');$mutable->delete(2);# ... or use immarray() immutable arrays:my $static = immarray( qw/ foo bar baz / );$static->set(0, 'quux'); # dies$static->[0] = 'quux'; # diespush @$static, 'quux'; # dies# Construct a hash:my $hash = hash( foo => 'bar', snacks => 'cake' );# You can set multiple keys in one call:$hash->set( foobar => 'baz', pie => 'cherry' );# ... which is useful for merging in another (plain) hash:my %foo = ( pie => 'pumpkin', snacks => 'cheese' );$hash->set( %foo );# ... or another hash object:my $second = hash( pie => 'key lime' );$hash->set( $second->export );# Retrieve one value as a simple scalar:my $snacks = $hash->get('snacks');# ... or retrieve multiple values as an array-type object:my $vals = $hash->get('foo', 'foobar');# Take a hash slice of keys, return a new hash object# consisting of the retrieved key/value pairs:my $slice = $hash->sliced('foo', 'pie');# Arrays inflate to hash objects:my $items = array( qw/ foo bar baz/ )->map(sub { $_ => 1 })->inflate;if ($items->exists('foo')) {# ...}# Hashes inflate to simple objects with accessors:my $obj = $hash->inflate;$snacks = $obj->snacks;# Methods returning multiple values typically return new array-type objects:my @match_keys = $hash->keys->grep(sub { m/foo/ })->all;my @match_vals = $hash->values->grep(sub { m/bar/ })->all;my @sorted_pairs = hash( foo => 2, bar => 3, baz => 1)->kv->sort_by(sub { $_->[1] })->all; # ( [ baz => 1 ], [ foo => 2 ], [ bar => 3 ] )# Perl6-inspired Junctions:if ( $hash->keys->any_items == qr/snacks/ ) {# ... hash has key(s) matching /snacks/ ...}if ( $hash->values->all_items > 10 ) {# ... all hash values greater than 10 ...}# Type-checking arrays via Type::Tiny:use Types::Standard -all;my $int_arr = array_of Int() => 1 .. 10;# Type-checking hashes:use Types::Standard -all;my $int_hash = hash_of Int() => (foo => 1, bar => 2);# Native list types can be autoboxed:use List::Objects::WithUtils 'autobox';my $foo = [ qw/foo baz bar foo quux/ ]->uniq->sort;my $bar = +{ a => 1, b => 2, c => 3 }->values->sort;# Autoboxing is lexically scoped like normal:{ no List::Objects::WithUtils::Autobox;[ 1 .. 10 ]->shuffle; # dies}
DESCRIPTION
A set of roles and classes defining an object-oriented interface to Perl hashes and arrays with useful utility methods, junctions, type-checking ability, and optional autoboxing. Originally derived from Data::Perl.
Uses
The included objects are useful as-is but are largely intended for use as data container types for attributes. This lends a more natural object-oriented syntax; these are particularly convenient in combination with delegated methods, as in this example:
package Some::Thing;use List::Objects::WithUtils;use Moo;has items => (is => 'ro',builder => sub { array },handles => +{add_items => 'push',get_items => 'all',items_where => 'grep',},);# ... later ...my $thing = Some::Thing->new;$thing->add_items(@more_items);# Operate on all positive items:for my $item ($thing->items_where(sub { $_ > 0 })->all) {...}
List::Objects::Types provides Type::Tiny-based types & coercions matching the list objects provided by this distribution. These integrate nicely with typed or untyped list objects:
package Accounts;use List::Objects::Types -types;use Moo 2;has usergroups => (is => 'ro',# +{ $group => [ [ $usr => $id ], ... ] }# Coerced to objects all the way down:isa => TypedHash[ TypedArray[ArrayObj] ],coerce => 1,builder => sub { +{} },);# ... later ...my $users_in_grp = $accts->usergroups->get($some_group)->grep(sub { $_[0]->get(0) });
Objects
Arrays
array (List::Objects::WithUtils::Array) provides basic mutable ARRAY-type objects. Behavior is defined by List::Objects::WithUtils::Role::Array; look there for documentation on available methods.
immarray is imported from List::Objects::WithUtils::Array::Immutable and operates much like an array, except methods that mutate the list are not available; using immutable arrays promotes safer programming patterns.
array_of provides Type::Tiny-compatible type-checking array objects that can coerce and check their values as they are added; see List::Objects::WithUtils::Array::Typed.
immarray_of provides immutable type-checking arrays; see List::Objects::WithUtils::Array::Immutable::Typed.
Hashes
hash is the basic mutable HASH-type object imported from List::Objects::WithUtils::Hash; see List::Objects::WithUtils::Role::Hash for documentation.
immhash provides immutable (restricted) hashes; see List::Objects::WithUtils::Hash::Immutable.
hash_of provides Type::Tiny-compatible type-checking hash objects; see List::Objects::WithUtils::Hash::Typed.
immhash_of provides immutable type-checking hashes; see List::Objects::WithUtils::Hash::Immutable::Typed.
Importing
A bare import list ("use List::Objects::WithUtils;") will import all of the object constructor functions described above; they can also be selectively imported, e.g.:
use List::Objects::WithUtils 'array_of', 'hash_of';
Importing autobox lexically enables List::Objects::WithUtils::Autobox, which provides List::Objects::WithUtils::Array or List::Objects::WithUtils::Hash methods for native ARRAY and HASH types.
Importing all or :all will import all of the object constructors and additionally turn autobox on; "use Lowu;" is a shortcut for importing all.
Debugging
Most methods belonging to these objects are heavily micro-optimized -- at the cost of useful error handling.
Since there are few built-in argument checks, a mistake in your code can frequently lead to slightly cryptic errors from the perl side:
> my $pos; # whoops, I'm still undefined later:> if ($arr->exists($pos)) { ... }Use of uninitialized value in numeric le (<=) at $useless_lib_lineno
... in which case Devel::Confess is likely to improve your quality of life by providing a real backtrace:
$ perl -d:Confess my_app.plUse of uninitialized value in numeric le (<=) at ...[...]::Array::exists(ARRAY(0x8441068), undef) called at ...
Subclassing
The importer for this package is somewhat flexible; a subclass can override import to pass import tags and a target package by feeding this package's import() a HASH:
# Subclass and import to target packages (see Lowu.pm f.ex):package My::Defaults;use parent 'List::Objects::WithUtils';sub import {my ($class, @params) = @_;$class->SUPER::import(+{import => [ 'autobox', 'array', 'hash' ],to => scalar(caller)})}
Functionality is mostly defined by Roles. For example, it's easy to create your own array class with new methods:
package My::Array::Object;use Role::Tiny::With;# Act like List::Objects::WithUtils::Array:with 'List::Objects::WithUtils::Role::Array','List::Objects::WithUtils::Role::Array::WithJunctions';# One way to add your own functional interface:use Exporter 'import'; our @EXPORT = 'my_array';sub my_array { __PACKAGE__->new(@_) }# ... add/override methods ...
... in which case you may want to also define your own hash subclass that overrides "array_type" to produce your preferred arrays:
package My::Hash::Object;use Role::Tiny::With;with 'List::Objects::WithUtils::Role::Hash';use Exporter 'import'; our @EXPORT = 'my_hash';sub my_hash { __PACKAGE__->new(@_) }sub array_type { 'My::Array::Object' }# ... add/override methods ...
SEE ALSO
List::Objects::WithUtils::Role::Array for documentation on the basic set of array() methods.
List::Objects::WithUtils::Role::Array::WithJunctions for documentation on array() junction-returning methods.
List::Objects::WithUtils::Array::Immutable for more on immarray() immutable arrays.
List::Objects::WithUtils::Array::Typed for more on array_of() type-checking arrays.
List::Objects::WithUtils::Array::Immutable::Typed for more on immarray_of() immutable type-checking arrays.
List::Objects::WithUtils::Role::Hash for documentation regarding hash() methods.
List::Objects::WithUtils::Hash::Immutable for more on immhash() immutable hashes.
List::Objects::WithUtils::Hash::Typed for more on hash_of() type-checking hashes.
List::Objects::WithUtils::Hash::Immutable::Typed for more on immhash_of() immutable type-checking hashes.
List::Objects::WithUtils::Autobox for details on autoboxing.
The Lowu module for a convenient importer shortcut.
List::Objects::Types for relevant Type::Tiny types.
MoopsX::ListObjects for integration with Moops class-building sugar.
AUTHOR
Jon Portnoy <[email protected]>
Licensed under the same terms as Perl.
The original Array and Hash roles were derived from Data::Perl by Matthew Phillips (CPAN: MATTP), haarg, and others.
Immutable array objects were originally inspired by Const::Fast by Leon Timmermans (CPAN: LEONT), but now use "tie".
Junctions are adapted from Perl6::Junction by Carl Franks (CPAN: CFRANKS)
Most of the type-checking code and other useful additions were contributed by Toby Inkster (CPAN: TOBYINK)
A significant portion of this code simply wraps other widely-used modules, especially:
List::Util
List::UtilsBy
Type::Tiny
Inspiration for a few pieces comes from the "classic" (version 0.33) List::MoreUtils.