March 20, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> I think the shock will be mollified by the explanation that const is for the stuff "before the star" and final is for the stuff "after the star".
> 
> const char *const s = "hello"; // C++
> 
> Many C++ programmers complain about this syntax, finding it silly.

I also have to always stop and think about what it means. Const in C++ actually has 3 distinct meanings, and which is in force depends on the context.

In my discussions about this with C++ programmers, even C++ compiler implementors, very, very few understand just what is going on with C++ const. I think a lot of the confusion here about const is coming from the misunderstandings engendered by the C++ mechanism.

What the D proposal does is identify those 3 distinct meanings, and make them distinct and individually controllable. This should also, in the long run, make it more understandable once one is able to get past the wacky confusion that is C++ const.

It also gives those who care about const-correctness the mechanism to control precisely what they need, and it can even help the optimizer generate better code. (C++ const is worthless for generating better code.)
March 20, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Don Clugston wrote:
>> Frits van Bommel wrote:
>>> Don Clugston wrote:
>>>> But then this really surprises me:
>>>>
>>>>> final int a = 2;
>>>>>
>>>>> void f(final int b)
>>>>> {
>>>>> }
>>>>> The choice of "final" in the second case prevents f from changing its argument,
>>>>  > and it's the free will choice of f's author. The "final" does not
>>>>  > influence f's signature or how other people use it.
>>>>  > It's just constraining f's implementation.
>>>>
>>>> because it seems that that we still have 'const' surprises for C++ refugees.
>>>
>>> I don't see how this would be a surprise for anyone coming from C++. This is exactly how 'const' works for value types, isn't it?
>>
>> Yes. The point is that for some uses of C++ const, you have to use 'const', while for others you need to use 'final'. I feel that this weakens the argument for using the word 'const' in the C++ sense. The hypothetical C++ refugee is still going to have culture shock when using D const.
> 
> I think the shock will be mollified by the explanation that const is for the stuff "before the star" and final is for the stuff "after the star".
> 
> const char *const s = "hello"; // C++
> 
> Many C++ programmers complain about this syntax, finding it silly.
> 
> 
> Andrei

Huh? Wait a second, but won't D have the same issue, albeit with 'final'? For example, suppose you have:
  final Foo foo;
then what is:
  typeof(&foo)
?





-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
March 20, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Derek Parnell wrote:
>> On Mon, 19 Mar 2007 22:34:13 -0700, Andrei Alexandrescu (See Website For
>> Email) wrote:
>>
>>> Tyler Knott wrote:
>>>> *const and invariant only make sense when applied to references, otherwise they are ignored or (preferably) errors.
>>> Correct.
>>
>> Can 'const' and 'invariant' apply to structs, especially ones that contain
>> references.
>>
>>   struct Foo
>>   {
>>       char[] s;
>>       char c;
>>   }
>>
>>   const Foo f;  // Is this a mistake then?
> 
> That code is fine. I should have said "const and invariant make sense only when applied to types that are, or contain, references". Sorry.
> 
>>   f.s[0] = 'a'; // okay???
> 
> No.
> 
>>   f.s = "xyz";  // okay???
> 
> Yes.
> 
>>   f.s = "def".dup; // okay ???
> 
> Yes.
> 
>>   f.s.length = 1;  // okay?
> 
> Yes.
> 
>>   f.s ~= 'a'; // okay??
> 
> Yes.
> 
>>   f.c = 'a';   // okay???
> 
> Yes.
> 
>>   const Foo somefunc();
>>   f = somefunc; // okay??
> 
> Yes.
> 
>>   Foo someotherfunc();
>>   f = someotherfunc; // okay??
> 
> Yes. Non-const transforms into const no problem.
> 
> 
> Andrei

What?? If f is 'const', how are any of those "f.s =" assignments allowed?


-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
March 20, 2007
Bruno Medeiros wrote:
> 
> Huh? Wait a second, but won't D have the same issue, albeit with 'final'? For example, suppose you have:
>   final Foo foo;
> then what is:
>   typeof(&foo)
> ?
> 

