Perl's OO framework, Moose, has got a very nice feature that is Lazy
Attributes. Regular ones are assigned by construction: they can be
checked automatically to be explicitly set (required
), and they can be
undefined only if allowed by the type (Maybe[T]
can be undefined, while
T
must be defined). If not required, attributes can be declared as lazy,
and an automatic procedure can be defined for when they are accessed, so
they get automatically generated upon the first access.
But what if you are not using Moose?
Moose is extremely fancy, but not always what you need. Sometimes OO is an overkill, and you simply want plain old perl. Still you would like to get some lazyness.
Say for instance we have a command with different operating modes foo
,
bar
and baz
. The first two modes need a particular data structure
named $magic
to be in place, which is not required in the third mode.
The $magic
structure is generated by the load_magic
function, which is
expensive.
A simple way of getting it done is of course the following:
my $magic
sub load_magic {
my %out;
#... here monkeys ...
$out{huge} = $huge;
$out{slow} = $slow;
return \%out;
}
if (mode eq 'foo') {
$magic = load_magic;
foo($magic->{huge}, $magic->{slow});
} elif (mode eq 'bar') {
$magic = load_magic;
# Do other stuff with $magic
bar($magic->{huge}, $magic->{slow});
} elif (mode eq 'baz') {
# Do more stuff, no $magic involved
baz();
}
This works just nice, and it's also easy to understand.
Things may however get complicated. What if instead of mutually exclusive modes we have optional chunks of execution?
if ($required{foo}) {
$magic = load_magic;
foo($magic->{huge}, $magic->{slow});
}
if ($required{bar}) {
$magic = load_magic unless defined $magic;
bar($magic->{huge}, $magic->{slow});
}
if ($required{baz}) {
baz();
}
…Here it's still simple, but we have got to keep duplicating the conditional.
Equivalent but lazy: merge $magic
into load_magic
with a state
variable:
use feature qw/state/;
sub magic {
state $magic = do {
my %out;
# ... here monkeys ...
$out{huge} = $huge;
$out{slow} = $slow;
\%out;
}
}
The state
feature will help us in keeping the state and initializing it
once. The do
block will be therefore executed just on the first time
magic
is called, while subsequent calls will just run the remaining part
of the magic
function, that is …nothing.
A function not using return
will implicitly return the last computed
value, which here is $magic
.
In a sense, it's like the magic
function is a simple Object with only
one method. It keeps the context. It can also be seen as a singleton in
Java.
Now we can simply accomplish our task with three simple lines:
foo(magic->{huge}, magic->{slow}) if $required{foo};
bar(magic->{huge}, magic->{slow}) if $required{bar};
baz if $required{baz};
Simple and elegant.