June 22, 2007
Walter Bright wrote:
> http://www.digitalmars.com/d/const.html

So in short, 'const' protects data and 'final' freezes references.  How do these two apply to an int declaration?

    const final int x = 5;

Is either a compiler error? are they synonyms in this case?

This aspect of the design seems very straightforward, aside from the question above.  What bothers me, however, is the use of 'invariant'. Adding a third keyword simply to represent data that's "really really const" just confuses things to me, and I haven't been able to get past this.  Given that adding a third keyword doubles the number of permutations for describing const behavior, I think the addition of 'invariant' should be very carefully considered.  Is there any way we could get along without it?  I realize that 'invariant' would be rarely used in practice, but that doesn't change the impact an additional attribute has on the complexity of this design.

Frankly, I think we could almost get away with one keyword, but for the fact that D doesn't use a reference qualifier for class references. About the only workaround I could think of to describe a const reference to mutable data would be something like this:

    const (ref MyClass) x;

And inserting the 'ref' seems even more confusing than simply having 'final' as in the current design.


Sean
June 22, 2007
Sean Kelly wrote:
> Walter Bright wrote:
>> http://www.digitalmars.com/d/const.html
> 
> So in short, 'const' protects data and 'final' freezes references.  How do these two apply to an int declaration?
> 
>     const final int x = 5;
> 
> Is either a compiler error? are they synonyms in this case?

It's not an error, it's just redundant.


> This aspect of the design seems very straightforward, aside from the question above.  What bothers me, however, is the use of 'invariant'. Adding a third keyword simply to represent data that's "really really const" just confuses things to me, and I haven't been able to get past this.

invariant = the data doesn't change
const = the data cannot be changed through this reference to it

> Given that adding a third keyword doubles the number of permutations for describing const behavior, I think the addition of 'invariant' should be very carefully considered.  Is there any way we could get along without it?  I realize that 'invariant' would be rarely used in practice, but that doesn't change the impact an additional attribute has on the complexity of this design.

In C++, sometimes const means invariant, and sometimes it means readonly view. I've found even C++ experts who don't know how it works.


> Frankly, I think we could almost get away with one keyword, but for the fact that D doesn't use a reference qualifier for class references. About the only workaround I could think of to describe a const reference to mutable data would be something like this:
> 
>     const (ref MyClass) x;
> 
> And inserting the 'ref' seems even more confusing than simply having 'final' as in the current design.

C++ tries too hard to use one keyword to do it all. The result is, as the article showed, serious difficulties.
June 22, 2007
Walter Bright wrote:
> http://www.digitalmars.com/d/const.html

It's a good article. However, it could really use some examples. The article on scope was very convincing because it gives plausible use-cases. In particular, I think there needs to be an example for 'invariant'. When is 'const' not good enough?
Where you state that invariant solves the aliasing problem, you could prove it by rewriting the example from the C++ section.

QUOTE ---
But there is a need for a constant declaration referencing a mutable type.This is provided with the final storage class for declarations...Its main purpose is
...[that it]...can be mentally separated from variable declarations that are meant to change
---
I don't find this very convincing. Is the mental benefit really significant enough to justify the extra complexity? The existence of three const-related keywords is pretty tough on mental space!

QUOTE ----
int x = 3;
const int *p = &x;
*p = 4;		// error, read-only view
x = 5;		// ok
int y = *p;	// y is set to 5

This is one instance of the so-called aliasing problem, since while the above snippet is trivial, the existence of such aliases can be very hard to detect in a complex program. It is impossible for the compiler to reliably detect it. This means that the compiler cannot cache 4 in a register and reuse the cached value to replace *p, it must go back and actually dereference p again.
---
Shouldn't that be: "the compiler cannot cache 3 in a register" ?
June 22, 2007
After reading the articles by Walter referred to recently, I'm less confused that I was, but would still appreciate some further explanation.

So should "const foo bar = baz" be an error if foo is a value type, on the basis that one should be using "invariant" or maybe final? It it safe to have two things const/invariant that become identical depending on type?

