One way to fix our above implementation is to add a state hash variable that'll hold all previous calculations that were already performed, and skip calculation for known values. Decorators will give us a way to "inject" the described functionality to the function without changing its source code.

Our First Decorator: Log Inputs and Output

A decorator is a higher order function which takes a function and returns a new one with some modified behaviour. Let's start with a simple example that just adds a debug print before and after calling the function.

The decorator code itself to add log messages would look something like this:

A Second Example: Call Count

Higher order functions can also save data in a special way. Every variable defined inside the external (higher order function) but above the internal result function will maintain its value between invocations of the internal function.

This allows us to add memory to our decorated function. The following decorator prints how many times a function was called before calling it:

Function was called 1 times at a.pl line 16.
Calling function with: 10 at a.pl line 30.
Got: 20 at a.pl line 32.
20
Function was called 2 times at a.pl line 16.
Calling function with: 20 at a.pl line 30.
Got: 40 at a.pl line 32.
40

Installing The Decorator

So far we were able to use higher order functions to modify behaviour or existing functions, but we always had to call the newly created function by reference. I think that felt a bit odd.

Luckily perl lets us modify the package's symbol table by using typeglobs, so this code works:

*{main::twice} = logged(\&twice);
say twice(10);

Or for a more generic version which installs the decorator into the current package:

And as a bonus it also allows using multiple decorators. Here's a sample usage:

decorate('main::twice', \&counted, \&logged);
twice(5);

Back To Fixing Fib

I belive now we're ready to fix our broken fib function using a decorator. The decorator is called memoize and its job is to remember past invocations and results, and if a function is called for a second time with the same input, the value is fetched from memory. This saves fib the need to perform the same calculations over and over again.

Caveats

Decorators are a great pattern to have in your toolbox. When writing this post I skipped some issues that may be of importance in your code:

When calling a decorated function we change its context. In the general case you probably want to use wantarray.

Decorators may add metadata to functions (for example call count). Unfortunately perl offers no good place to store this data. The best I found was to add a new function to the symbol table that fetches this data. Better ideas are welcomed.

Symbol table manipulation in perl may be a bit slow. If that's a problem to you it may be a good idea to implement decorate as XS.