Const sucks

Bruce Adams tortoise_74 at ya.hoo.nospamme.uk.ceo
Tue Sep 11 01:09:37 PDT 2007


Walter Bright Wrote:

> Const, final, invariant, head const, tail const, it's grown into a 
> monster. It tries to cover all the bases, but in doing so is simply not 
> understandable.
> 
> Andrei and Bartosz have spent some time together going back to square 
> one with what are we trying to achieve with const, and came up with 
> another proposal.
> 
> What we are trying to achieve:
> 
> a) utility for functional programming
> b) better modularity by self-documenting interfaces better
> c) be able to treat reference types as if they were value types (i.e. 
> strings should behave to the user like value types, even though they are 
> references)
> 
> The insights they came up with were:
> 
> 1) final (i.e. 'head const') is not necessary for a, b or c. final is a 
> local thing, and not strictly necessary.
> 
> 2) tail const can be handled in other ways, read on
> 
> So, what we are left with is:
> 
> o  no more final
> 
> o  const and invariant now mean "fully const" and "fully invariant", 
> where fully means "head and tail combined":
> 
> const int x = 0;   // x is constant
> const int* p = &x;  // neither p nor *p can be changed
> const(int*) p = &x;  // neither p nor *p can be changed
> const(int)* p = &x;  // p can change, *p cannot be changed
> 
> o  const and invariant are transitive. There is no way to specify a 
> (pointer to)(const pointer to)(mutable int).
> 
> o  tail invariant for an array or pointer type can be done using the 
> existing syntax:
> 
>   invariant(char)[] s;   // string, i.e. an array of invariant chars
>   const(T)* p;  // pointer to tail const T
> 
> o  tail const of a struct would have to be done by making the struct a 
> template:
> 
>    struct S(T) { T member; }
>    S!(int)   // tail mutable
>    S!(const(int)) // tail const
> 
> o  one can construct a template to generically produce tail const or 
> tail invariant versions of a type.
> 
> o  it will be illegal to attempt to change the key value of a foreach 
> loop, but the compiler will not be able to diagnose all attempts to do so
> 
> o  static const/invariant means the initializer is evaluated at compile 
> time. non-static const/invariant means it is evaluated at run time.
> 
> o  no initializer means it is assigned in the corresponding constructor.
> 
> o  const/invariant declarations will always allocate memory (so their 
> addresses can be taken)
> 
> o  So, we still need a method to declare a constant that will not 
> consume memory. We'll co-opt the future macro syntax for that:
> 
>      macro x = 3;
>      macro s = "hello";

Time for a rethink. I think I may have (part of) a solution.

Fundermentally its a property of an individual pointer or reference type.

const means - I will not change what I point to.
invariant means - no-one will change what I point to.

Fundermentally this can be applied independently to every pointer in
a chain of  pointers to pointers to pointers...

The challenge for the language designer is to make this easily expressible and understandable to the user.

We are still thinking along C++ lines. Maybe constraints are (part of) the answer.

One powerful concept introduced was that const and invariant could
be transitive. This helps in some specific cases and in might be an idea to make transitivity declarable. i.e. transitive const  versus vanilla const.
Similarly you can use brackets of some kind to specify the scope (i.e. which range of pointers in a change) that constness or invariantivity applies to.

However, I think doing it the c++ way is not the only answer. constness only comes into play in interfaces. Interfaces have contracts. Lets use these to specify the constraint clearly and concisely.
i.e.
instead of:

const int* foo(const double* bar, invariant int* quango);

how about:

pre {
  assert(bar.const == true);
  assert(quango.invariant == true);
}
post {
  // how do you say return value?
  assert(returnValue.const == true);
}
int* foo(double* bar, int* quango)
{
}

For a trivial function like that it doesn't help readability that much.
But for something more complicated it will.

instead of:

invariant int x;
invariant int* y;
const* invariant int* z; //syntax fuzzy here

invariant int x;
int* y;
int** z;

assert(z.const == true);
assert((*z).invariant == true);

Just more syntactic sugar.
Likewise

int x;
const int* y;
const* const int* z;

could become:

int x;
const int* y;
transitive const int** z;   //insert favourite keyword for 'transistive' here

Just some incomplete thoughts to throw into the mix.

Regards,

Bruce.



More information about the Digitalmars-d mailing list