And does "final" exist *solely* so that you can approximate "invariant" for objects as well as for structs and scalars? I can see its use when you want something to be invariant but with the stipulation that it must exist in memory and have an address. In which case, why have final vs invariant at all, rather than having final be, for example, "weak invariant" or "stored invariant" or something like that? From here, final just looks *semantically* like "a poor man's invariant".


June 22, 2007
Don Clugston wrote:
> QUOTE ----
> int x = 3;
> const int *p = &x;
> *p = 4;        // error, read-only view
> x = 5;        // ok
> int y = *p;    // y is set to 5
> 
> This is one instance of the so-called aliasing problem, since while the above snippet is trivial, the existence of such aliases can be very hard to detect in a complex program. It is impossible for the compiler to reliably detect it. This means that the compiler cannot cache 4 in a register and reuse the cached value to replace *p, it must go back and actually dereference p again.
> ---
> Shouldn't that be: "the compiler cannot cache 3 in a register" ?

No. At "*p = 4;" the compiler can't cache 4, because then at "int y = *p;" y would become 4 instead of 5.

Although in the example it's moot because "*p = 4;" is an error.

-- 
Remove ".doesnotlike.spam" from the mail address.
June 22, 2007
Deewiant wrote:
> Don Clugston wrote:
>> QUOTE ----
>> int x = 3;
>> const int *p = &x;
>> *p = 4;        // error, read-only view
>> x = 5;        // ok
>> int y = *p;    // y is set to 5
>>
>> This is one instance of the so-called aliasing problem, since while the
>> above snippet is trivial, the existence of such aliases can be very hard
>> to detect in a complex program. It is impossible for the compiler to
>> reliably detect it. This means that the compiler cannot cache 4 in a
>> register and reuse the cached value to replace *p, it must go back and
>> actually dereference p again.
>> ---
>> Shouldn't that be: "the compiler cannot cache 3 in a register" ?
> 
> No. At "*p = 4;" the compiler can't cache 4, because then at "int y = *p;" y
> would become 4 instead of 5.
> 
> Although in the example it's moot because "*p = 4;" is an error.
In which case the example doesn't demonstrate anything. <g>

Better, I think, would be:
int x = 3;
const int *p = &x;
*p = 4;        // error, read-only view
int y = *p;
x=5;
int z = *p;

and then compiler can't set z=y.
Then we can see what invariant is for:

int x = 3;
invariant int *p = &x; // error, x is not invariant


invariant int x = 3;
invariant int *p = &x;
*p = 4;        // error, p is invariant
int y = *p;
x=5;           // error, x is invariant
int z = *p;
// compiler knows that y==z.
June 22, 2007
Walter Bright, el 22 de junio a las 01:07 me escribiste:
> Sean Kelly wrote:
> >Walter Bright wrote:
> >>http://www.digitalmars.com/d/const.html
> >So in short, 'const' protects data and 'final' freezes references.  How do these two apply to an int declaration?
> >    const final int x = 5;
> >Is either a compiler error? are they synonyms in this case?
> 
> It's not an error, it's just redundant.

Shouldn't be better to be an error? So it's more clear that final makes sense only for reference types.

Even more, aren't:

const int x = 5;
final int x = 5;
invariant int x = 5;

all the same?

-- 
LUCA - Leandro Lucarella - Usando Debian GNU/Linux Sid - GNU Generation
------------------------------------------------------------------------
E-Mail / JID:     luca@lugmen.org.ar
GPG Fingerprint:  D9E1 4545 0F4B 7928 E82C  375D 4B02 0FE0 B08B 4FB2
GPG Key:          gpg --keyserver pks.lugmen.org.ar --recv-keys B08B4FB2
------------------------------------------------------------------------
No existe nada más intenso que un reloj, ni nada más flaco que una
bicicleta. No intenso como el café, ni flaco como escopeta.
	-- Ricardo Vaporeso
June 22, 2007
Leandro Lucarella wrote:
> Walter Bright, el 22 de junio a las 01:07 me escribiste:
>> Sean Kelly wrote:
>>> Walter Bright wrote:
>>>> http://www.digitalmars.com/d/const.html
>>> So in short, 'const' protects data and 'final' freezes references.  How do these two apply to an int declaration?
>>>    const final int x = 5;
>>> Is either a compiler error? are they synonyms in this case?
>> It's not an error, it's just redundant.
> 
> Shouldn't be better to be an error? So it's more clear that final makes
> sense only for reference types.

