NAME
    MooseX::Extended - Extend Moose with safe defaults and useful features

VERSION
    version 0.27

SYNOPSIS
        package My::Names {
            use MooseX::Extended types => [qw(compile Num NonEmptyStr Str PositiveInt ArrayRef)];
            use List::Util 'sum';

            # the distinction between `param` and `field` makes it easier to
            # see which are available to `new`
            param _name => ( isa => NonEmptyStr, init_arg => 'name' );
            param title => ( isa => Str,         required => 0 );

            # forbidden in the constructor
            field created => ( isa => PositiveInt, default => sub {time} );

            sub name ($self) {
                my $title = $self->title;
                my $name  = $self->_name;
                return $title ? "$title $name" : $name;
            }

            sub add ( $self, $args ) {
                state $check = compile( ArrayRef [ Num, 1 ] );    # at least one number
                ($args) = $check->($args);
                return sum( $args->@* );
            }

            sub warnit ($self) {
                carp("this is a warning");
            }
        }

DESCRIPTION
    This module is BETA code. It's feature-complete for release and has no
    known bugs. We believe it's ready for production, but make no promises.

    This is a quick overview. See MooseX::Extended::Manual::Tutorial for
    more information.

    This class attempts to create a safer version of Moose that defaults to
    read-only attributes and is easier to read and write.

    It tries to bring some of the lessons learned from the Corinna project
    <https://github.com/Ovid/Cor>, while acknowledging that you can't always
    get what you want (such as true encapsulation and true methods).

    This:

        package My::Class {
            use MooseX::Extended;

            ... your code here
        }

    Is sort of the equivalent to:

        package My::Class {
            use v5.20.0;
            use Moose;
            use MooseX::StrictConstructor;
            use feature qw( signatures postderef postderef_qq);
            no warnings qw( experimental::signatures experimental::postderef );
            use namespace::autoclean;
            use Carp;
            use mro 'c3';

            ... your code here

            __PACKAGE__->meta->make_immutable;
        }
        1;

    It also exports two functions which are similar to Moose "has": "param"
    and "field".

    A "param" is a required parameter (defaults may be used). A "field" is
    not intended to be passed to the constructor.

    Note that the "has" function is still available, even if it's not
    needed.

    Also, while your author likes the postfix block syntax, it's not
    required. You can even safely inline multiple packages in the same file:

        package My::Point;
        use MooseX::Extended types => [qw/Num/];

        param [ 'x', 'y' ] => ( isa => Num );

        package My::Point::Mutable;
        use MooseX::Extended;
        extends 'My::Point';

        param [ '+x', '+y' ] => ( writer => 1, clearer => 1, default => 0 );

        sub invert ($self) {
            my ( $x, $y ) = ( $self->x, $self->y );
            $self->set_x($y);
            $self->set_y($x);
        }

        # MooseX::Extended will cause this to return true, even if we try to return
        # false
        0;

