Reputation: 1227
What is the best way to build attributes lazily?
class I {
has $!cheezeburger;
method cheezeburger {
given $!cheezeburger {
when .so {return $_}
default {
# build $cheezeburger, set attribute to it, return
}
}
}
}
That's a lot of cheezeburger. What might be a less verbose way?
Upvotes: 6
Views: 232
Reputation: 32404
There are two lazy attribute modules.
Brad's $!cheezeburger //= do { ... };
seems like a fairly straight-forward solution that'll suffice for many use cases.
You may find that #perl6 folk want or can provide something better.
The most recent serious #perl6 discussions I'm aware of about lazy attribute initialization happened during 2015 on May 5th, 7th, 20th and June 5th, 8th, and 20th. Search for "will lazy" in pages of #perl6 log with at least one "will lazy" match. The TL;DR of these discussions is that rjbs, mst, and other Moose users were used to nice lazy attribute initialization and a solution was added to Rakudo; it was then backed out because masak and others thought it had issues and they thought nice solutions could be created in module space and then moved back in to core if/when that seemed wise.
Upvotes: 5
Reputation: 169573
The pragmatic solution given by Brad that initializes the attribute if it is undefined should be good enough for many cases:
class Foo {
has $!cheezeburger;
method cheezeburger {
$!cheezeburger //= do { ... }
}
}
Another approach would be using does
to replace the accessor method by mixing in a role during its first call, using black magic (aka NQP ops) to access the private attribute:
class Foo {
has $!cheezeburger;
method cheezeburger {
self does role {
method cheezeburger {
use nqp;
nqp::getattr(self, Foo, '$!cheezeburger');
}
}
$!cheezeburger = do { ... }
}
}
Upvotes: 3
Reputation:
class A {
has $!lazy;
method BUILD { $!lazy := Nil };
method lazy { $!lazy := (my $a = 42) if $!lazy =:= Nil; $!lazy }
};
my $a = A.new;
say [$a.lazy, $a.lazy];
If $!lazy
is meant to hold undefined values you need to hop through a few loops. First we bind Nil
to $!lazy
to hold a value a container should not be able to hold. If $!lazy
is still bound to Nil
we create a new container and assign a value to it. If the value is immutable, you don't need the extra container. Any type constraint you need on $!lazy need to be on $a
because constraints are a property of a container not the variable/class-property.
Upvotes: 4