DEV Community

DragosTrif
DragosTrif

Posted on • Edited on

6 1

Perl Roles

1. Roles Definition

A role is a set of methods that provide extra behavior to a class. Roles can't be used independently they need a class to consume them. Roles are a good alternative to inheritance.

2. A sample role

A role is declared in a *.pm file.

package Role::JSON;
use Moose::Role;

use JSON 'encode_json';

requires qw( data );

sub to_json {
  my $self = shift;

  return encode_json( $self->data() );
}

1;
Enter fullscreen mode Exit fullscreen mode

3. Consuming a role

In the previous example the Role::JSON requires
the consuming class to have a method named data().

package Foo;
use Moose;
with "Role::JSON";

sub data {
 my $self = shift;
 return { foo => 'bar' }; 
}

__PACKAGE__->meta->make_immutable;

1;
Enter fullscreen mode Exit fullscreen mode

Then you could just call the method defined in the role in your program:


my $obj = Foo->new();

print $obj->to_json();

# And that prints the following JSON
# { foo : 'bar' }

Enter fullscreen mode Exit fullscreen mode

4. Checking if class consumes a role

Because roles are not inherited you cannot use isa() to check if a class consumes a role instead you should use does():

$object->does("Role::JSON");
Enter fullscreen mode Exit fullscreen mode

5. Roles without Moose/Moo

Cpan module Role::Tiny allows you to use roles with vanilla OOP not just with Moose or Moo. Like Moose or Moo, Role::Tiny applies strict and warnings to the caller.

package Role::Foo;
use Role::Tiny;
sub data { 
 my $self = shift; 
 return { foo => 'bar' };
}
1;

package Bar;
use lib 'lib';
use Role::Tiny::With;
with  'Role::Foo';
....
1;
Enter fullscreen mode Exit fullscreen mode

Role::Tiny makes available to your role the following method modifiers: before, around and after.
In this example when $self->data is called the around block executes and you get JSON returned.

package Role::JSON;
use Role::Tiny;
use JSON 'encode_json';

requires qw( data );

around data => sub  {
  my $orig = shift;
  my $self = shift;

  return encode_json( $self->$orig() );
};  

Enter fullscreen mode Exit fullscreen mode

6. Compositional safety

Roles attempt to guarantee compositional safety. So, if two roles have the same method defined and you try to consume them in the same class, you will get an error message.

 Due to method name conflicts in roles ....
Enter fullscreen mode Exit fullscreen mode

Just remember that in order to trigger the error message you need to consume all the roles at once:

# good
package Foo;
use lib 'lib';
use Role::Tiny::With;
with  'Role::XML', 'Role::JSON';

# bad second method is ignored
package Foo;
use lib 'lib';
use Role::Tiny::With;
with  'Role::XML'; 
with  'Role::JSON';

Enter fullscreen mode Exit fullscreen mode

7. How to fix method collision

  • implement the methods yourself in your class, thus causing the corresponding role methods to be ignored

  • For Moose use the excludes key word

package Role::JSON;
use Moose::Role;

sub serialize { ... }

package Role::XML;
use Moose::Role;

sub serialize { ... }

package Foo;
use Moose;
with Role::Serializable::JSON,
     Role::Serializable::XML => { excludes => 'serialize' };
Enter fullscreen mode Exit fullscreen mode
  • For Role::Tiny use namespace::clean
package Role::XML;

use Role::Tiny;

sub serialize { my $self = shift; print 'test'  };
# serialize() will not be imported in the consuming class
use namespace::clean;

1;
Enter fullscreen mode Exit fullscreen mode

8. Bibliography

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

Top comments (2)

Collapse
 
matthewpersico profile image
Matthew O. Persico

This is very well done. Kudos on a having a Bibliography. Would it be safe to say, and worth adding to the article, that roles are also known as "interfaces"?

Collapse
 
dragostrif profile image
DragosTrif

A role could have only a 'requires' section and no methods or method modifiers. Such a behavior fits the definition of an interface.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay