How do Perl method attributes work?

The famous Perl built-in function is attributes. However, the official documentation does a pretty poor job of introducing newcomers to the concept. At the same time, frameworks such as Catalyst make extensive use of attributes, which seem to make things much easier there. Since using something, not knowing that the consequences are absorbed a little, I would like to know the details. Syntactically, they look like Python decorators, but the documentation implies something simpler.

Could you explain (if possible, examples from the real world) which attributes fit and what happens behind the door?

+45
perl attributes
Jun 12. '09 at 2:51 p.m.
source share
3 answers

You are right, the documentation is not very clear in this area, especially since the attributes are not so complicated. If you define a subroutine attribute, for example:

sub some_method :Foo { } 

Perl will be when compiling your program (this is important) look for the magic sub MODIFY_CODE_ATTRIBUTES in the current package or any of its parent classes. This will be called with the name of the current package, a link to your subroutine, and a list of attributes defined for that subroutine. If this handler does not exist, compilation will fail.

What you do in this handler is completely up to you. Yes, it's right. No hidden magic at all. If you want to report an error, then when the name of the violating attributes is returned, compilation will end with the message "invalid attribute".

There is another handler called FETCH_CODE_ATTRIBUTES that will be called whenever someone says

 use attributes; my @attrs = attributes::get(\&some_method); 

This handler receives the passed package name and subroutine and should return a list of attributes of the subroutine (although what you really do is up to you again).

Here is an example to enable a simple “labeling” of methods with arbitrary attributes, which you may request later:

 package MyClass; use Scalar::Util qw( refaddr ); my %attrs; # package variable to store attribute lists by coderef address sub MODIFY_CODE_ATTRIBUTES { my ($package, $subref, @attrs) = @_; $attrs{ refaddr $subref } = \@attrs; return; } sub FETCH_CODE_ATTRIBUTES { my ($package, $subref) = @_; my $attrs = $attrs{ refaddr $subref }; return @$attrs; } 1; 

Now in MyClass and in all its subclasses you can use arbitrary attributes and query them using attributes::get() :

 package SomeClass; use base 'MyClass'; use attributes; # set attributes sub hello :Foo :Bar { } # query attributes print "hello() in SomeClass has attributes: ", join ', ', attributes::get(SomeClass->can('hello')); 1; __END__ hello() in SomeClass has attributes: Foo, Bar 

In general, attributes do not do very much, which, on the other hand, makes them very flexible: you can use them as real "attributes" (as shown in this example), implement something like decorators (see Sinan's answer ) or for your own insidious purposes.

+36
Jun 12 '09 at 15:27
source share
— -
+11
Jun 12 '09 at 15:15
source share

Attributes are one of the things that, if you do not know how to use them, you should not worry about them. I once made a database_method attribute to tell the system that a set of records would be requested before entering this method, and that the method knew that the main inputs would come from the stored procedure to which it corresponded.

I used attributes to wrap the actual, specified actions with this data. So, one of the really seeming useful ideas is to wrap methods with indirection, but it was harder to do the work with the caller without redefining it. In the end, it was too noticeable as an “expert-only” feature and would require support to track secret intranas - something you want to avoid if you write Perl in a perl-shop.




People may want to vote for me, but I take from an article quoted by Sinan:

Warning

Although this is a powerful method, it is not perfect. The code will not properly wrap anonymous subroutines, and it will not necessarily extend the call context to wrapped functions. In addition, using this technique will significantly increase the number of subprogram managers that your program must execute at run time. Depending on the complexity of your program, this can significantly increase the size of the call stack. If blinding speed is the main goal of the project, this strategy may not be for you.

These are significant disadvantages if you do not want to override caller . I don't care about blinding speed, and I'm almost ready to try my hand at redefining caller to bypass any subroutine that registers as DO_NOT_REPORT - but I have some harmless encoding that hasn't beaten me up yet.

Even the article admits how poorly documented this feature is and contains this warning. Tell me, when was it still nice to use a fascinating, obscure feature? This is often enough that people insert UNIVERSAL into the namespace to avoid the inheritance problem.

(But if you think this is a bad answer, only one lower downward sign will give me a sign of peer pressure: D)

+5
Jun 12 '09 at 15:01
source share



All Articles