1# NAME
2
3List::Objects::WithUtils - List objects, kitchen sink included
4
5# SYNOPSIS
6
7 ## A small sample; consult the description, below, for links to
8 ## extended documentation
9
10 # Import all object constructor functions:
11 # array immarray array_of immarray_of
12 # hash immhash hash_of immhash_of
13 use List::Objects::WithUtils;
14
15 # Import all of the above plus autoboxing:
16 use List::Objects::WithUtils ':all';
17 # Same as above, but shorter:
18 use Lowu;
19
20 # Most methods returning lists return new objects; chaining is easy:
21 array(qw/ aa Ab bb Bc bc /)
22 ->grep(sub { /^b/i })
23 ->map(sub { uc })
24 ->uniq
25 ->all; # ( 'BB', 'BC' )
26
27 # Useful utilities from other list modules are available:
28 my $want_idx = array(
29 +{ id => '400', user => 'bob' },
30 +{ id => '600', user => 'suzy' },
31 +{ id => '700', user => 'fred' },
32 )->first_index(sub { $_->{id} > 500 });
33
34 my $itr = array( 1 .. 7 )->natatime(3);
35 while ( my @nextset = $itr->() ) {
36 ...
37 }
38
39 my $meshed = array(qw/ a b c d /)
40 ->mesh( array(1 .. 4) )
41 ->all; # ( 'a', 1, 'b', 2, 'c', 3, 'd', 4 )
42
43 my ($evens, $odds) = array( 1 .. 20 )
44 ->part(sub { $_[0] & 1 })
45 ->all;
46
47 my $sorted = array(
48 +{ name => 'bob', acct => 1 },
49 +{ name => 'fred', acct => 2 },
50 +{ name => 'suzy', acct => 3 },
51 )->sort_by(sub { $_->{name} });
52
53 # array() objects are mutable:
54 my $mutable = array(qw/ foo bar baz /);
55 $mutable->insert(1, 'quux');
56 $mutable->delete(2);
57
58 # ... or use immarray() immutable arrays:
59 my $static = immarray( qw/ foo bar baz / );
60 $static->set(0, 'quux'); # dies
61 $static->[0] = 'quux'; # dies
62 push @$static, 'quux'; # dies
63
64 # Construct a hash:
65 my $hash = hash( foo => 'bar', snacks => 'cake' );
66
67 # You can set multiple keys in one call:
68 $hash->set( foobar => 'baz', pie => 'cherry' );
69
70 # ... which is useful for merging in another (plain) hash:
71 my %foo = ( pie => 'pumpkin', snacks => 'cheese' );
72 $hash->set( %foo );
73
74 # ... or another hash object:
75 my $second = hash( pie => 'key lime' );
76 $hash->set( $second->export );
77
78 # Retrieve one value as a simple scalar:
79 my $snacks = $hash->get('snacks');
80
81 # ... or retrieve multiple values as an array-type object:
82 my $vals = $hash->get('foo', 'foobar');
83
84 # Take a hash slice of keys, return a new hash object
85 # consisting of the retrieved key/value pairs:
86 my $slice = $hash->sliced('foo', 'pie');
87
88 # Arrays inflate to hash objects:
89 my $items = array( qw/ foo bar baz/ )->map(sub { $_ => 1 })->inflate;
90 if ($items->exists('foo')) {
91 # ...
92 }
93
94 # Hashes inflate to simple objects with accessors:
95 my $obj = $hash->inflate;
96 $snacks = $obj->snacks;
97
98 # Methods returning multiple values typically return new array-type objects:
99 my @match_keys = $hash->keys->grep(sub { m/foo/ })->all;
100 my @match_vals = $hash->values->grep(sub { m/bar/ })->all;
101
102 my @sorted_pairs = hash( foo => 2, bar => 3, baz => 1)
103 ->kv
104 ->sort_by(sub { $_->[1] })
105 ->all; # ( [ baz => 1 ], [ foo => 2 ], [ bar => 3 ] )
106
107 # Perl6-inspired Junctions:
108 if ( $hash->keys->any_items == qr/snacks/ ) {
109 # ... hash has key(s) matching /snacks/ ...
110 }
111 if ( $hash->values->all_items > 10 ) {
112 # ... all hash values greater than 10 ...
113 }
114
115 # Type-checking arrays via Type::Tiny:
116 use Types::Standard -all;
117 my $int_arr = array_of Int() => 1 .. 10;
118
119 # Type-checking hashes:
120 use Types::Standard -all;
121 my $int_hash = hash_of Int() => (foo => 1, bar => 2);
122
123 # Native list types can be autoboxed:
124 use List::Objects::WithUtils 'autobox';
125 my $foo = [ qw/foo baz bar foo quux/ ]->uniq->sort;
126 my $bar = +{ a => 1, b => 2, c => 3 }->values->sort;
127
128 # Autoboxing is lexically scoped like normal:
129 { no List::Objects::WithUtils::Autobox;
130 [ 1 .. 10 ]->shuffle; # dies
131 }
132
133# DESCRIPTION
134
135A set of roles and classes defining an object-oriented interface to Perl
136hashes and arrays with useful utility methods, junctions, type-checking
137ability, and optional autoboxing. Originally derived from [Data::Perl](https://metacpan.org/pod/Data::Perl).
138
139## Uses
140
141The included objects are useful as-is but are largely intended for use as data
142container types for attributes. This lends a more natural object-oriented
143syntax; these are particularly convenient in combination with delegated
144methods, as in this example:
145
146 package Some::Thing;
147 use List::Objects::WithUtils;
148 use Moo;
149
150 has items => (
151 is => 'ro',
152 builder => sub { array },
153 handles => +{
154 add_items => 'push',
155 get_items => 'all',
156 items_where => 'grep',
157 },
158 );
159
160 # ... later ...
161 my $thing = Some::Thing->new;
162 $thing->add_items(@more_items);
163 # Operate on all positive items:
164 for my $item ($thing->items_where(sub { $_ > 0 })->all) {
165 ...
166 }
167
168[List::Objects::Types](https://metacpan.org/pod/List::Objects::Types) provides [Type::Tiny](https://metacpan.org/pod/Type::Tiny)-based types & coercions
169matching the list objects provided by this distribution. These integrate
170nicely with typed or untyped list objects:
171
172 package Accounts;
173 use List::Objects::Types -types;
174 use Moo 2;
175
176 has usergroups => (
177 is => 'ro',
178 # +{ $group => [ [ $usr => $id ], ... ] }
179 # Coerced to objects all the way down:
180 isa => TypedHash[ TypedArray[ArrayObj] ],
181 coerce => 1,
182 builder => sub { +{} },
183 );
184
185 # ... later ...
186 my $users_in_grp = $accts->usergroups
187 ->get($some_group)
188 ->grep(sub { $_[0]->get(0) });
189
190
191## Objects
192
193### Arrays
194
195**array** ([List::Objects::WithUtils::Array](https://metacpan.org/pod/List::Objects::WithUtils::Array)) provides basic mutable
196ARRAY-type objects. Behavior is defined by
197[List::Objects::WithUtils::Role::Array](https://metacpan.org/pod/List::Objects::WithUtils::Role::Array); look there for documentation on
198available methods.
199
200**immarray** is imported from [List::Objects::WithUtils::Array::Immutable](https://metacpan.org/pod/List::Objects::WithUtils::Array::Immutable) and
201operates much like an **array**, except methods that mutate the list are not
202available; using immutable arrays promotes safer programming patterns.
203
204**array\_of** provides [Type::Tiny](https://metacpan.org/pod/Type::Tiny)-compatible type-checking array objects
205that can coerce and check their values as they are added; see
206[List::Objects::WithUtils::Array::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Typed).
207
208**immarray\_of** provides immutable type-checking arrays; see
209[List::Objects::WithUtils::Array::Immutable::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Immutable::Typed).
210
211### Hashes
212
213**hash** is the basic mutable HASH-type object imported from
214[List::Objects::WithUtils::Hash](https://metacpan.org/pod/List::Objects::WithUtils::Hash); see
215[List::Objects::WithUtils::Role::Hash](https://metacpan.org/pod/List::Objects::WithUtils::Role::Hash) for documentation.
216
217**immhash** provides immutable (restricted) hashes; see
218[List::Objects::WithUtils::Hash::Immutable](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Immutable).
219
220**hash\_of** provides [Type::Tiny](https://metacpan.org/pod/Type::Tiny)-compatible type-checking hash
221objects; see [List::Objects::WithUtils::Hash::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Typed).
222
223**immhash\_of** provides immutable type-checking hashes; see
224[List::Objects::WithUtils::Hash::Immutable::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Immutable::Typed).
225
226## Importing
227
228A bare import list (`use List::Objects::WithUtils;`) will import all of the
229object constructor functions described above; they can also be selectively
230imported, e.g.:
231
232 use List::Objects::WithUtils 'array_of', 'hash_of';
233
234Importing **autobox** lexically enables [List::Objects::WithUtils::Autobox](https://metacpan.org/pod/List::Objects::WithUtils::Autobox),
235which provides [List::Objects::WithUtils::Array](https://metacpan.org/pod/List::Objects::WithUtils::Array) or
236[List::Objects::WithUtils::Hash](https://metacpan.org/pod/List::Objects::WithUtils::Hash) methods for native ARRAY and HASH types.
237
238Importing **all** or **:all** will import all of the object constructors and
239additionally turn **autobox** on; `use Lowu;` is a shortcut for importing
240**all**.
241
242## Debugging
243
244Most methods belonging to these objects are heavily micro-optimized -- at the
245cost of useful error handling.
246
247Since there are few built-in argument checks, a mistake in your code can
248frequently lead to slightly cryptic errors from the perl side:
249
250 > my $pos; # whoops, I'm still undefined later:
251 > if ($arr->exists($pos)) { ... }
252 Use of uninitialized value in numeric le (<=) at $useless_lib_lineno
253
254... in which case [Devel::Confess](https://metacpan.org/pod/Devel::Confess) is likely to improve your quality of life
255by providing a real backtrace:
256
257 $ perl -d:Confess my_app.pl
258 Use of uninitialized value in numeric le (<=) at ...
259 [...]::Array::exists(ARRAY(0x8441068), undef) called at ...
260
261## Subclassing
262
263The importer for this package is somewhat flexible; a subclass can override
264import to pass import tags and a target package by feeding this package's
265`import()` a HASH:
266
267 # Subclass and import to target packages (see Lowu.pm f.ex):
268 package My::Defaults;
269 use parent 'List::Objects::WithUtils';
270 sub import {
271 my ($class, @params) = @_;
272 $class->SUPER::import(
273 +{
274 import => [ 'autobox', 'array', 'hash' ],
275 to => scalar(caller)
276 }
277 )
278 }
279
280Functionality is mostly defined by Roles.
281For example, it's easy to create your own array class with new methods:
282
283 package My::Array::Object;
284 use Role::Tiny::With;
285 # Act like List::Objects::WithUtils::Array:
286 with 'List::Objects::WithUtils::Role::Array',
287 'List::Objects::WithUtils::Role::Array::WithJunctions';
288
289 # One way to add your own functional interface:
290 use Exporter 'import'; our @EXPORT = 'my_array';
291 sub my_array { __PACKAGE__->new(@_) }
292
293 # ... add/override methods ...
294
295... in which case you may want to also define your own hash subclass that
296overrides `array_type` to produce your preferred arrays:
297
298 package My::Hash::Object;
299 use Role::Tiny::With;
300 with 'List::Objects::WithUtils::Role::Hash';
301
302 use Exporter 'import'; our @EXPORT = 'my_hash';
303 sub my_hash { __PACKAGE__->new(@_) }
304
305 sub array_type { 'My::Array::Object' }
306
307 # ... add/override methods ...
308
309
310# SEE ALSO
311
312[List::Objects::WithUtils::Role::Array](https://metacpan.org/pod/List::Objects::WithUtils::Role::Array) for documentation on the basic set of
313`array()` methods.
314
315[List::Objects::WithUtils::Role::Array::WithJunctions](https://metacpan.org/pod/List::Objects::WithUtils::Role::Array::WithJunctions) for documentation on `array()`
316junction-returning methods.
317
318[List::Objects::WithUtils::Array::Immutable](https://metacpan.org/pod/List::Objects::WithUtils::Array::Immutable) for more on `immarray()`
319immutable arrays.
320
321[List::Objects::WithUtils::Array::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Typed) for more on `array_of()`
322type-checking arrays.
323
324[List::Objects::WithUtils::Array::Immutable::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Array::Immutable::Typed) for more on
325`immarray_of()` immutable type-checking arrays.
326
327[List::Objects::WithUtils::Role::Hash](https://metacpan.org/pod/List::Objects::WithUtils::Role::Hash) for documentation regarding `hash()`
328methods.
329
330[List::Objects::WithUtils::Hash::Immutable](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Immutable) for more on `immhash()`
331immutable hashes.
332
333[List::Objects::WithUtils::Hash::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Typed) for more on `hash_of()`
334type-checking hashes.
335
336[List::Objects::WithUtils::Hash::Immutable::Typed](https://metacpan.org/pod/List::Objects::WithUtils::Hash::Immutable::Typed) for more on
337`immhash_of()` immutable type-checking hashes.
338
339[List::Objects::WithUtils::Autobox](https://metacpan.org/pod/List::Objects::WithUtils::Autobox) for details on autoboxing.
340
341The [Lowu](https://metacpan.org/pod/Lowu) module for a convenient importer shortcut.
342
343[List::Objects::Types](https://metacpan.org/pod/List::Objects::Types) for relevant [Type::Tiny](https://metacpan.org/pod/Type::Tiny) types.
344
345[MoopsX::ListObjects](https://metacpan.org/pod/MoopsX::ListObjects) for integration with [Moops](https://metacpan.org/pod/Moops) class-building sugar.
346
347# AUTHOR
348
349Jon Portnoy <avenj@cobaltirc.org>
350
351Licensed under the same terms as Perl.
352
353The original Array and Hash roles were derived from [Data::Perl](https://metacpan.org/pod/Data::Perl) by Matthew
354Phillips (CPAN: MATTP), haarg, and others.
355
356Immutable array objects were originally inspired by [Const::Fast](https://metacpan.org/pod/Const::Fast) by Leon
357Timmermans (CPAN: LEONT), but now use `tie`.
358
359Junctions are adapted from [Perl6::Junction](https://metacpan.org/pod/Perl6::Junction) by Carl Franks (CPAN: CFRANKS)
360
361Most of the type-checking code and other useful additions were contributed by
362Toby Inkster (CPAN: TOBYINK)
363
364A significant portion of this code simply wraps other widely-used modules, especially:
365
366[List::Util](https://metacpan.org/pod/List::Util)
367
368[List::UtilsBy](https://metacpan.org/pod/List::UtilsBy)
369
370[Type::Tiny](https://metacpan.org/pod/Type::Tiny)
371
372Inspiration for a few pieces comes from the "classic" (version 0.33)
373[List::MoreUtils](https://metacpan.org/pod/List::MoreUtils).
374