# 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;[email protected]>

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).