CONFIGURATION
    You may pass an import list to MooseX::Extended.

        use MooseX::Extended
          excludes => [qw/StrictConstructor carp/],      # I don't want these features
          types    => [qw/compile PositiveInt HashRef/]; # I want these type tools

  "types"
    Allows you to import any types provided by MooseX::Extended::Types.

    This:

        use MooseX::Extended::Role types => [qw/compile PositiveInt HashRef/];

    Is identical to this:

        use MooseX::Extended::Role;
        use MooseX::Extended::Types qw( compile PositiveInt HashRef );

  "excludes"
    You may find some features to be annoying, or even cause potential bugs
    (e.g., if you have a "croak" method, our importing of "Carp::croak" will
    be a problem. You can exclude the following:

    *   "StrictConstructor"

            use MooseX::Extended excludes => ['StrictConstructor'];

        Excluding this will no longer import "MooseX::StrictConstructor".

    *   "autoclean"

            use MooseX::Extended excludes => ['autoclean'];

        Excluding this will no longer import "namespace::autoclean".

    *   "c3"

            use MooseX::Extended excludes => ['c3'];

        Excluding this will no longer apply the C3 mro.

    *   "carp"

            use MooseX::Extended excludes => ['carp'];

        Excluding this will no longer import "Carp::croak" and "Carp::carp".

    *   "immutable"

            use MooseX::Extended excludes => ['immutable'];

        Excluding this will no longer make your class immutable.

    *   "true"

            use MooseX::Extended excludes => ['true'];

        Excluding this will require your module to end in a true value.

    *   "param"

            use MooseX::Extended excludes => ['param'];

        Excluding this will make the "param" function unavailable.

    *   "field"

            use MooseX::Extended excludes => ['field'];

        Excluding this will make the "field" function unavailable.

  "includes"
    Some experimental features are useful, but might not be quite what you
    want.

    By default, MooseX::Extended tries to be relatively conservative.
    However, you might want to turn it up to 11. There are optional,
    EXPERIMENTAL features you can use for this. They're turned by the
    "includes" flag.

    *   "multi"

            use MooseX::Extended includes => [qw/multi/];

            multi sub foo ($self, $x)      { ... }
            multi sub foo ($self, $x, $y ) { ... }

        Allows you to redeclare a method (or subroutine) and the dispatch
        will use the number of arguments to determine which subroutine to
        use. Note that "slurpy" arguments such as arrays or hashes will take
        precedence over scalars:

            multi sub foo ($self, @x) { ... }
            multi sub foo ($self, $x) { ... } # will never be called

        Thus, the following probably doesn't do what you want.

            package Foo {
                use MooseX::Extended includes => [qw/multi/];

                multi sub foo ($self, @bar) { return '@bar' }
                multi sub foo ($self, $bar) { return '$bar' }
            }

            say +Foo->new->foo(1);
            say +Foo->new->foo(1,2,3);

        Both of the above will print the string @bar. The second definition
        of "foo" is effectively lost.

        Only available on Perl v5.26.0 or higher. Requires
        Syntax::Keyword::MultiSub.

    *   "async"

            package My::Thing {
                use MooseX::Extended
                types    => [qw/Str/],
                includes => ['async'];
                use IO::Async::Loop;

                field output => ( is => 'rw', isa => Str, default => '' );

                async sub doit ( $self, @list ) {
                    my $loop = IO::Async::Loop->new;
                    $self->output('> ');
                    foreach my $item (@list) {
                        await $loop->delay_future( after => 0.01 );
                        $self->output( $self->output . "$item " );
                    }
                }
            }

        Allows you to write asynchronous code with "async" and "await".

        Only available on Perl v5.26.0 or higher. Requires
        Future::AsyncAwait.

    *   "try"

            package My::Try {
                use MooseX::Extended includes => [qw/try/];

                sub reciprocal ( $self, $num ) {
                    try {
                        return 1 / $num;
                    }
                    catch {
                        croak "Could not calculate reciprocal of $num: $@";
                    }
                }
            }

        Allows you to use try/catch blocks, via Syntax::Keyword::Try.

        Only available on Perl v5.24.0 or higher. Requires
        Syntax::Keyword::Try.

REDUCING BOILERPLATE
    Let's say you've settled on the following feature set:

        use MooseX::Extended
            excludes => [qw/StrictConstructor carp/],
            includes => [qw/multi/];

    And you keep typing that over and over. We've removed a lot of
    boilerplate, but we've added different boilerplate. Instead, just create
    "My::Custom::Moose" and "use My::Custom::Moose;". See
    MooseX::Extended::Custom for details.

IMMUTABILITY
  Making Your Class Immutable
    You no longer need to end your Moose classes with:

        __PACKAGE__->meta->make_immutable;

    That prevents further changes to the class and provides some
    optimizations to make the code run much faster. However, it's somewhat
    annoying to type. We do this for you, via B::Hooks::AtRuntime. You no
    longer need to do this yourself.

  Making Your Instance Immutable
    By default, attributes defined via "param" and "field" are read-only.
    However, if they contain a reference, you can fetch the reference,
    mutate it, and now everyone with a copy of that reference has mutated
    state.

    To handle that, we offer a new "clone => $clone_type" pair for
    attributes.

    See the MooseX::Extended::Manual::Cloning documentation.

OBJECT CONSTRUCTION
    Object construction for MooseX::Extended is identical to Moose because
    MooseX::Extended *is* Moose, so no changes are needed. However, in
    addition to "has", we also provide "param" and "field" attributes, both
    of which are "is => 'ro'" by default.

    The "param" is *required*, whether by passing it to the constructor, or
    using "default" or "builder".

    The "field" is *forbidden* in the constructor and is lazy if it has a
    builder, because that builder is often dependent on attributes set in
    the constructor (and why call it if it's not used?).

    Here's a short example:

        package Class::Name {
            use MooseX::Extended types => [qw(compile Num NonEmptyStr Str)];

            # these default to 'ro' (but you can override that) and are required
            param _name => ( isa => NonEmptyStr, init_arg => 'name' );
            param title => ( isa => Str,         required => 0 );

            # fields must never be passed to the constructor
            # note that ->title and ->name are guaranteed to be set before
            # this because fields are lazy by default
            field name => (
                isa     => NonEmptyStr,
                default => sub ($self) {
                    my $title = $self->title;
                    my $name  = $self->_name;
                    return $title ? "$title $name" : $name;
                },
            );
        }

    See MooseX::Extended::Manual::Construction for a full explanation.

ATTRIBUTE SHORTCUTS
    When using "field" or "param", we have some attribute shortcuts:

        param name => (
            isa       => NonEmptyStr,
            writer    => 1,   # set_name
            reader    => 1,   # get_name
            predicate => 1,   # has_name
            clearer   => 1,   # clear_name
            builder   => 1,   # _build_name
        );

        sub _build_name ($self) {
            ...
        }

    You can also do this:

        param name ( isa => NonEmptyStr, builder => sub {...} );

    That's the same as:

        param name ( isa => NonEmptyStr, builder => '_build_name' );

        sub _build_name {...}

    See MooseX::Extended::Manual::Shortcuts for a full explanation.

INVALID ATTRIBUTE DEFINITIONS
    The following Moose code will print "WhoAmI". However, the second
    attribute name is clearly invalid.

        package Some::Class {
            use Moose;

            has name   => ( is => 'ro' );
            has '-bad' => ( is => 'ro' );
        }

        my $object = Some::Class->new( name => 'WhoAmI' );
        say $object->name;

    "MooseX::Extended" will throw a
    Moose::Exception::InvalidAttributeDefinition exception if it encounters
    an illegal method name for an attribute.

    This also applies to various attributes which allow method names, such
    as "clone", "builder", "clearer", "writer", "reader", and "predicate".

    Trying to pass a defined "init_arg" to "field" will also throw this
    exception, unless the init_arg begins with an underscore. (It is
    sometimes useful to be able to define an "init_arg" for unit testing.)

BUGS AND LIMITATIONS
    When using Test::Compile, there are intermittent segfaults with
    MooseX::Extended unless you use "exclude => ['immutable']". We are still
    trying to figure out wy this is happening. We are not aware of any
    production code affected by this.

    See also:

    *   The github issue <https://github.com/Ovid/moosex-extreme/issues/41>

    *   Perlmonks discussion <https://perlmonks.org/?node_id=11145964>

MANUAL
    *   MooseX::Extended::Manual::Tutorial

    *   MooseX::Extended::Manual::Overview

    *   MooseX::Extended::Manual::Construction

    *   MooseX::Extended::Manual::Shortcuts

    *   MooseX::Extended::Manual::Cloning

RELATED MODULES
    *   MooseX::Extended::Types is included in the distribution.

        This provides core types for you.

    *   MooseX::Extended::Role is included in the distribution.

        "MooseX::Extended", but for roles.

TODO
    Some of this may just be wishful thinking. Some of this would be
    interesting if others would like to collaborate.

  Configurable Types
    We provide "MooseX::Extended::Types" for convenience, along with the
    "declare" function. We should write up (and test) examples of extending
    it.

  "BEGIN::Lift"
    This idea maybe belongs in "MooseX::Extended::OverKill", but ...

    Quite often you see things like this:

        BEGIN { extends 'Some::Parent' }

    Or this:

        sub serial_number; # required by a role, must be compile-time
        has serial_number => ( ... );

    In fact, there are a variety of Moose functions which would work better
    if they ran at compile-time instead of runtime, making them look a touch
    more like native functions. My various attempts at solving this have
    failed, but I confess I didn't try too hard.

NOTES
    There are a few things you might be interested to know about this module
    when evaluating it.

    Most of this is written with bog-standard Moose, so there's nothing
    terribly weird inside, but you may wish to note that we use
    B::Hooks::AtRuntime and true. They seem sane, but *caveat emptor*.

SEE ALSO
    *   Corinna <https://github.com/Ovid/Cor>

        The RFC of the new version of OOP planned for the Perl core.

    *   MooseX::Modern <https://metacpan.org/pod/MooseX::Modern>

        MooseX::Modern - Precision classes for Modern Perl

    *   Zydeco <https://metacpan.org/pod/Zydeco>

        Zydeco - Jazz up your Perl

    *   Dios <https://metacpan.org/pod/Dios>

        Dios - Declarative Inside-Out Syntax

    *   MooseX::AttributeShortcuts
        <https://metacpan.org/pod/MooseX::AttributeShortcuts>

        MooseX::AttributeShortcuts - Shorthand for common attribute options

AUTHOR
    Curtis "Ovid" Poe <curtis.poe@gmail.com>

COPYRIGHT AND LICENSE
    This software is Copyright (c) 2022 by Curtis "Ovid" Poe.

    This is free software, licensed under:

      The Artistic License 2.0 (GPL Compatible)