www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Final, Const, Invariant

reply "David B. Held" <dheld codelogicconsulting.com> writes:
Since the Extended Type Design thread has blown my stack (and taken all 
the heap in my mail reader), here is my explanation of these concepts 
for anyone who might still be confused.  Yes, the explanation is 
cartoony, but I hope it helps:

Final

This is the "Velcro(TM)" property.  Any variable declared with this 
property is like super-glue.  You stick a value to it, and it doesn't 
let go.  You can't pull the value off it no matter how hard you try.

final int x = 42;  // uh-oh...what if x doesn't like 42?

x = 5; // oops! x is still stuck to the 42!!

final int y = x; // y is 42

final Bob alice = new Bob();  // Yay! Bob and alice got married!

alice.changeBob(); // ok, final doesn't stop this...

Bob.mutate(alice); // ...or this

alice = pete;  // Nope! alice is "monogamous"...no new Bobs for her!

Final is a "storage class" because it doesn't say anything about the 
value we are pointing to...just us.

Invariant

This is the IronMan property.  This is a type property, so it's a 
property of objects, not handles/pointers/references.  No matter who 
points at you, your skin is an impenetrable alloy, and you remain 
steadfastly unchanged:

invariant string s = "Hello, world";  // Yup, this friendly message is 
also invariant

s = "Goodbye, world";  // Ok, Invariant doesn't say anything about this
name, only what this name refers to

s = mutableString();  // Nope!  The type of s requires that we only 
refer to IronMan objects...s is a big fan of IronMan

s.breakWill();  // Ain't gonna happen...this is IronMan, and he can't be 
broken!

final invariant string s = "Bonjour, amigo!";  // s has an identity 
crisis, but it's permanent :(

s = "Hola, amie!";  // Nope...final means we are married to "Bonjour..."

s.reverse();  // Nuh-uh...our Franish is carved in stone

Const

This is the Rose Colored Glasses property.  Also known as the 
Look-But-Don't-Touch property.  Const variables are 
Puritanical...everything they see is invariant, or so they think.

const Point* p = new Point(x, y); // Ok, even though this new Point is 
intrinsically mutable, our Rose Colored Glasses make us think it's 
really Invariant

p = differentPoint;  // Ok, because const is a type qualifier, which 
means that it affects what kinds of folks we associate with, but doesn't 
stop us from associating altogether

p->getX(); // Ok, because getX() is also const...the underlying Point 
remains unchanged

p->move(5, 6);  // Wrong! Even though the underlying point is not 
invariant, we pretend that it is, because the glasses make us think the 
invariance is half-full

final const Point* q = p; // Now we've gone and aliased p, and we can't 
undo it

q->move(w, z);  // Just as illegal as it was for p

q = new Point(1, 2); // Start over?  Fuhggettaboutit

invariant const Point* r = new Point(8, 10); // Redundant, but 
legal...the underlying object is immutable, so in this case, the world 
Really Is Rose-Colored; we can take the glasses off, but we like the style

final invariant const Point* end = new Point(double.infinity, 
double.infinity); // Also redundant, but this is the immutable 
unchangeable end of the universe.

Dave
Mar 23 2007
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
David B. Held wrote:
 Since the Extended Type Design thread has blown my stack (and taken all 
 the heap in my mail reader), here is my explanation of these concepts 
 for anyone who might still be confused.  Yes, the explanation is 
 cartoony, but I hope it helps:
 

Haha thanks, only know I understand it clearly, time to read some of that thread again... It is much clearer though than C++, for there it depends on where you put the const which kind of meaning it has. To sum it up, there are three kinds of 'immutability', where one (const) is a subset of another (invariant). With this, we can control the semantics we want precisely and combine them to get a grand total of five different meanings: final Object x = y; // x won't ever reference anything but y const Object* x = &y; // y can't be changed by x invariant Object x = new Object; // x can't be changed at all final const Object* x = &y; // x points to y forever and can't change y final invariant Object* x = &y; // same as above but y will never change This is right?
Mar 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Lutger wrote:
 David B. Held wrote:
 Since the Extended Type Design thread has blown my stack (and taken 
 all the heap in my mail reader), here is my explanation of these 
 concepts for anyone who might still be confused.  Yes, the explanation 
 is cartoony, but I hope it helps:

