Fully transitive const is not necessary

Steven Schveighoffer schveiguy at yahoo.com
Tue Apr 1 07:33:46 PDT 2008


I have been reading through the reddit posts about the const faq, and also 
some posts here by guslay, and I believe Walter needs to re-think his 
beliefs about how transitive const is necessary for the future.

First, I think transitive const should be the default.  I am not a fan of 
C++'s head-const, where only the head is const, and all data referenced 
through the head is mutable.  I think this promotes "shoot yourself in the 
foot" code.  It is a valuable tool to have the compiler tell you "hey, you 
wanted this to be const, remember?"  At the same time it is also useful to 
tell the compiler "hey, I want this to not be affected by const, because 
it's not logically part of the state of the object."

Second, I'll show how logical const is ALREADY available with D's const, 
just in a less convenient form.  Let's look at an invariant property that is 
not invariant:

class X
{
   private static int _x;
   invariant int x()
   {
       return _x++;
   }
}

Notice that despite all efforts of the compiler to make sure invariant is 
transitive, it still cannot do any parallelizations or optimizations on the 
call to x.  If it does not have the source code for x, it cannot be sure 
that x isn't doing something like the above.

We can translate this into a form where we keep an associative array that 
maps each instance to a mutable struct that can be the non-const portion of 
the instance, so each instance can be separate:

class X
{
    private static int[X] _x;
    this()
    {
        _x[this] = 0;
    }

    ~this()
    {
         _x.remove(this);
    }

    invariant int x()
    {
       int *myx = this in _x;
       return (*myx)++;
    }
}

The problem is, you can think of each function not only passing the 
arguments, and possibly a this pointer, but passing a context pointer to the 
global namespace, which is never const.  As long as that remains mutable, we 
can store mutable data associated with a class in that space.  In order to 
allow multiprogramming, you need to ensure that the function does not access 
that space, unless the data is invariant.  But this is called a 'pure' 
function, and can exist WITH logical const and invariant.  If it couldn't, 
then it couldn't exist with today's version of const, which allows logical 
const and invariant as I have demonstrated.

Here is my interpretation of all this:

const is a tool for developers, not for the compiler.  Since const data can 
change at any time, it cannot be used for multithreaded or other 
optimizations.

invariant allows for some optimization on the compiler.  Invariant 
optimization is limited to POD values, because invariant functions can 
always be logically invariant, and there is no way to detect it.

pure is a tool for multiprogramming.  This allows all the fun stuff that 
Walter is always talking about.  pure can co-exist with logical const and 
logical invariant, because if it couldn't, then pure couldn't exist in 
today's version of const.

Since const is a tool for developers, and CANNOT BE USED for optimization, 
let's please have logical const in a form that performs better and is easier 
to implement than the above example.  Something like C++'s mutable (but not 
exactly that, I'll explain below).

Since invariant functions cannot be guaranteed as pure functions, let's have 
logical invariance in a form that performs better and is easier to implement 
than the above example.  Optimizations based on POD types can still be had 
even with logical invariance.

Head const by default sucks, and still does, because const is less useful as 
a tool in that state.  However, as I have shown, head const cannot be 
avoided, and so why not support it as the exception to the transitive const 
rule?

mutable is used as the keyword in C++ to describe this type.  However, I do 
not like this word.  For example, whatever keyword is used, it should be 
usable just like const or invariant:

mutable(X)* refToX;

This would describe a reference to a mutable X.  adding const to this would 
then make the reference const, but the thing it points to mutable, giving us 
a head-const variable.

But what if X is an alias to const(Y)?  The statement implies that 
mutable(X) makes X mutable, even if it is was previously const or invariant, 
but this would be no good.  We need a word that says "this isn't affected by 
const or invariant, but it could already be const or invariant".  Something 
that is short and not heavily used in code.  Mabye even some form of 
punctuation.

Walter, I think you need to look at this, because not doing this is going to 
keep D out of the mainstream, as most developers will (and already do) 
complain about the lack of logical const.  I was one of those, but I gave 
up, because I convinced myself that logical const wasn't that important, and 
was a design flaw.  But since fully transitive const does not give us the 
benefits that you have been saying it will, why not allow it?

At the very least, this proof shows that you need a better reason to require 
fully transitive const/invariant than "I need it for the future."

-Steve 





More information about the Digitalmars-d mailing list