Possible way to achieve lazy loading with const objects

Peter Alexander peter.alexander.au at gmail.com
Sat Sep 24 04:19:33 PDT 2011


On 24/09/11 5:11 AM, Jonathan M Davis wrote:
> Okay. I'm not saying that we should necessarily implement this. I'm just
> looking to air out an idea here and see if there are any technical reasons why
> it can't be done or is unreasonable.
>
> Some programmers have expressed annoyance and/or disappointment that there is
> no logical const of any kind in D. They generally seem to be trying to do one
> of two things - caching return values in member functions or lazily loading
> the values of member variables. I really don't know how we could possibly do
> caching with const, but I _do_ have an idea of how we could implement lazy
> loading. Here's what it looks like syntactically:
>
> struct S
> {
>      lazy T var = func();
> }
>
> The lazy indicates that var is going to be lazily loaded, and func returns the
> value that var will be initialized with. However, instead of being a normal
> variable of type T, this is what happens to var:
>
> 1. Instead of a member variable of type T, S gets a bool (e.g. __varLoaded)
> and a variable of type T (e.g. __var).
>
> 2. __varLoaded is default-initialized to false, and __var is void (so,
> garbage).
>
> 3. Every reference to var is replaced with a call to a getter property
> function (e.g. __varProp). There is no setter property.
>
> 4. __varProp looks something like this:
>
> T __varProp()
> {
>      if(!__varLoaded)
>      {
>          __var = func();
>          __varLoaded = true;
>      }
>
>      return __var;
> }
>
> 5.  __varProp may or may not be inlined (but it would be nice if it would be).
>
> 6.  If the S being constructed is shared or immutable and __varProp is not
> called in the constructor, then __varProp is called immediately after the
> constructor (or at the end of the constructor if that works better for the
> compiler).
>
> 7. An opCast is added to S for shared S and immutable S which calls __varProp
> - or if such on opCast already exists, the call to __varProp is added at the
> end of it.
>
>
> The result of all of this is that the value of var is constant, but it isn't
> calculated until it's asked for. It doesn't break const at all, since the
> compiler can guarantee that altering the value of __varLoaded and __var is
> safe. And since the value is eagerly loaded in the case of immutable and
> shared, immutable  and shared don't cause any problems.
>
> So, the question is: Does this work? And if not, why? And if it _does_ work,
> is it a good idea? And if not, why?
>
> Again, I'm not necessarily suggesting that we implement this right now, but it
> at least _seems_ like a viable solution for introducing lazy loading in const
> objects, and I'd like to know whether there's a good possibility that it will
> actually work to implement something like this in the compiler. If it _is_
> feasible, and we want to actually do it, since it's backwards compatible (as
> far as I can tell anyway), we can implement it at some point in the future
> when D has stabilized more, but I thought that the idea was at least worth
> dicsussing.
>
> I'm not at all convinced that the added complexity to the language and to the
> compiler is worth the gain, but there are a number of programmers who want
> some sort of lazy-loading ability for member variables in const objects, and
> this seems to provide that.
>
> - Jonathan M Davis

Lazy loading and caching are the same thing.

struct Foo
{
     T m_lazyObj = null;
     bool m_isLoaded = false;

     T getObj()
     {
         if (!m_isLoaded)
         {
             m_lazyObj = func();
             m_isLoaded = true;
         }
         return m_lazyObj;
     }
}

Change m_lazyObj to m_cachedObj and m_isLoaded to m_isCached and you 
have caching.

A problem with your suggestion is that it can only handle one load. It's 
very often the case that you need to mark the object as 'dirty' and set 
the flag back to false so that it gets reloaded.

I'm happy to not have logical const in D provided that the Object 
interface (and other similar interfaces) don't require that opEquals is 
const or any nonsense like that. const means physical const, and 
opEquals should not require physical const.

IMO const/immutable should *only* be used when you need to pass things 
between threads i.e. when you *really do* need physical const. If people 
start using const like you would in C++ then every interface just 
becomes unnecessarily restrictive.


More information about the Digitalmars-d mailing list