Haha thanks, only know I understand it clearly, time to read some of that thread again... It is much clearer though than C++, for there it depends on where you put the const which kind of meaning it has. To sum it up, there are three kinds of 'immutability', where one (const) is a subset of another (invariant). With this, we can control the semantics we want precisely and combine them to get a grand total of five different meanings: final Object x = y; // x won't ever reference anything but y const Object* x = &y; // y can't be changed by x invariant Object x = new Object; // x can't be changed at all final const Object* x = &y; // x points to y forever and can't change y final invariant Object* x = &y; // same as above but y will never change This is right?

Yah. Andrei
Mar 23 2007
parent reply Dan <murpsoft hotmail.com> writes:
Andrei Alexandrescu (See Website For Email) Wrote:

 Lutger wrote:
 David B. Held wrote:
 Since the Extended Type Design thread has blown my stack (and taken 
 all the heap in my mail reader), here is my explanation of these 
 concepts for anyone who might still be confused.  Yes, the explanation 
 is cartoony, but I hope it helps:

Haha thanks, only know I understand it clearly, time to read some of that thread again... It is much clearer though than C++, for there it depends on where you put the const which kind of meaning it has. To sum it up, there are three kinds of 'immutability', where one (const) is a subset of another (invariant). With this, we can control the semantics we want precisely and combine them to get a grand total of five different meanings: final Object x = y; // x won't ever reference anything but y const Object* x = &y; // y can't be changed by x invariant Object x = new Object; // x can't be changed at all final const Object* x = &y; // x points to y forever and can't change y final invariant Object* x = &y; // same as above but y will never change This is right?

Yah. Andrei

I think the concept is right. Details, thoughts on which word ought to mean which? I agree we should use three keywords to accomplish the storage class thing. Note though that const and invariant are already keywords, and this might break some things... I'm still curious though Andrei, what about Object*[32][char[]], where we want to ensure the char[]'s are invariant? Is that Object*[32][invariant char[]] ? Also, if this gets implemented, enough needs to be explained so we can use it correctly for all the odd uses. it will have to go in the guide, mm? Can I use static with that? Do I need to? If something's invariant, is it implicitly static? So can I use myFunc( invariant char[], int x) ? What does that mean, that it won't change at all during the function operation, that it only accepts invariant char[]'s, or what? Can I use it in a tuple, template, mixin, and alias function/delegate? That needs to be decided now I think.
Mar 23 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Dan wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:
 
 Lutger wrote:
 David B. Held wrote:
 Since the Extended Type Design thread has blown my stack (and taken 
 all the heap in my mail reader), here is my explanation of these 
 concepts for anyone who might still be confused.  Yes, the explanation 
 is cartoony, but I hope it helps:

that thread again... It is much clearer though than C++, for there it depends on where you put the const which kind of meaning it has. To sum it up, there are three kinds of 'immutability', where one (const) is a subset of another (invariant). With this, we can control the semantics we want precisely and combine them to get a grand total of five different meanings: final Object x = y; // x won't ever reference anything but y const Object* x = &y; // y can't be changed by x invariant Object x = new Object; // x can't be changed at all final const Object* x = &y; // x points to y forever and can't change y final invariant Object* x = &y; // same as above but y will never change This is right?

Andrei

I think the concept is right. Details, thoughts on which word ought to mean which? I agree we should use three keywords to accomplish the storage class thing. Note though that const and invariant are already keywords, and this might break some things...

My code has const everywhere. In fact, I use 'const' about ten times as often as 'static' or 'class'. And the new scheme does not seem to have a direct equivalent for it. How can I say, "I want to refer this literal by a name, but in all other respects I want it to behave exactly as a literal"? In particular, I do not want storage associated with it (attempting to take its address is a bug), and it should work with CTFE. There's an ugly hack where you can use an anonymous enum, but it only works for integral types; does not work for floating point or string literals. Note that named floating point constants are extremely common in scientific code. In fact, it's almost always bad style to use an unnamed floating point literal! If 'const' is used as described in the new scheme, seriously consider if we need a 'literal' or 'define' keyword. Since I suspect that I'm losing the battle for 'const'=='literal', I have a suggestion. The best word I've come up with for what the proposed 'const' means is 'constrained'. Which is interesting because of the way you'd abbreviate it... eg. const Object* x = &y; // y can't be changed by x --> x is constrained in what it can do to y. 'const' could be described as an abbreviation of 'constrained' rather than 'constant'.
Mar 26 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Don Clugston wrote:
 My code has const everywhere. In fact, I use 'const' about ten times as 
 often as 'static' or 'class'. And the new scheme does not seem to have a 
 direct equivalent for it. How can I say, "I want to refer this literal 
 by a name, but in all other respects I want it to behave exactly as a 
 literal"? In particular, I do not want storage associated with it 
 (attempting to take its address is a bug), and it should work with CTFE.

