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_of
# Import all of the above plus autoboxing:
# 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'
;
# dies
push
@$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:
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
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.:
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.pl
Use 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;
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:
'List::Objects::WithUtils::Role::Array::WithJunctions'
;
# One way to add your own functional interface:
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;
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 <avenj@cobaltirc.org>
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:
Inspiration for a few pieces comes from the "classic" (version 0.33) List::MoreUtils.