invariant Foo* or const Foo*, depending on whichever is most appropriate for the situation.  I'd guess that invariant Foo* would be the default because it's semantically closest to what is being expressed (a pointer to immutable data), though const Foo* is also allowable because you can't modify data through const pointers either.
March 20, 2007
Bruno Medeiros wrote:
> 
> What?? If f is 'const', how are any of those "f.s =" assignments allowed?
> 

Because f is a POD (Plain Old Data) structure and not a reference you can modify the data in the structure, but because it's declared const you can't modify any data referenced.  If you want f.s to be unassignable then declare f to be final Foo, though you can then modify the data referenced by f.s so f.s[0] = 'a'; becomes legal.

For example:

struct Foo
{
    char[] s = "hello";
    char c;
}

const Foo const_foo;
final Foo final_foo;
final const Foo const_final_foo;
const Foo* const_foo_pointer = new Foo;

const_foo.s = "test"; //Legal: data directly modified (const only invalidates modification through references)
final_foo.s = "test"; //Illegal: cannot modify data in final variables
const_final_foo.s = "test"; //Illegal: cannot modify data in final variables
const_foo_pointer.s = "test"; //Illegal: cannot modify data through const references

const_foo.s[0] = 'a'; //Illegal: cannot modify data through const references
		      //(const declaration is transitive to const_foo.s reference)
final_foo.s[0] = 'a'; //Legal: you're not modifying any data in final_foo, only data referenced by it
const_final_foo.s[0] = 'a'; //Illegal: cannot modify data through const references
const_foo_pointer.s[0] = 'a'; //Illegal: cannot modify data through const references
March 20, 2007
Tyler Knott wrote:
> Bruno Medeiros wrote:
>>
>> Huh? Wait a second, but won't D have the same issue, albeit with 'final'? For example, suppose you have:
>>   final Foo foo;
>> then what is:
>>   typeof(&foo)
>> ?
>>
> 
> invariant Foo* or const Foo*, depending on whichever is most appropriate for the situation.  I'd guess that invariant Foo* would be the default because it's semantically closest to what is being expressed (a pointer to immutable data), though const Foo* is also allowable because you can't modify data through const pointers either.

As I understand, there will be an implicit cast of mutables to immutables, so assuming 'const' appears under 'invariant' in this list, I would expect|hope it to be 'const Foo*' so that it is passable to either 'const' or 'invariant' typed parameters.  Ie:

final Foo foo;

void alpha (invariant Foo x) {...}
void beta (const Foo x) {...}

auto ptr = &foo;
alpha(*foo); // no problem regardless
beta(*foo); // an issue if ptr is invariant

Again, this is /if/ the casting rules will be as I think they will.  I'm just now starting to wrap my own head around it.

Presumably if I really /want/ it to be invariant, I could maybe do this?
invariant ptr = &foo;

Which would perform the mentioned implicit cast before assignment, discarding the potential 'const' view.

-- Chris Nicholson-Sauls
March 20, 2007
Bruno has answered your specific questions, so I'll take a more general tack.

A symbol is a name to which is 'bound' a value.

static int x = 3;

'3' is the value.
'int' is the type.
'x' is the symbol.
'static' is the storage class.

Here, we bind a new value to the symbol x:
    x = 4;

A storage class originally meant where the symbol is stored, such as in the data segment, on the stack, in a register, or in ROM. It's been generalized a bit since then. The main way to tell a storage class apart is that:
1) a storage class applies to the symbol
2) a type is independent of storage class, i.e. you cannot create a type that is "pointer to static" or "array of extern". Storage classes do not affect overloading, nor type deduction.

'invariant' and 'const' are type modifiers (aka type constructors), which mean when they are applied to a type, a new type is created that is a combination.

'invariant' is a guarantee that any data of that type will never change. 'const' is a guarantee that any data of that type will never be modified through a reference to that type (though other, non-const references to that type can modify the data).
March 20, 2007
Andrei Alexandrescu (See Website For Email) wrote:
<snip>
> We can make "const" mean "final const" and drop "final", at the cost of disallowing correct code (e.g. functions that modify their own private parameters). Or we could come up with new syntax that puts "const" in a different position for array, e.g.:
> 
> void Fun(const char[] const s);
> 
> After considering such alternatives, final becomes much more attractive.


