NAME
    Marlin - 🐟 pretty fast class builder with most Moo/Moose features 🐟

SYNOPSIS
      use v5.20.0;
      no warnings "experimental::signatures";
  
      package Person {
        use Types::Common -lexical, -all;
        use Marlin::Util -lexical, -all;
        use Marlin
          'name!' => Str,
          'age?'  => Int;
    
        signature_for introduction => (
          method   => true,
          named    => [ audience => Optional[InstanceOf['Person']] ],
        );
    
        sub introduction ( $self, $arg ) {
          say "Hi " . $arg->audience->name . "!" if $arg->has_audience;
          say "My name is " . $self->name . ".";
        }
      }
  
      package Employee {
        use Marlin -base => 'Person', 'employee_id!';
      }
  
      my $alice = Person->new( name => 'Alice Whotfia' );
  
      my $bob = Employee->new(
        name         => 'Bob Dobalina',
        employee_id  => '007',
      );
  
      $alice->introduction( audience => $bob );

DESCRIPTION
    Marlin is a fast class builder, inspired by Moose and Moo. It supports
    most of their features, but with a different syntax. Because it uses
    Class::XSAccessor, Class::XSConstructor, and Type::Tiny::XS, it is usually
    *slightly* faster though. Especially if you keep things simple and don't
    use features that force Marlin to fall back to using Pure Perl.

    It may not be as fast as classes built with the Perl builtin `class`
    syntax introduced in Perl v5.38.0, but has more features and supports Perl
    versions as old as v5.8.8. (Some features require v5.12.0+.)

    Marlin was created by the developer of Type::Tiny and Sub::HandlesVia and
    integrates with them.

  Using Marlin
    Marlin does all of its work at compile time, so doesn't export keywords
    like `has` into your namespace.

   Declaring Attributes
    Any strings found in the `use Marlin` line (except a few special ones
    beginning with a dash, used to configure Marlin) will be assumed to be
    attributes you want to declare for your class.

      package Address {
        use Marlin qw( street_address locality region country postal_code );
      }
  
      my $adr = Address->new( street_address => '123 Test Street' );
      say $adr->street_address;

    Any attributes you declare will will be accepted by the constructor that
    Marlin creates for your class, and reader/getter methods will be created
    to access their values.

    Attributes can be followed by a hashref to tailor their behaviour.

      package Address {
        use Marlin::Util qw( true false );
    
        use Marlin
          street_address  => { is => 'rw', required  => true },
          locality        => { is => 'rw' },
          region          => { is => 'rw' },
          country         => { is => 'rw', required => true },
          postal_code     => { is => 'rw', predicate => 'has_pc' },
          ;
      }
  
      my $adr = Address->new(
        street_address => '123 Test Street',
        country        => 'North Pole',
      );
  
      $adr->has_pc or die;  # will die as there is no postal_code

    Some behaviours are so commonly useful that there are shortcuts for them.

      # Shortcut for: name => { required => true }
      use Marlin 'name!';
  
      # Shortcut for: name => { predicate => true }
      use Marlin 'name?';
  
      # Shortcut for: name => { is => "rwp" }
      use Marlin 'name=';
  
      # Shortcut for: name => { is => "rw" }
      use Marlin 'name==';
  
      # Shortcut for: name => { init_arg => undef }
      use Marlin 'name.';

    Using these shortcuts, our previous Address example can be written as:

      package Address {
        use Marlin qw(
          street_address==!
          locality==
          region==
          country==!
          postal_code==?
        );
      }

    The order of these trailing modifiers doesn't matter, so 'foo=?' means the
    same as 'foo?=', though in the double-equals modifier for read-write
    attributes, the equals signs cannot have a character between them.

    There are also some useful alternatives to providing a full hashref:

      use Types::Common 'Str';
  
      # Shortcut for: name => { required => true, isa => Str }
      use Marlin 'name!' => Str;
  
      # Shortcut for: name => { lazy => true, builder => sub { ... } }
      use Marlin 'name' => sub { ... };

    If we wanted to add type checks to our previous Address example, we might
    use:

      package Address {
        use Types::Common 'Str';
        use Marlin
          'street_address==!'  => Str,
          'locality=='         => Str,
          'region=='           => Str,
          'country==!'         => Str,
          'postal_code==?'     => Str,
          ;
      }

    Marlin can *almost* be used as a drop-in replacement for Class::Tiny.
    Examples from the Class::Tiny SYNOPSIS:

      # Person.pm
      package Person;
      use Marlin qw( name );
      1;
  
      # Employee.pm
      package Employee;
      use parent 'Person';
      use Marlin qw( ssn ), timestamp => sub { time };
      1;

    The only change was to remove the hashref which wrapped `timestamp => sub
    { time }`.

   Supported Features for Attributes
    The following Moose/Moo-like features are supported for attributes:

    `is`
        Supports: bare, ro, rw, rwp, lazy.

    `required`
        If true, indicates that callers must provide a value for this
        attribute to the constructor. If false, indicates that it is optional.

        To indicate that the attribute is *forbidden* in the constructor, use
        a combination of `init_arg => undef` and a strict constructor.

    `init_arg`
        The name of the parameter passed to the constructor which will be used
        to populate this attribute.

        Setting to an explicit `undef` prevents the constructor from
        initializing the attribute from the arguments passed to it.

    `reader`
        You can specify the name for a reader method:

          use Marlin name => { reader => "get_name" };

        If you use `reader => 1` or `reader => true`, Marlin will pick a
        default name for your reader by adding "_get" to the front of
        attributes that have a leading underscore and "get_" otherwise.

        Marlin supports a number of options to keep your accessors truly
        private. (More so than just a leading "_".)

        You can specify a scalarref variable to install the reader into:

          use Marlin name => { reader => \( my $get_name ) };
          ...
          say $thingy->$get_name();

        From Perl v5.12.0 onwards, the following is also supported:

          use Marlin name => { reader => 'my get_name' };
          ...
          say get_name( $thingy );

        From Perl v5.42.0 onwards, the following is also supported:

          use Marlin name => { reader => 'my get_name' };
          ...
          say $thingy->&get_name();

        If you use the 'my get_name' syntax on Perl versions too old to
        support lexical subs, they will be installed as a normal sub in the
        caller package. (Note that the caller package might differ from the
        class currently being built, especially in the case of Marlin::Struct
        classes.)

    `writer`
        Like `reader`, but a writer method.

        If you use `writer => 1` or `writer => true`, Marlin will pick a
        default name for your writer by adding "_set" to the front of
        attributes that have a leading underscore and "set_" otherwise.

        Supports the same lexical method possibilities as `reader`.

    `accessor`
        A combination reader or writer, depending on whether it's called with
        a parameter or not.

        If you use `accessor => 1` or `accessor => true`, Marlin will pick a
        default name for your writer which is just the same as your
        attribute's name.

        Supports the same lexical method possibilities as `reader`.

    `clearer`
        Like `reader`, but a clearer method.

        If you use `clearer => 1` or `clearer => true`, Marlin will pick a
        default name for your clearer by adding "_clear" to the front of
        attributes that have a leading underscore and "clear_" otherwise.

        Supports the same lexical method possibilities as `reader`.

    `predicate`
        Like `reader`, but a predicate method, checking whether a value was
        supplied for the attribute. (It checks `exists`, not `defined`!)

        If you use `predicate => 1` or `predicate => true`, Marlin will pick a
        default name for your predicate by adding "_has" to the front of
        attributes that have a leading underscore and "has_" otherwise.

        Supports the same lexical method possibilities as `reader`.

    `builder`, `default`, and `lazy`
        The `default` can be set to a coderef or a non-reference value to set
        a default value for the attribute.

        As an extension to what Moose and Moo allow, you can also set the
        default to a reference to a string of Perl code.

          default => \'[]'

        Alternatively, `builder` can be used to provide the name of a method
        to call which will generate a default value.

        If you use `builder => 1` or `builder => true`, Marlin will assume a
        builder name of "_build_" followed by your attribute name. If you use
        `builder => sub {...}` then the coderef will be installed with that
        name.

        If you choose `lazy`, then the default or builder will be run when the
        value of the attribute is first needed. Otherwise it will be run in
        the constructor.

        If you use lazy builders/defaults, readers/accessors for the affected
        attributes will be implemented in Perl rather than XS. This is a good
        reason to have separate methods for readers and writers, so that the
        reader can remain fast!

    `constant`
        Defines a constant attribute. For example:

          package Person {
            use Marlin
              ...,
              species => { constant => 'Homo sapiens' };
          }
  
          my $bob = Person->new( ... );
          say $bob->species;

        Constants attributes cannot have writers, clearers, predicates,
        builders, defaults, or triggers. They must be a simple non-reference
        value. They cannot be passed to the constructor. They *can* have a
        type constraint and coercion, which will be used *once* at compile
        time. They can have `handles` and `handles_via`, provided the
        delegated methods do not attempt to alter the constant.

        These constant attributes are still intended to be called as object
        methods. Calling them as functions is *not supported* and even though
        it might sometimes work, no guarantees are provided that it will
        continue to work.

          say $bob->species;      # GOOD
          say Person::species();  # BAD

        If you want that type of constant, use the constant pragma.

    `trigger`
        A method name or coderef to call after an attribute has been set.

        If you use `trigger => 1` or `trigger => true`, Marlin will assume a
        trigger name of "_trigger_" followed by your attribute name.

        Marlin's triggers are a little more sophisticated than Moose's: within
        the trigger, you can call the setter again without worrying about
        re-triggering the trigger.

          use v5.42.0;
  
          package Person {
            use Types::Common -types, -lexical;
            use Marlin::Util -all, -lexical;
    
            use Marlin
              first_name => {
                is      => 'rw',
                isa     => Str,
                trigger => sub ($me) { $self->clear_full_name }
              },
              last_name => {
                is      => 'rw',
                isa     => Str,
                trigger => sub ($me) { $self->clear_full_name }
              },
              full_name => {
                is      => 'lazy',
                isa     => Str,
                clearer => true,
                builder => sub ($me) { join q[ ], $me->first_name, $me->last_name }
              };
          }

        Currently if your class has any triggers, this will force any
        writers/accessors for the affected attributes to be implemented in
        Perl instead of XS. This is a good reason to have separate methods for
        readers and writers, so that the reader can remain fast!

        It is usually possible to design your API in ways that don't require
        triggers.

          use v5.42.0;
  
          package Person {
            use Types::Common -types, -lexical;
            use Marlin::Util -all, -lexical;
    
            use Marlin
              first_name => {
                is      => 'ro',
                isa     => Str,
                writer  => 'my set_first_name',
              },
              last_name => {
                is      => 'ro',
                isa     => Str,
                writer  => 'my set_last_name',
              },
              full_name => {
                is      => 'lazy',
                isa     => Str,
                clearer => true,
                builder => sub ($me) { join q[ ], $me->first_name, $me->last_name }
              };
    
            signature_for rename => (
              method  => true,
              named   => [ first_name => Optional[Str], last_name => Optional[Str] ],
            );
    
            sub rename ( $self, $arg ) {
              $self->&set_first_name($arg->first_name) if $arg->has_first_name;
              $self->&set_last_name($arg->last_name) if $arg->has_last_name;
              return $self;
            }
          }

    `handles` and `handles_via`.
        Method delegation.

        Supports `handles_via` like with Sub::HandlesVia.

        Lexical methods are possible here too.

          use v5.42.0;
  
          package Person {
            use Types::Common -lexical, -types;
    
            use Marlin
              name   => Str,
              emails => {
                is           => 'ro',
                isa          => ArrayRef[Str]
                default      => sub { [] },
                handles_via  => 'Array',
                handles      => [
                  'add_email'       => 'push',
                  'my find_emails'  => 'grep',
                ],
              };
    
            sub has_hotmail ( $self ) {
              my @h = $self->&find_emails( sub { /\@hotmail\./ } );
              return( @h > 0 );
            }
          }
  
          my $bob = Person->new( name => 'Bob' );
          $bob->add_email( 'bob@hotmail.example' );
          die unless $bob->has_hotmail;
  
          die if $bob->can('find_emails');  # will not die

    `isa` and `coerce`
        A type constraint for an attribute.

        Any type checks or coercions will force the accessors and writers for
        those attributes to be implemented in Perl instead of XS.

        You can use `isa => sub { ... }` like Moo.

    `enum`
        You can use `enum => ['foo','bar']` as a shortcut for `isa =>
        Enum['foo','bar']`

    `auto_deref`
        Rarely used Moose option. If you call a reader or accessor in list
        context, will automatically apply `@{}` or `%{}` to the value if it's
        an arrayref or hashref.

    `storage`
        It is possible to give a hint to Marlin about how to store an
        attribute.

          use v5.12.0;
          use Marlin::Util -all, -lexical;
          use Types::Common -types, -lexical;
  
          package Local::User {
            use Marlin
              'username!',  => Str,
              'password!'   => {
                is            => bare,
                isa           => Str,
                writer        => 'change_password',
                required      => true,
                storage       => 'PRIVATE',
                handles_via   => 'String',
                handles       => { check_password => 'eq' },
              };
          }
  
          my $bob = Local::User->new( username => 'bd', password => 'zi1ch' );
  
          die if exists $bob->{password};   # will not die
          die if $bob->can('password');     # will not die
  
          if ( $bob->check_password( 'zi1ch' ) ) {
            ...;  # this code should execute
          }
  
          $bob->change_password( 'monk33' );

        Note that in the above example, setting `is => bare` prevents any
        reader from being created, so you cannot call `$bob->password` to
        discover his password. This would normally suffer the issue that the
        password is still stored in `$bob->{password}` if you access the
        object as a hashref.

        However, setting `storage => "PRIVATE"` tells Marlin to store the
        value privately so it no longer appears in the hashref, so won't be
        included in any Data::Dumper dumps sent to your logger, etc. This does
        complicate things if you ever need to serialize your object to a file
        or database though! (Note that while the value is not stored in the
        hashref, it is still stored *somewhere*. A determined Perl hacker can
        easily figure out where. This shouldn't be relied on in place of
        proper security.)

        Marlin supports three storage methods for attributes: "HASH" (the
        default), "PRIVATE" (as above), and "NONE" (only used for constants).

    `documentation`
        Does nothing, but you can put a string of documentation for an
        attribute here.

   Marlin Options
    Any strings passed to Marlin that have a leading dash are taken to be
    options affecting how Marlin builds your class.

    `-base` or `-parents` or `-isa` or `-extends`
        Sets the parent classes of your class.

          package Employee {
            use Marlin -base => ['Person'], qw( employee_id payroll_number );
          }

        Marlin currently only supports inheriting from other Marlin classes,
        or from Class::XSConstructor, Class::Tiny, Moo, Moose, and Mouse
        classes. Other base classes *may* work, especially if they use blessed
        hashref instances and don't do anything fancy in their constructor.

        Marlin can inherit from classes built with Mite, provided that the MOP
        option was enabled (see Mite::Manual::MOP) and Moose is available.
        (Mite doesn't expose attribute metadata, so Marlin needs to force the
        class to "upgrade itself" to a Moose class.)

        You can include version numbers:

          package Employee {
            use Marlin -base => ['Person 2.000'], ...;
          }

        If you've only got one parent class (fairly normal situation!) you can
        use a string instead of an arrayref:

          package Employee {
            use Marlin -base => 'Person', qw( employee_id payroll_number );
          }

        You can technically manually set your @ISA, but must do it *before*
        Marlin creates your class; otherwise Marlin won't be able to see any
        attribute definitions in parent classes.

          package Employee {
            use Person 1.0;
            BEGIN { @ISA = ( 'Person' ) };
            use Marlin qw( employee_id payroll_number );
          }

        I don't know why you'd want to do that though.

    `-with` or `-roles` or `-does`
        Composes roles into your class.

          package Payable {
            use Marlin::Role -requires => ['payroll_number'];
          }
  
          package Employee {
            use Marlin
              -extends => ['Person'],
              -with    => ['Payable'],
              qw( employee_id payroll_number );
          }

        Marlin classes can accept roles built with Marlin::Role, Role::Tiny,
        Moo::Role, Moose::Role, or Mouse::Role.

        Like `-base`, you can include version numbers.

    `-this` or `-self` or `-class`
        Specifies the name of your class. If you don't include this, it will
        just use `caller`, which is normally what you want.

        The following are roughly equivalent:

          package Person {
            use Marlin 'name!';
          }
  
          use Marlin -this => 'Person', 'name!';

        The main difference is what scope any lexical subs Marlin creates will
        end up in. (And if your version of Perl is too old to support lexical
        subs, the "scope" they will be installed in is actually the caller
        package!)

    `-constructor`
        Tells Marlin to use a constructor name other than `new`:

          package Person {
            use Marlin -constructor => 'create', 'name!';
          }
  
          my $bob = Person->create( name => 'Bob' );

        It can sometimes be useful to name your constructor something like
        `_new` if you wish to create your own `new` method wrapping it.

    `-strict` or `-strict_constructor`
        Tells Marlin to build a constructor like MooX::StrictConstructor or
        MooseX::StrictConstructor, which will reject unknown arguments.

        Since version 0.007000, this is the default.

    `-sloppy` or `-sloppy_constructor` `-loose` or `-loose_constructor`
        Switches off the strict constructor.

        Option introduced in version 0.007000. This was previously the
        default.

    `-mods` or `-modifiers`
        Exports the `before`, `after`, `around`, and `fresh` method modifiers
        from Class::Method::Modifiers, but lexical versions of them.

   Marlin Extensions
    Strings in the `use Marlin` line which start with a colon are used to load
    Marlin extensions.

    For example:

      package Local::Foobar {
        use Marlin qw( foo bar :Clone );
      }

    Will create a class called Local::Foobar with attributes "foo" and "bar",
    but use the Marlin extension Marlin::X::Clone. (This module is bundled
    with Marlin as a demonstration of how to create extensions.)

    Extensions can be followed by a hashref of arguments for the extension:

      package Local::Foobar {
        use Marlin qw( foo bar ), ':Clone' => { try => 1 };
      }

    The `try` argument is special. By setting it to true, it tells Marlin to
    only *try* to use that extension, but carry on if the extension cannot be
    loaded.

    It is also possible to write attribute-specific extensions.

      package Local::Foobar {
        use Marlin foo => { is => 'rw', ':Frobnicate' => {...} };
      }

    This will apply a role "Marlin::XAttribute::Frobnicate" to the
    Marlin::Attribute object that is used to generate accessors, etc.

   Other Features
    `BUILD` and `DEMOLISH` are supported.

   Major Missing Features
    Here are some features found in Moo and Moose which are missing from
    Marlin:

    *   Support for `BUILDARGS`.

        You can work around this by naming your constructor something other
        than `new`, then wrapping it.

    *   The metaobject protocol.

  API
    Marlin provides an API of sorts.

    `my $meta = Marlin->new( @args )`
        Creates an object representing a class, but doesn't build the class
        yet.

    `my $meta = Marlin->find_meta( $class_name )`
        Returns an object representing an existing class or role. Will
        automatically import Moose and Moo classes and roles too.

    `$meta->do_setup`
        Builds the class.

    `$meta->caller`
        Returns the name of the package which called Marlin.

    `$meta->this`
        Returns the name of the class being built.

    `$meta->parents`
        Returns an arrayref of parents. Each parent is itself an arrayref with
        the first element being the class name and the second element being a
        version number.

    `$meta->roles`
        Returns an arrayref of roles. Each role is itself an arrayref with the
        first element being the class name and the second element being a
        version number.

    `$meta->attributes`
        Returns an arrayref of attributes defined in this class. Includes
        attributes from composed roles, but not inherited attributes from
        parent classes. Each attribute is either a hashref or a
        Marlin::Attribute object.

        Calling `$meta->canonicalize_attributes` will replace any hashrefs in
        this list with Marlin::Attribute objects. That method is chainable, so
        the best way to get a list of Marlin::Attribute objects is: `@{
        $meta->canonicalize_attributes->attributes }`.

    `$meta->attributes_with_inheritance`
        Like `$meta->attributes`, but includes parent classes.

    `$meta->strict`
        Boolean indicating if the constructor will be strict.

    `$meta->constructor`
        Name of the constructor method. Usually "new".

    `$meta->modifiers`
        Boolean indicating whether Marlin should export
        Class::Method::Modifiers keywords into the package.

    `$meta->inhaled_from`
        Usually undef, but may be "Moose", "Moose::Role", "Moo", "Moo::Role",
        "Mouse", "Mouse::Role", "Class::Tiny", or "Class::XSConstructor" to
        indicate that this metadata was imported from another OO framework.

    `$meta->short_name`
        The package name without any colons. This is used in the
        stringification provided by `to_string`.

    `$meta->is_struct`
        Boolean indicating that the class was created by Marlin::Struct.

    `$meta->to_string( $object )`
        Stringifies the object to a representation useful in debugging, etc.

    `$meta->to_arrayref( $object )`
        Creates an arrayref representation of the object which closely
        resembles the string representation.

    `Marlin->can_lexical`
        Returns true if Marlin is running in an environment that supports
        lexical subs.

BUGS
    Please report any bugs to <https://github.com/tobyink/p5-marlin/issues>.

SEE ALSO
    Marlin::Role, Marlin::Struct, Marlin::Util, Marlin::Manual::Principles,
    Marlin::Manual::Comparison.

    Example extensions: Marlin::X::Clone, Marlin::XAttribute::Alias,
    Marlin::XAttribute::LocalWriter, Marlin::XAttribute::Lvalue.

    Modules that Marlin exposes the functionality of: Class::XSAccessor,
    Class::XSConstructor, Types::Common, Type::Params, and Sub::HandlesVia.

    Inspirations: Moose and Moo.

    See also: MooseX::Marlin, MooX::Marlin.

AUTHOR
    Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE
    This software is copyright (c) 2025 by Toby Inkster.

    This is free software; you can redistribute it and/or modify it under the
    same terms as the Perl 5 programming language system itself.

DISCLAIMER OF WARRANTIES
    THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
    MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

    🐟

