Python::Decorator - Function composition at compile-time
use Python::Decorator; # the 2 lines above 'sub incr' are Python-style decorators. # they add memoizing and debugging behaviors to incr() @memoize # decorator without arguments @debug("incr") # decorator with arguments sub incr { return $_[0]+1; }
This code:
use Python::Decorator; @memoize sub incr { return $_[0]+1; }
is really just the same as this one:
{ no strict 'refs'; *{__PACKAGE__."::incr"} = memoize( sub { return $_[0]+1; } ); }
In fact, the syntax:
@foo sub bar {
reads as: "upon compiling bar(), redefine bar() to be the function returned by foo(&bar). Or in functional programming terms, replace bar by the composition of foo o bar.
bar()
foo(&bar)
bar
foo o bar
The function foo() is called a decorator because it 'decorates' bar by adding some new behavior to it. foo is a higher order function: it takes a function to decorate and returns the new decorated function.
foo()
foo
As in Python, you can pass arguments to the decorator:
@mylog("incr") # log calls to incr() sub incr { return $_[0]+1; }
becomes:
{ no strict 'refs'; *{__PACKAGE__."::incr"} = mylog("incr")->( sub { return $_[0]+1; } ); }
Notice that a decorator that takes arguments does not behave in the same way as one that takes no arguments. In the case above, the function mylog() takes some arguments and returns a function that acts as a no argument decorator.
mylog()
As in Python, you can apply multiple decorators to one subroutine, hence composing multiple functions in one:
# replace incr by proxify(mylog(memoize(incr))) @proxify @mylog("incr") @memoize sub incr { return $_[0]+1; }
{ no strict 'refs'; *{__PACKAGE__."::incr"} = proxify(mylog("incr")->(memoize( sub { return $_[0]+1; } ))); }
Finally, if you want to see what Python::Decorator really does to the source, call it with:
use Python::Decorator debug => 1;
Decorators are syntax sugar for function composition at compile-time.
That's it, really. But despite this apparent simplicity, decorators enable powerfull expressions by enabling a more functional approach to programming.
Decorators were introduced in Python 2.4 (end of 2004) and have proven since to provide functionality close to that of macros in LISP. There are also related to aspect oriented programming (AOP), though AOP can be seen as a special use case for decorators. For a complete description of Python decorators, ask google or check out the links in the 'SEE ALSO' section.
Notice that our decorators are not related in any way to the design pattern of the same name.
Python::Decorator implements the decorator syntax for Perl5, using exactly the same syntax as in Python. A decorator therefore looks like either one of:
@<decorator-sub> sub decorated-sub {}
or
@<decorator-sub>(@some,@perl,%arguments) sub decorated-sub {}
where <decorator-sub> is the name of a subroutine that will decorate the subroutine decorated-sub. The @ marks the beginning of a decorator expression. The decorator expression ends without ';' and the decorator arguments (if any) are usual Perl arguments.
<decorator-sub>
decorated-sub
@
Python::Decorator is a source filter, meaning it manipulates source code before compilation. Subroutines are therefore decorated at compile-time.
This module is a proof-of-concept in at least 2 ways:
There is no consensus as to what syntax macros or function composition should have in Perl. Therefore Python::Decorator implements decorators using Python's own syntax instead of trying to introduce an arbitrary perlish syntax. If this module proves usefull, someone will have to clone it into something more perlish.
This module experiments using PPI to parse and modify Perl5 source code within a source filter. Though this is a slow and somewhat unstable technique, I believe it is a way to go to get macros working in Perl.
Those functions are for internal use only:
import
filter
Use in production code is REALLY NOT RECOMMENDED!
See PPI, Filter::Util::Call, Aspect. About Python decorators, see:
http://www.phyast.pitt.edu/~micheles/python/documentation.html http://www.artima.com/weblogs/viewpost.jsp?thread=240808
Check first whether it is a PPI issue. Otherwise, report to the author!
$Id: Decorator.pm,v 1.6 2008-11-05 20:56:42 erwan Exp $
Erwan Lemonnier <erwan@cpan.org>.
<erwan@cpan.org>
This code is provided under the Perl artistic license and comes with no warranty whatsoever.
To install Python::Decorator, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Python::Decorator
CPAN shell
perl -MCPAN -e shell install Python::Decorator
For more information on module installation, please visit the detailed CPAN module installation guide.