Replacing const with final should do the trick.
Mar 26 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Don Clugston wrote:
 My code has const everywhere. In fact, I use 'const' about ten times 
 as often as 'static' or 'class'. And the new scheme does not seem to 
 have a direct equivalent for it. How can I say, "I want to refer this 
 literal by a name, but in all other respects I want it to behave 
 exactly as a literal"? In particular, I do not want storage associated 
 with it (attempting to take its address is a bug), and it should work 
 with CTFE.

Replacing const with final should do the trick.

Will that prevent taking the address, the same way 'const' does now? There's also this previously discussed case: void func(int x) { final a = ctfe_func(7); // will never change, ever. final b = non_ctfe_func(8); // won't change during this function call } A largely unrelated question, why are existing 'const' items stored in the obj file (instead of being discarded immediately after use)? (I'm excluding uninitialised class 'const' items set in the constructor, which are fundamentally different).
Mar 26 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Don Clugston wrote:
 Walter Bright wrote:
 Don Clugston wrote:
 My code has const everywhere. In fact, I use 'const' about ten times 
 as often as 'static' or 'class'. And the new scheme does not seem to 
 have a direct equivalent for it. How can I say, "I want to refer this 
 literal by a name, but in all other respects I want it to behave 
 exactly as a literal"? In particular, I do not want storage 
 associated with it (attempting to take its address is a bug), and it 
 should work with CTFE.

Replacing const with final should do the trick.

Will that prevent taking the address, the same way 'const' does now?

It's ironic, the "can't take the address of a const" is actually a bug. I guess it has inadvertently turned into a feature! I could argue it either way.
 There's also this previously discussed case:
 
 void func(int x)
 {
   final a = ctfe_func(7);  // will never change, ever.

Make it static final. There's no reason to put such a declaration on the stack.
   final b = non_ctfe_func(8); // won't change during this function call

Leave as is.
 }
 
 A largely unrelated question, why are existing 'const' items stored in 
 the obj file (instead of being discarded immediately after use)?

Because it's a bug that one cannot take the address of them. But it could become a feature, in which case they shouldn't be put in the obj file.
 (I'm 
 excluding uninitialised class 'const' items set in the constructor, 
 which are fundamentally different).

Mar 26 2007
parent Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 Don Clugston wrote:
 My code has const everywhere. In fact, I use 'const' about ten times 
 as often as 'static' or 'class'. And the new scheme does not seem to 
 have a direct equivalent for it. How can I say, "I want to refer 
 this literal by a name, but in all other respects I want it to 
 behave exactly as a literal"? In particular, I do not want storage 
 associated with it (attempting to take its address is a bug), and it 
 should work with CTFE.

Replacing const with final should do the trick.

Will that prevent taking the address, the same way 'const' does now?

It's ironic, the "can't take the address of a const" is actually a bug. I guess it has inadvertently turned into a feature! I could argue it either way.

So I've misunderstood the intention of 'const' all this time! Actually, I don't know how it could work, with the way D does constant folding. Currently, a literal like 3.1f is *not* the nearest value to 3.1 which will fit into a float, but rather it's the value 3.1, represented as accurately as possible, with a type of 'float'. const float F1 = float.max * 100.0; const float F2 = F1/200.0; // sets F2 = float.max/2. But if the literal is stored in the executable, it has to be changed into 'the nearest value which will fit into a float'. So precision drops, and overflow values become infinity. double D1 = F1; // D1 = float.max*100.0 double D2 = F1/200.0; // D2 = float.max/2. double X1 = *(&F1); // X1 = infinity double X2 = *(&F1)/200.0; // X2 = infinity Which would mean that the same symbol can have a run time value which is different to its compile time value. IMHO, the fact that D currently doesn't allow you to access the run time value is a great feature, that is consistent with the constant folding, and protects you from subtle bugs.
 A largely unrelated question, why are existing 'const' items stored in 
 the obj file (instead of being discarded immediately after use)?

Because it's a bug that one cannot take the address of them. But it could become a feature, in which case they shouldn't be put in the obj file.

I think it would improve executable size, and possibly compilation time as well.
Mar 26 2007