I think someone, sometime mentioned changing the meaning of the 'in' keyword.  I guess that would have the same meaning with functions parameters that is planned for const now?

void Fun(in char[] s);  // Fun can't modify the array contents

void Fun(const char[] s);


// 'in' looks nice here
char* strcpy(char* to, in char* from);
char* strcpy(char* to, const char* from);

// let's say this returns the number of irreversibly converted chars, like iconv does.
size_t utf8ToLatin1(in char[] from, out ubyte[] to);
size_t utf8ToLatin1(const char[] from, out ubyte[] to);


One obvious problem is that in and out would not be really opposites. 'out' and 'inout' would take the address of the argument, but 'in' does nothing of the sort.  Maybe that could be confusing.
March 20, 2007
Chris Nicholson-Sauls wrote:
> As I understand, there will be an implicit cast of mutables to immutables, so assuming 'const' appears under 'invariant' in this list, I would expect|hope it to be 'const Foo*' so that it is passable to either 'const' or 'invariant' typed parameters.  Ie:
> 

Actually, I take back what I said in my previous post in this thread.  Taking the address of a final variable should always result in an invariant pointer.  The reason is that invariant references require the data they reference to never, ever change whereas const references expect the data they reference to be changed by other non-const/non-immutable references to that data.  Obviously, since const references can't guarantee the data they reference won't change they can't be cast to immutable references at all, but because const references can still guarantee they won't mutate the data they reference immutable references can be freely cast to const references.
March 20, 2007
On Tue, 20 Mar 2007 16:01:35 -0700, Walter Bright wrote:


> A symbol is a name to which is 'bound' a value.
 ...

> Here, we bind a new value to the symbol x:
>      x = 4;

I used to use the verb 'to assign' for this concept. I guess that's still
okay or must I modernize <G>

> static int x = 3;
> 
> '3' is the value.
> 'int' is the type.
> 'x' is the symbol.
> 'static' is the storage class.
> 
> 
> A storage class originally meant where the symbol is stored, such as in
> the data segment, on the stack, in a register, or in ROM. It's been
> generalized a bit since then. The main way to tell a storage class apart
> is that:
> 1) a storage class applies to the symbol

"to the symbol"?  Don't you mean "to the data that the symbol represents"? In the case above, the symbol is 'x', and I don't think 'x' is being stored anywhere except in the compiler's internal tables, and I'm sure 'static' isn't referring to the compiler's internals.


> 'invariant' is a guarantee that any data of that type will never change.

 class Foo
 {
   int a;
   char[] s;
   this(char[] d)
   {
       s = d.dup;
       a = s.length;
   }
 }
 invariant Foo f = new Foo("nice");

 f.a = 1; // fails??? changing f's data
 f.s = "bad"; // fails??? changing f's data
 f.s.length = 1; // fails??? changing f's data
 f.s[0] = 'r'; // okay ??? not changing f's data

 f = new Foo("rabbit"); // okay 'cos 'f' is a reference
                        // and not the object???

> 'const' is a guarantee that any data of that type will never be modified through a reference to that type (though other, non-const references to that type can modify the data).

 const Foo f = new Foo("nice");
       Foo g = f;

 f.a = 1; // fails??? changing f's data
 g.a = 1; // okay??? Using 'g' and not 'f'.
 f.s = "bad"; // fails??? changing f's data
 g.s = "bad"; // okay??? Using 'g' and not 'f'.
 f.s.length = 1; // fails??? changing f's data
 g.s.length = 1; // okay??? Using 'g' and not 'f'.
 f.s[0] = 'r'; // okay ??? not changing f's data

 f = new Foo("rabbit"); // okay 'cos 'f' is a reference
                        // and not the object???

 (*(&f)).a = 1; // okay??? access through f's address and not 'f'.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Justice for David Hicks!"
21/03/2007 10:39:07 AM