oop intermediate

How to use coercion with Moo attributes in Perl?

Question

How to use coercion with Moo attributes in Perl?

In Moo, attribute coercion is not built-in like in Moose, but you can implement it with Type::Tiny-based tools such as Type::Utils and Types::Standard. The key is to declare a type constraint and define coercions explicitly, then enable coercion on your attribute.

The previous example failed because Dumper was not correctly imported or called: calling Dumper as a method (e.g. $obj->Dumper) causes an undefined subroutine error. Instead, you must use use Data::Dumper; and call Dumper() as a standalone function.

Working Example: Attribute Coercion with Moo Using Type::Tiny

use strict;
use warnings;

use Moo;
use Types::Standard qw(ArrayRef Str);
use Type::Utils qw(declare as coerce from via);
use Data::Dumper;

# Declare a new type constraint for ArrayRef of Str
my $TagArrayType = declare
  as ArrayRef[Str];

# Coerce from Str to ArrayRef[Str]
coerce $TagArrayType,
  from Str,
  via { [$_] };

# Coerce from ArrayRef (identity)
coerce $TagArrayType,
  from ArrayRef,
  via { $_ };

{
    package MyClass;
    use Moo;
    use Types::Standard qw(ArrayRef Str);
    use Data::Dumper;

    has tags => (
        is     => 'rw',
        isa    => $TagArrayType,
        coerce => 1,
        default => sub { [] },
    );

    sub dump_tags {
        my ($self) = @_;
        print Dumper($self->tags);
    }
}

# Create objects with and without coercion
my $obj1 = MyClass->new(tags => 'single_tag');         # Coerced into ['single_tag']
my $obj2 = MyClass->new(tags => ['tag1', 'tag2']);     # Passed as-is

print "Object 1 tags:\n";
$obj1->dump_tags();

print "Object 2 tags:\n";
$obj2->dump_tags();

Explanation

  • declare as ArrayRef[Str] creates a type constraint object for arrays of strings used for attribute validation.
  • The coerce function adds conversion rules from Str to ArrayRef[Str], and identity from ArrayRef.
  • Setting coerce => 1 on the tags attribute enables automatic conversion from input values according to the coercion rules.
  • Data::Dumper::Dumper() is called as a standalone function, not a method, so it prints out the coerced attribute correctly.
  • This example works out of the box with core modules plus Moo and Type::Tiny ecosystem installed.

Common Pitfalls

  • Calling Dumper as a method ($obj->Dumper) triggers errors; use print Dumper($data) instead.
  • Without assigning the result of declare as ... to a variable, coercion won't work.
  • Only one from => source per coerce call is allowed.
  • This method requires the Type::Tiny ecosystem modules (Type::Utils, Types::Standard) available in your environment.

Summary

Although Moo doesn't automatically support attribute coercion like Moose, you can effectively add it using the Type::Tiny ecosystem. Declare your types, define coercions explicitly, and enable coercion on attributes to accept multiple input forms gracefully. Always remember to call utility functions like Dumper correctly to avoid runtime errors.

Verified Code

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

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
Object 1 tags:
$VAR1 = [
          'single_tag'
        ];
Object 2 tags:
$VAR1 = [
          'tag1',
          'tag2'
        ];
STDERR
(empty)

Was this helpful?

Related Questions