oop intermediate

How to use type constraints with Moo attributes in Perl?

Question

How to use type constraints with Moo attributes in Perl?

In Moo, a lightweight object system for Perl, adding type constraints to attributes helps enforce that attribute values meet certain criteria. While Moo itself doesn’t include a built-in type constraint system like MooX::Types::MooseLike or Moose, you can still apply type constraints in a flexible, idiomatic way using the isa option on attributes with a code reference for validation. This offers a simple, manual approach to attribute validation without pulling in heavy dependencies.

Using the isa option with a coderef

In Moo, the has function for declaring attributes accepts an isa parameter, which can be a subroutine reference that validates the attribute’s value each time it’s set. If the validation fails (sub returns false), Moo throws an exception.

This lets you implement custom type constraints like:

  • Checking for specific Perl types (string, integer)
  • Ensuring values match regex patterns
  • Validating complex structures like array refs or hashes

This approach reflects Perl’s TMTOWTDI (There’s More Than One Way To Do It) philosophy by giving you control over the validation logic.

Example: Attribute with a type constraint using isa coderef

use strict;
use warnings;
use Moo;

{
    package Person;
    use Moo;

    has 'name' => (
        is  => 'ro',
        isa => sub {
            my $val = shift;
            die "Name must be a non-empty string"
              unless defined $val && $val =~ /\S/;
        },
        required => 1,
    );

    has 'age' => (
        is  => 'rw',
        isa => sub {
            my $val = shift;
            die "Age must be an integer >= 0"
              unless defined $val && $val =~ /^\d+$/ && $val >= 0;
        },
        required => 1,
    );

    # Example of a more complex constraint for an arrayref of strings
    has 'tags' => (
        is  => 'rw',
        isa => sub {
            my $val = shift;
            die "Tags must be an array reference"
              unless ref $val eq 'ARRAY';
            die "Each tag must be a non-empty string"
              unless grep { !defined $_ || $_ eq '' } @$val == 0;
        },
        default => sub { [] },
    );
}

# Demonstration of usage
eval {
    my $p = Person->new(name => "Alice", age => 30);
    print "Created person: ", $p->name, ", age ", $p->age, "\n";

    $p->age(35);
    print "Age updated to: ", $p->age, "\n";

    # This will fail due to invalid age
    $p->age(-5);
};
if ($@) {
    print "Caught error: $@";
}

Key points

  • isa should be a subroutine that dies with an error if the validation fails.
  • The validation runs on initial construction and also on any further assignment (if the attribute is writable).
  • You can keep type constraints as simple or complex as needed.
  • This pattern works with Perl 5.8 and newer, compatible with all major Moo versions.

Common pitfalls

  • Make sure your isa subs die on validation failure — silent failures won’t prevent bad data.
  • Consider edge cases (undef values, empty strings) explicitly depending on your type needs.
  • If you want richer type checking (coercions, complex variants), look into adding MooX::Types::MooseLike or migrating to Moose.

In summary, Moo provides a straightforward mechanism for type constraints by passing an isa coderef to has. This fits well with Perl’s flexible approach and avoids the complexity and dependencies of more heavyweight solutions.

Verified Code

Executed in a sandbox to capture real output. • v5.34.1 • 36ms

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
Caught error: isa check for "tags" failed: Each tag must be a non-empty string at - line 37.
STDERR
(empty)

Was this helpful?

Related Questions