Allowing it allows cleaner generic code, otherwise templates would often have to check whether parameters were value or reference types.
June 22, 2007

Frits van Bommel wrote:
> Leandro Lucarella wrote:
>> Walter Bright, el 22 de junio a las 01:07 me escribiste:
>>> Sean Kelly wrote:
>>>> Walter Bright wrote:
>>>>> http://www.digitalmars.com/d/const.html
>>>> So in short, 'const' protects data and 'final' freezes references.
>>>> How do these two apply to an int declaration?
>>>>    const final int x = 5;
>>>> Is either a compiler error? are they synonyms in this case?
>>> It's not an error, it's just redundant.
>>
>> Shouldn't be better to be an error? So it's more clear that final makes sense only for reference types.
> 
> Allowing it allows cleaner generic code, otherwise templates would often have to check whether parameters were value or reference types.

Indeed; we already need to special-case functions that return void.  "It can't be *that* bad!"  In some cases, it means an entire template has to be duplicated *just* for the void case.  I had to do this for a coroutine implementation, and was not happy about it.

	-- Daniel
June 22, 2007
Walter Bright wrote:
> Sean Kelly wrote:
>> Walter Bright wrote:
>>> http://www.digitalmars.com/d/const.html
>>
>> So in short, 'const' protects data and 'final' freezes references.  How do these two apply to an int declaration?
>>
>>     const final int x = 5;
>>
>> Is either a compiler error? are they synonyms in this case?
> 
> It's not an error, it's just redundant.

Okay, thanks.

>> This aspect of the design seems very straightforward, aside from the question above.  What bothers me, however, is the use of 'invariant'. Adding a third keyword simply to represent data that's "really really const" just confuses things to me, and I haven't been able to get past this.
> 
> invariant = the data doesn't change
> const = the data cannot be changed through this reference to it

And a 'const' at the declaration point implicitly means 'invariant'.

>> Given that adding a third keyword doubles the number of permutations for describing const behavior, I think the addition of 'invariant' should be very carefully considered.  Is there any way we could get along without it?  I realize that 'invariant' would be rarely used in practice, but that doesn't change the impact an additional attribute has on the complexity of this design.
> 
> In C++, sometimes const means invariant, and sometimes it means readonly view. I've found even C++ experts who don't know how it works.

Odd.  The C++ system always seemed extremely simple to me.

>> Frankly, I think we could almost get away with one keyword, but for the fact that D doesn't use a reference qualifier for class references. About the only workaround I could think of to describe a const reference to mutable data would be something like this:
>>
>>     const (ref MyClass) x;
>>
>> And inserting the 'ref' seems even more confusing than simply having 'final' as in the current design.
> 
> C++ tries too hard to use one keyword to do it all. The result is, as the article showed, serious difficulties.

I personally find the use of three keywords to represent three overlapping facets of const behavior to be very confusing, and am concerned about trying to explain it to novice programmers.  With three keywords, there are six possible combinations:

final
const invariant
final const
final invariant
const invariant
final const invariant

That some of these may be redundant just serves to further confuse the issue in my opinion.  So I wondered whether one of the keywords could be done away with.  Previously, you said 'invariant' may only apply to data whose value can be determined at compile-time, thus I imagine it can only apply to concrete/data types (ie. not classes).  Assuming this is true, I wonder whether there is truly a point in having 'invariant' at all.  Assuming it were done away with, the system becomes much simpler to me:

final
const
final const

And that's it.  'final' means a reference cannot be rebound, 'const' means the data cannot be altered (through the reference), and 'final const' means that both the reference is frozen and the data cannot be changed.  And that's it.  That the existence of invariant required the addition of parenthesis on class invariants just serves to strengthen the argument in my mind.  So in short, I'm just not convinced that 'invariant' provides enough utility to warrant the cost in complexity and broken consistency (unittest doesn't require parens, so why does invariant?).


Sean