1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
|
# 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
use 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'; # 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:
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](https://metacpan.org/pod/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](https://metacpan.org/pod/List::Objects::Types) provides [Type::Tiny](https://metacpan.org/pod/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](https://metacpan.org/pod/List::Objects::WithUtils::Array)) provides basic mutable
ARRAY-type objects. Behavior is defined by
[List::Objects::WithUtils::Role::Array](https://metacpan.org/pod/List::Objects::WithUtils::Role::Array); look there for documentation on
available methods.
**immarray** is imported from [List::Objects::WithUtils::Array::Immutable](https://metacpan.org/pod/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](https://metacpan.org/pod/Type::Tiny)-compatible type-checking array objects
that can coerce and check their values as they are added; see
[List::Objects::WithUtils::Array::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Typed).
**immarray\_of** provides immutable type-checking arrays; see
[List::Objects::WithUtils::Array::Immutable::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Immutable::Typed).
### Hashes
**hash** is the basic mutable HASH-type object imported from
[List::Objects::WithUtils::Hash](https://metacpan.org/pod/List::Objects::WithUtils::Hash); see
[List::Objects::WithUtils::Role::Hash](https://metacpan.org/pod/List::Objects::WithUtils::Role::Hash) for documentation.
**immhash** provides immutable (restricted) hashes; see
[List::Objects::WithUtils::Hash::Immutable](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Immutable).
**hash\_of** provides [Type::Tiny](https://metacpan.org/pod/Type::Tiny)-compatible type-checking hash
objects; see [List::Objects::WithUtils::Hash::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Typed).
**immhash\_of** provides immutable type-checking hashes; see
[List::Objects::WithUtils::Hash::Immutable::Typed](https://metacpan.org/pod/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](https://metacpan.org/pod/List::Objects::WithUtils::Autobox),
which provides [List::Objects::WithUtils::Array](https://metacpan.org/pod/List::Objects::WithUtils::Array) or
[List::Objects::WithUtils::Hash](https://metacpan.org/pod/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](https://metacpan.org/pod/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;
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](https://metacpan.org/pod/List::Objects::WithUtils::Role::Array) for documentation on the basic set of
`array()` methods.
[List::Objects::WithUtils::Role::Array::WithJunctions](https://metacpan.org/pod/List::Objects::WithUtils::Role::Array::WithJunctions) for documentation on `array()`
junction-returning methods.
[List::Objects::WithUtils::Array::Immutable](https://metacpan.org/pod/List::Objects::WithUtils::Array::Immutable) for more on `immarray()`
immutable arrays.
[List::Objects::WithUtils::Array::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Typed) for more on `array_of()`
type-checking arrays.
[List::Objects::WithUtils::Array::Immutable::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Immutable::Typed) for more on
`immarray_of()` immutable type-checking arrays.
[List::Objects::WithUtils::Role::Hash](https://metacpan.org/pod/List::Objects::WithUtils::Role::Hash) for documentation regarding `hash()`
methods.
[List::Objects::WithUtils::Hash::Immutable](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Immutable) for more on `immhash()`
immutable hashes.
[List::Objects::WithUtils::Hash::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Typed) for more on `hash_of()`
type-checking hashes.
[List::Objects::WithUtils::Hash::Immutable::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Immutable::Typed) for more on
`immhash_of()` immutable type-checking hashes.
[List::Objects::WithUtils::Autobox](https://metacpan.org/pod/List::Objects::WithUtils::Autobox) for details on autoboxing.
The [Lowu](https://metacpan.org/pod/Lowu) module for a convenient importer shortcut.
[List::Objects::Types](https://metacpan.org/pod/List::Objects::Types) for relevant [Type::Tiny](https://metacpan.org/pod/Type::Tiny) types.
[MoopsX::ListObjects](https://metacpan.org/pod/MoopsX::ListObjects) for integration with [Moops](https://metacpan.org/pod/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](https://metacpan.org/pod/Data::Perl) by Matthew
Phillips (CPAN: MATTP), haarg, and others.
Immutable array objects were originally inspired by [Const::Fast](https://metacpan.org/pod/Const::Fast) by Leon
Timmermans (CPAN: LEONT), but now use `tie`.
Junctions are adapted from [Perl6::Junction](https://metacpan.org/pod/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](https://metacpan.org/pod/List::Util)
[List::UtilsBy](https://metacpan.org/pod/List::UtilsBy)
[Type::Tiny](https://metacpan.org/pod/Type::Tiny)
Inspiration for a few pieces comes from the "classic" (version 0.33)
[List::MoreUtils](https://metacpan.org/pod/List::MoreUtils).
|