File: README.mkdn

package info (click to toggle)
liblist-objects-withutils-perl 2.028003-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,292 kB
  • sloc: perl: 1,957; makefile: 17; sh: 6
file content (373 lines) | stat: -rw-r--r-- 13,776 bytes parent folder | download | duplicates (4)
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 &lt;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).