An uncommon, but not rare, situation that I run into from time to time, is that there are variables used either globally or as a member of a class that are expensive in their resource usage (time to compute, or memory size), but which are only sometimes used, e.g. only when certain functions are called, which depends on the runtime usage of the program.
The concept of lazy initialization is used in these situations to avoid paying this cost up front, and deferring it until the value is used. E.g. if your object has a member named value
, then obj.value
may refer to a method that returns a cached value, and if the cache is empty, produces the value and caches it.
However, in D, such usage runs into problems when the object/method is either const
or immutable
. Making such changes requires modifying a variable that technically existed at the time obj
was created.
While working on a project, I encountered a solution in Dart which I think could be carried over into D:
https://dart.dev/guides/language/language-tour#late-variables
When you mark a variable as late but initialize it at its declaration, then the initializer runs the first time the variable is used. This lazy initialization is handy in a couple of cases:
The variable might not be needed, and initializing it is costly.
You’re initializing an instance variable, and its initializer needs access to this.
In the following example, if the temperature variable is never used, then the expensive readThermometer() function is never called:
// This is the program's only call to readThermometer().
late String temperature = readThermometer(); // Lazily initialized.
The already existing keyword lazy
could be used for this purpose in D, and then it would be possible to write code such as:
class Thing {
lazy immutable String piDigits = computePiDigits();
String computePiDigits() { ... }
double getTemperature() const {
// If only this function is used, piDigits is never computed.
}
double getCircumferance() const {
// Some silly usage of piDigits, which causes computePiDigits() to be run.
return piDigits.to!double * radius * 2.0;
}
}
Is there another way to perform lazy initialization that is compatible with const/immutable, or is this needed as a language feature?