Fully transitive const is not necessary

Bill Baxter dnewsgroup at billbaxter.com
Tue Apr 1 11:54:42 PDT 2008


Good arguments, Steven.
The hand-waving over future benefits enabled by transitive const also 
has me feeling uneasy.  If the benefits are known then it should be 
possible to show some examples.  Clearly, though, as you say the benefit 
is not (at least directly) related to parallel optimizations.  So what 
optimizations does it allow?

So probably to use "pure" is going to require that a function A)
touches only const/invariant data and B) does not reference any globals.

So it seems logical, then, that there would need to be a const system in 
place to specify what things are ok for a pure function to use and do. 
Imagine a pure function that manipulates an array of external class 
objects.  The array will need to be of type const(Klass)[] to denote 
that the Klass objects won't be touched.  Or if the pure function wants 
to call a Klass method, it better be a pure method.

But as I think Sean mentioned before, the constness in that case could 
conceivably be checked by the compiler without requiring user 
annotations.  Consider this case though:

   // Hypothetical pure function with implicit/deduced constness
   pure int foo(Klass a) {
      scope b = new Klass;
      Klass[] x;
      x ~= a;
      x ~= b;
      random_shuffle(x);
      x[0].prop = 10;  // is this ok or not?
   }

The trouble is that it's difficult for the compiler to guess whether to 
treat x as const(Klass)[] or mutable Klass[].   It is possible in this 
case though.  Basically it can infer from the x[0].prop=10 line that the 
container must be mutable, and thus the line  x ~= a is invalid because 
the parameters to a pure function are assumed const.

So hmm, it does seem conceivable to me that the compiler could enforce 
"pure" without needing any explicit const notation.


I also agree with Sean that D is great, and that the simplicity of D is 
one of it's biggest selling points.  And also that const currently 
doesn't seem to be helping much in that department.


--bb

Steven Schveighoffer wrote:
> 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