www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Extended Type Design.

reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
What is the status of the experimental designs for the "storage classes" 
manipulation that Andrei and others where thinking of for D. The last I 
heard from it was Andrei's max suggestion from his max design challenge, 
however, I think that suggestion may suffer from some problems in 
regards to the "maxtype" requirement, plus it is wholly incomplete in 
regards to how storage classes interact between each other. Like Andrei 
said, what is a "const inout lazy const char[]", if valid at all? Is 
there any news here? Is there a working(aka complete) design?

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 14 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage classes" 
 manipulation that Andrei and others where thinking of for D. The last I 
 heard from it was Andrei's max suggestion from his max design challenge, 
 however, I think that suggestion may suffer from some problems in 
 regards to the "maxtype" requirement, plus it is wholly incomplete in 
 regards to how storage classes interact between each other. Like Andrei 
 said, what is a "const inout lazy const char[]", if valid at all? Is 
 there any news here? Is there a working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable. There is talk about deprecating lazy if it's best implemented via other mechanisms. There is also talk about deprecating "inout" in favor of "ref" on grounds that the often-useful "inout const" is likely to become #1 reason for bashing D. To read a declaration like "const inout lazy const char[]", you can first parenthesize it appropriately: const(inout(lazy(const(char[])))) The lazy thing is really a delegate that returns a const char[]. The inout around it passes that delegate by reference, and the const at the top makes the delegate immutable. Andrei
Mar 15 2007
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage
 classes" manipulation that Andrei and others where thinking of for D.
 The last I heard from it was Andrei's max suggestion from his max
 design challenge, however, I think that suggestion may suffer from
 some problems in regards to the "maxtype" requirement, plus it is
 wholly incomplete in regards to how storage classes interact between
 each other. Like Andrei said, what is a "const inout lazy const
 char[]", if valid at all? Is there any news here? Is there a
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable. There is talk about deprecating lazy if it's best implemented via other mechanisms. There is also talk about deprecating "inout" in favor of "ref" on grounds that the often-useful "inout const" is likely to become #1 reason for bashing D. To read a declaration like "const inout lazy const char[]", you can first parenthesize it appropriately: const(inout(lazy(const(char[])))) The lazy thing is really a delegate that returns a const char[]. The inout around it passes that delegate by reference, and the const at the top makes the delegate immutable. Andrei

This is fantastic news. I'm really happy to know you guys are making progress on this. So if I understand this right: * "final int*" is an immutable pointer that points to mutable data, * "const int*" is a mutable pointer to immutable data, but that data *may* be mutable in another context (ie: const((new int[10]).ptr)) and * "const! int*" means "seriously, this *will not* change". In any case, great to know things are moving along :) -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 15 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Daniel Keep wrote:
 This is fantastic news.  I'm really happy to know you guys are making
 progress on this.

Thank you! In fact, it did take an embarrassingly long time to implement something within all of the self-imposed constraints, but now we're really happy with the result.
 So if I understand this right:
 
 * "final int*" is an immutable pointer that points to mutable data,

Yes. For example: int a, b; final int * p = &a; *p = 5; // fine p = &b; // error! Cannot rebind final symbol!
 * "const int*" is a mutable pointer to immutable data, but that data
 *may* be mutable in another context (ie: const((new int[10]).ptr)) and

Exactly. For example: int a; const int * p = &a; *p = 5; // error! Cannot modify const data! a = 5; // yah, modify data under the feet of p This is a great feature for calling functions and providing "isolated" interfaces. For example, say you write a function void print(char[]); You want to clarify to the caller that you NEVER modify their data. So you define the function as: void print(const char[]); Then callers can call print with mutable or immutable data; it makes no difference to print. Unlike in C++, it takes much more effort to cast away constness, and the results are never well-defined.
 * "const! int*" means "seriously, this *will not* change".

Right. For example: int a; const! int* p = &a; // error! cannot convert int* to const! int* final int b = 42; const! int* p1 = &b; // yah, b is truly immutable const! int* p2 = new const! int; // yah Andrei
Mar 15 2007
parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Daniel Keep wrote:
 
 This is fantastic news.  I'm really happy to know you guys are making
 progress on this.

Thank you! In fact, it did take an embarrassingly long time to implement something within all of the self-imposed constraints, but now we're really happy with the result.
 So if I understand this right:

 * "final int*" is an immutable pointer that points to mutable data,

Yes. For example: int a, b; final int * p = &a; *p = 5; // fine p = &b; // error! Cannot rebind final symbol!
 * "const int*" is a mutable pointer to immutable data, but that data
 *may* be mutable in another context (ie: const((new int[10]).ptr)) and

Exactly. For example: int a; const int * p = &a; *p = 5; // error! Cannot modify const data! a = 5; // yah, modify data under the feet of p This is a great feature for calling functions and providing "isolated" interfaces. For example, say you write a function void print(char[]); You want to clarify to the caller that you NEVER modify their data. So you define the function as: void print(const char[]); Then callers can call print with mutable or immutable data; it makes no difference to print. Unlike in C++, it takes much more effort to cast away constness, and the results are never well-defined.
 * "const! int*" means "seriously, this *will not* change".

Right. For example: int a; const! int* p = &a; // error! cannot convert int* to const! int* final int b = 42; const! int* p1 = &b; // yah, b is truly immutable const! int* p2 = new const! int; // yah Andrei

... and return values: "const char[] immutableTextProp()" ? Just a thought on the const! syntax; it looks somewhat odd, and perhaps too much like template syntax? How about calling one of the "const" types "readonly" or "view" instead?
Mar 15 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Thu, 15 Mar 2007 23:45:05 -0700, kris wrote:

 Andrei Alexandrescu (See Website For Email) wrote:
 Daniel Keep wrote:
 
 This is fantastic news.  I'm really happy to know you guys are making
 progress on this.

Thank you! In fact, it did take an embarrassingly long time to implement something within all of the self-imposed constraints, but now we're really happy with the result.
 So if I understand this right:

 * "final int*" is an immutable pointer that points to mutable data,

Yes. For example: int a, b; final int * p = &a; *p = 5; // fine p = &b; // error! Cannot rebind final symbol!
 * "const int*" is a mutable pointer to immutable data, but that data
 *may* be mutable in another context (ie: const((new int[10]).ptr)) and

Exactly. For example: int a; const int * p = &a; *p = 5; // error! Cannot modify const data! a = 5; // yah, modify data under the feet of p This is a great feature for calling functions and providing "isolated" interfaces. For example, say you write a function void print(char[]); You want to clarify to the caller that you NEVER modify their data. So you define the function as: void print(const char[]); Then callers can call print with mutable or immutable data; it makes no difference to print. Unlike in C++, it takes much more effort to cast away constness, and the results are never well-defined.
 * "const! int*" means "seriously, this *will not* change".

Right. For example: int a; const! int* p = &a; // error! cannot convert int* to const! int* final int b = 42; const! int* p1 = &b; // yah, b is truly immutable const! int* p2 = new const! int; // yah


I'm not sure about that last one. Does this mean that p2 points to an int that cannot change and p2 itself can no longer be changed? And what is the value in the int that p2 points to? Can you do this sort of thing ... int x = 5; const! int* p2 = new const! int(x); meaning that p2 now points to a 'final' int whose value is 5, plus p2 can never be changed now either.
 ... and return values: "const char[] immutableTextProp()" ?

Hmmmm... does "const char[] X" mean that X.ptr can change, X.length can never change and the data pointed to by X.ptr can never change? Or is "can never" too strong? Is it more that code within the scope of the declaration is not allowed to change those things?
 Just a thought on the const! syntax; it looks somewhat odd, and perhaps 
 too much like template syntax?  How about calling one of the "const" 
 types "readonly" or "view" instead?

Yes! "const!" is bad form. Really bad. Horrible in fact. Just make it a new keyword please, without using non-alpha characters. Such as "readonly", "immutable", "keepyourgrubbyhandsoff", ... anything but using "!". -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 16/03/2007 6:01:40 PM
Mar 16 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 Yes! "const!" is bad form. Really bad. Horrible in fact. Just make it a new
 keyword please, without using non-alpha characters. Such as "readonly",
 "immutable", "keepyourgrubbyhandsoff", ... anything but using "!".

You can use "super const". We should be really really careful about adding new keywords; D already has tons, and I have a feeling we don't want it to become Cobol or dBase. If there are ways to stay away from new keywords, we'll do so. I do realize that no matter the syntax, there will be people who won't like it. But avoiding adding keywords liberally is an overriding desideratum. Andrei
Mar 16 2007
next sibling parent reply mike <vertex gmx.at> writes:
Am 16.03.2007, 10:19 Uhr, schrieb Andrei Alexandrescu (See Website For  =

Email) <SeeWebsiteForEmail erdani.org>:

 You can use "super const". We should be really really careful about  =

 adding new keywords; D already has tons, and I have a feeling we don't=

 want it to become Cobol or dBase.

 If there are ways to stay away from new keywords, we'll do so. I do  =

 realize that no matter the syntax, there will be people who won't like=

 it. But avoiding adding keywords liberally is an overriding desideratu=

 Andrei

Why not "const final" or "final const"? (a) .. final (b) .. const (c) .. const final / final const "super" is for calling constructors and that should be the only use for = = this keyword. -mike -- = Erstellt mit Operas revolution=E4rem E-Mail-Modul: http://www.opera.com/= mail/
Mar 16 2007
next sibling parent torhu <fake address.dude> writes:
mike wrote:
 Why not "const final" or "final const"?
 
 (a) .. final
 (b) .. const
 (c) .. const final / final const
 
 "super" is for calling constructors and that should be the only use for  
 this keyword.

Wouldn't a "final const" or "const final" pointer be like using const for both the pointer and the value in C? // D int x = 5; const final int* p = &x; // C int x = 5; const int * const p = &x;
Mar 16 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
mike wrote:
 Am 16.03.2007, 10:19 Uhr, schrieb Andrei Alexandrescu (See Website
 For Email) <SeeWebsiteForEmail erdani.org>:
 
 You can use "super const". We should be really really careful about
  adding new keywords; D already has tons, and I have a feeling we
 don't want it to become Cobol or dBase.
 
 If there are ways to stay away from new keywords, we'll do so. I do
  realize that no matter the syntax, there will be people who won't
 like it. But avoiding adding keywords liberally is an overriding
 desideratum.
 
 
 Andrei

Why not "const final" or "final const"? (a) .. final (b) .. const (c) .. const final / final const

Because final const and super const express very different realities.
 "super" is for calling constructors and that should be the only use
 for this keyword.

From your email I see you are Austrian, so I'm surprised. :o) German is the most synthetic (as opposed to analytical) language in use, and has the admirable property of building precise terms by combining smaller polysemous terms. Andrei
Mar 16 2007
parent mike <vertex gmx.at> writes:
Am 16.03.2007, 18:59 Uhr, schrieb Andrei Alexandrescu (See Website For  =

Email) <SeeWebsiteForEmail erdani.org>:

 Because final const and super const express very different realities.

*scratches head* Oh ... I didn't know that final const already HAD a meaning.
  From your email I see you are Austrian, so I'm surprised. :o) German =

 the most synthetic (as opposed to analytical) language in use, and has=

 the admirable property of building precise terms by combining smaller =

 polysemous terms.

Yeah, I'm Austrian, living in the land that has been named after the chi= ef = from battlestar galactica, so I don't really speak german :) We have ou= r = own language, basically, that's why the rest of the german-speaking worl= d = can't understand us. Like the people from Switzerland can't be understoo= d = by anyone but themselves. I like the idea of combining keywords to give them a new meaning, just = using "super" for two totally unrelated concepts worries me a bit. "scop= e" = for instance is great: It has two meanings, but they're completely = related. But using super once as a call and once as a sort of amplifier = = for const seems kind of random. Also I can't help myself but "super cons= t" = just sounds a bit ridiculous to me: ' super duper =FCber const int i =3D 3; // Holy constant, batman! Sorry :) Anyway, I have no better idea, so go for it! -mike -- = Erstellt mit Operas revolution=E4rem E-Mail-Modul: http://www.opera.com/= mail/
Mar 16 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Derek Parnell wrote:
 Yes! "const!" is bad form. Really bad. Horrible in fact. Just make it 
 a new
 keyword please, without using non-alpha characters. Such as "readonly",
 "immutable", "keepyourgrubbyhandsoff", ... anything but using "!".

You can use "super const". We should be really really careful about adding new keywords; D already has tons, and I have a feeling we don't want it to become Cobol or dBase. If there are ways to stay away from new keywords, we'll do so. I do realize that no matter the syntax, there will be people who won't like it. But avoiding adding keywords liberally is an overriding desideratum. Andrei

So instead of making D like Cobol we make it like APL ? :/ I don't think the problem of Cobol was *having* too many keywords, but rather forcing you to *use* lots of keywords for the simplest things. (Disclaimer: I don't actually know Cobol, I'm imagining it to be like Pascal, but worse) So we should have that in consideration when talking about D. In fact, I think that any general idea of "adding more keywords is bad" is somewhat pernicious: every case should be examined in itself. In some cases it could be worth it, in others not. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Derek Parnell wrote:
 Yes! "const!" is bad form. Really bad. Horrible in fact. Just make it 
 a new
 keyword please, without using non-alpha characters. Such as "readonly",
 "immutable", "keepyourgrubbyhandsoff", ... anything but using "!".

You can use "super const". We should be really really careful about adding new keywords; D already has tons, and I have a feeling we don't want it to become Cobol or dBase. If there are ways to stay away from new keywords, we'll do so. I do realize that no matter the syntax, there will be people who won't like it. But avoiding adding keywords liberally is an overriding desideratum. Andrei

So instead of making D like Cobol we make it like APL ? :/ I don't think the problem of Cobol was *having* too many keywords, but rather forcing you to *use* lots of keywords for the simplest things. (Disclaimer: I don't actually know Cobol, I'm imagining it to be like Pascal, but worse)

No. Ada is like Pascal, but worse :oO. Cobol has 420 keywords.
 So we should have that in consideration when talking about D. In fact, I 
 think that any general idea of "adding more keywords is bad" is somewhat 
 pernicious: every case should be examined in itself. In some cases it 
 could be worth it, in others not.

I agree. Andrei
Mar 16 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 int a;
 const! int* p = &a; // error! cannot convert int* to const! int*
 final int b = 42;
 const! int* p1 = &b; // yah, b is truly immutable
 const! int* p2 = new const! int; // yah


I'm not sure about that last one. Does this mean that p2 points to an int that cannot change and p2 itself can no longer be changed? And what is the value in the int that p2 points to?

Good point. There is a little exception here. People expect that when they write: const char[] str = "Hi!"; they can't modify str nor its characters. So they expect str to be final as well. We then arrange that if const appears in a data definition, that data is automatically made final as well. So the above is equivalent to: final const char[] str = "Hi!"; If you don't want str to be final, do this: const(char[]) str = "Hi!";
 Can you do this sort of thing ...
 
  int x = 5;
  const! int* p2 = new const! int(x);
 
 meaning that p2 now points to a 'final' int whose value is 5, plus p2 can
 never be changed now either.

Yes, you can.
 ... and return values: "const char[] immutableTextProp()" ?

Hmmmm... does "const char[] X" mean that X.ptr can change, X.length can never change and the data pointed to by X.ptr can never change? Or is "can never" too strong? Is it more that code within the scope of the declaration is not allowed to change those things?

See the exception above. But now consider a function: void say(const char[] data) { ... } The bits of data are a private copy of say, so they can be changed discretionarily (if that's a word). But the bits pointed to by data do not belong to say, so they can't be written. If say wants to be fancy, it can look like this: void say(final const char[] data) { ... } Then data can't be even rebound. The "final" being a storage class, it doesn't appear in the function signature (e.g., in the interface file). Andrei
Mar 16 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 02:25:24 -0700, Andrei Alexandrescu (See Website For
Email) wrote:

 Derek Parnell wrote:
 int a;
 const! int* p = &a; // error! cannot convert int* to const! int*
 final int b = 42;
 const! int* p1 = &b; // yah, b is truly immutable
 const! int* p2 = new const! int; // yah


I'm not sure about that last one. Does this mean that p2 points to an int that cannot change and p2 itself can no longer be changed? And what is the value in the int that p2 points to?

Good point.

<G> But you didn't answer my question? What is the value of the int to which p2 is pointing?
 There is a little exception here. 

Oh no ... alarms start to sound when I see 'exceptions'. Is it not possible to eliminate this exception?
 People expect that when 
 they write:
 
 const char[] str = "Hi!";
 
 they can't modify str nor its characters. So they expect str to be final 
 as well. We then arrange that if const appears in a data definition, 
 that data is automatically made final as well. So the above is 
 equivalent to:
 
 final const char[] str = "Hi!";

Are you saying "People expect that when they write ... and so that's what we are going to have D do for them."? Let's see if I got this ... const char[] str = "Hi!"; // Neither 'str' nor it contents can // be changed. final const char[] str2 = "Hi again"; // Means the same as above syntax. const char* chr = "Hi!"; // 'chr' can be changed but the chars // it points to cannot be changed. final const char* chr2 = "Hi!"; // 'chr2' can be not be changed // and neither can its chars. I hope I got this wrong because that is a easy bug maker.
 If you don't want str to be final, do this:
 
 const(char[]) str = "Hi!";

You have got to be kidding! This is starting to get unreadable again. As a simple person, if I didn't want something to be 'final', I wouldn't use the 'final' keyword. But you are saying that if I don't want 'final', I not only have to avoid using 'final', I also have to put some of the phrase (and I know I'll never remember which parts) in parenthesis. But wait ... there's more. If I do want it to be 'final' all I have to do is not use the 'final' keyword. Am I getting this right? Is it not possible to make reading code simple?
 Hmmmm... does "const char[] X" mean that X.ptr can change, X.length can
 never change and the data pointed to by X.ptr can never change? Or is "can
 never" too strong? Is it more that code within the scope of the declaration
 is not allowed to change those things?

See the exception above.

The EXCEPTION (alarms still ringing) seems to be saying that 'const char[] X' means that X.ptr, X.length and the data in X's RAM cannot be changed.
 But now consider a function:
 
 void say(const char[] data)
 {
    ...
 }
 
 The bits of data are a private copy of say, so they can be changed 
 discretionarily (if that's a word). But the bits pointed to by data do 
 not belong to say, so they can't be written.
 
 If say wants to be fancy, it can look like this:
 
 void say(final const char[] data)
 {
    ...
 }

That didn't help. I'm even more confused now. Can I try? ... void say (const char[] data) { data[0] = 'a'; // Allowed but not passed back to caller. data = "ABC"; // Not allowed. }
 Then data can't be even rebound. The "final" being a storage class, it 
 doesn't appear in the function signature (e.g., in the interface file).

Hmmmm ... won't that cause problems for library writers who provide a library and ".di" files? -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Fri, 16 Mar 2007 02:25:24 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 
 Derek Parnell wrote:
 int a;
 const! int* p = &a; // error! cannot convert int* to const! int*
 final int b = 42;
 const! int* p1 = &b; // yah, b is truly immutable
 const! int* p2 = new const! int; // yah


that cannot change and p2 itself can no longer be changed? And what is the value in the int that p2 points to?


<G> But you didn't answer my question? What is the value of the int to which p2 is pointing?

The default value of int, which is zero.
 There is a little exception here. 

Oh no ... alarms start to sound when I see 'exceptions'. Is it not possible to eliminate this exception?

I would love to. It is only for the sake of compatibility with existing code.
 People expect that when 
 they write:

 const char[] str = "Hi!";

 they can't modify str nor its characters. So they expect str to be final 
 as well. We then arrange that if const appears in a data definition, 
 that data is automatically made final as well. So the above is 
 equivalent to:

 final const char[] str = "Hi!";

Are you saying "People expect that when they write ... and so that's what we are going to have D do for them."?

I was just trying to minimize the amount of broken code. Also, we need to figure what to do about this: const int capacity = 80; In this context const makes no sense because int does not contain pointers. The above should be: final int capacity = 80;
 Let's see if I got this ...
 
   const char[] str = "Hi!";  // Neither 'str' nor it contents can
                              // be changed.
   final const char[] str2 = "Hi again"; // Means the same as above syntax.

Correct.
   const char*  chr = "Hi!";  // 'chr' can be changed but the chars
                              // it points to cannot be changed.

Correct. The pointer can be made to point anywhere, but it's looking, no touching.
   final const char*  chr2 = "Hi!";  // 'chr2' can be not be changed
                                // and neither can its chars.

Yes.
 I hope I got this wrong because that is a easy bug maker.

How would the bugs be introduced?
 If you don't want str to be final, do this:

 const(char[]) str = "Hi!";

You have got to be kidding! This is starting to get unreadable again. As a simple person, if I didn't want something to be 'final', I wouldn't use the 'final' keyword.

The problem is that many simple persons have different simple needs. The parens actually help readability a lot when compared to C++ because you don't need to put several const's for complex types.
 But you are saying that if I don't want 'final', I not
 only have to avoid using 'final', I also have to put some of the phrase
 (and I know I'll never remember which parts) in parenthesis. But wait ...
 there's more. If I do want it to be 'final' all I have to do is not use the
 'final' keyword.  Am I getting this right?
 
 Is it not possible to make reading code simple?

An intuition that I see reasonable is that const by default engulfs everything to its right: const char[] str = "Hi!"; // same as const(char[] str) = "Hi!"; When it engulfs the symbol, it makes it immutable, hence final. So by putting the parens you limit const's power. But I'd be glad to drop this exception. I'm not sure what Walter and people who'll have to modify their code would say though.
 Hmmmm... does "const char[] X" mean that X.ptr can change, X.length can
 never change and the data pointed to by X.ptr can never change? Or is "can
 never" too strong? Is it more that code within the scope of the declaration
 is not allowed to change those things?


The EXCEPTION (alarms still ringing) seems to be saying that 'const char[] X' means that X.ptr, X.length and the data in X's RAM cannot be changed.

I am hearing it. :o)
 But now consider a function:

 void say(const char[] data)
 {
    ...
 }

 The bits of data are a private copy of say, so they can be changed 
 discretionarily (if that's a word). But the bits pointed to by data do 
 not belong to say, so they can't be written.

 If say wants to be fancy, it can look like this:

 void say(final const char[] data)
 {
    ...
 }

That didn't help. I'm even more confused now. Can I try? ... void say (const char[] data) { data[0] = 'a'; // Allowed but not passed back to caller. data = "ABC"; // Not allowed. }

Now, you got it backwards: data's contents belong to the caller, and data's pointer belongs to the callee.
 Then data can't be even rebound. The "final" being a storage class, it 
 doesn't appear in the function signature (e.g., in the interface file).

Hmmmm ... won't that cause problems for library writers who provide a library and ".di" files?

No. Andrei
Mar 16 2007
next sibling parent reply Dan <murpsoft hotmail.com> writes:
Wow...

I've never put this much thought into const/final stuff.  Ultimately, the
program *cannot* prevent data in memory from being changed.  It can at best
prevent you from accidentally changing it; hence final and const declarations
mean "I don't want anyone to change these, can you raise an error if someone
tries to within D?"

Also, the problem breaks down to 'what am I declaring const?'.  If I declare:

const int* x; // is it the pointer, or the int?

This doesn't seem important until you get to:

const int[int**][]* x; // now what is it?

Ultimately, you have this problem going through each and every pointer, array
and value.  You could theoretically write this:

const int[int**][]*[char[]][3][17][3,17]* x;  and now what are you going to do?
 declare it:

final const final final const const final final final final final const?
Even final const! super const! final! const! doesn't work, in fact I agree it
makes it worse.

Good luck getting readability with that.

Ultimately, something else needs to be developed.

Perhaps a character we can use in alongside each item on the type declaration,
or we need to be able to hit the whole line in one blow.

Just my penny.
Mar 16 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Dan wrote:
 Wow...
 
 I've never put this much thought into const/final stuff.  Ultimately, the
program *cannot* prevent data in memory from being changed.  It can at best
prevent you from accidentally changing it; hence final and const declarations
mean "I don't want anyone to change these, can you raise an error if someone
tries to within D?"
 
 Also, the problem breaks down to 'what am I declaring const?'.  If I declare:
 
 const int* x; // is it the pointer, or the int?
 
 This doesn't seem important until you get to:
 
 const int[int**][]* x; // now what is it?
 
 Ultimately, you have this problem going through each and every pointer, array
and value.  You could theoretically write this:
 
 const int[int**][]*[char[]][3][17][3,17]* x;  and now what are you going to
do?  declare it:
 
 final const final final const const final final final final final const?
 Even final const! super const! final! const! doesn't work, in fact I agree it
makes it worse.
 
 Good luck getting readability with that.
 
 Ultimately, something else needs to be developed.
 
 Perhaps a character we can use in alongside each item on the type declaration,
or we need to be able to hit the whole line in one blow..
 
 Just my penny.

I'm not sure what this is meant to convey. In any language with type constructor you are able to construct long, incomprehensible types. You don't need storage classes and qualifiers for that. Andrei
Mar 16 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 14:05:17 -0700, Andrei Alexandrescu (See Website For
Email) wrote:

 Dan wrote:
 Wow...
 
 I've never put this much thought into const/final stuff.  Ultimately, the
program *cannot* prevent data in memory from being changed.  It can at best
prevent you from accidentally changing it; hence final and const declarations
mean "I don't want anyone to change these, can you raise an error if someone
tries to within D?"
 
 Also, the problem breaks down to 'what am I declaring const?'.  If I declare:
 
 const int* x; // is it the pointer, or the int?
 
 This doesn't seem important until you get to:
 
 const int[int**][]* x; // now what is it?
 
 Ultimately, you have this problem going through each and every pointer, array
and value.  You could theoretically write this:
 
 const int[int**][]*[char[]][3][17][3,17]* x;  and now what are you going to
do?  declare it:
 
 final const final final const const final final final final final const?
 Even final const! super const! final! const! doesn't work, in fact I agree it
makes it worse.
 
 Good luck getting readability with that.
 
 Ultimately, something else needs to be developed.
 
 Perhaps a character we can use in alongside each item on the type declaration,
or we need to be able to hit the whole line in one blow..
 
 Just my penny.

I'm not sure what this is meant to convey.

It is trying to convey the difficultly in determining which 'final'/'const'/'super const' instance refers to which part of the declaration. Given ... int[int**][]*[char[]][3][17][3,17]* x; and I want to make the 'x' immutable and the 'char[]' immutable, how does one write the declaration? Where does one place the 'final'/'const'/'super const' and where do the parenthesis go? -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Fri, 16 Mar 2007 14:05:17 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 
 Dan wrote:
 Wow...

 I've never put this much thought into const/final stuff.  Ultimately, the
program *cannot* prevent data in memory from being changed.  It can at best
prevent you from accidentally changing it; hence final and const declarations
mean "I don't want anyone to change these, can you raise an error if someone
tries to within D?"

 Also, the problem breaks down to 'what am I declaring const?'.  If I declare:

 const int* x; // is it the pointer, or the int?

 This doesn't seem important until you get to:

 const int[int**][]* x; // now what is it?

 Ultimately, you have this problem going through each and every pointer, array
and value.  You could theoretically write this:

 const int[int**][]*[char[]][3][17][3,17]* x;  and now what are you going to
do?  declare it:

 final const final final const const final final final final final const?
 Even final const! super const! final! const! doesn't work, in fact I agree it
makes it worse.

 Good luck getting readability with that.

 Ultimately, something else needs to be developed.

 Perhaps a character we can use in alongside each item on the type declaration,
or we need to be able to hit the whole line in one blow..

 Just my penny.


It is trying to convey the difficultly in determining which 'final'/'const'/'super const' instance refers to which part of the declaration. Given ... int[int**][]*[char[]][3][17][3,17]* x; and I want to make the 'x' immutable and the 'char[]' immutable, how does one write the declaration? Where does one place the 'final'/'const'/'super const' and where do the parenthesis go?

final int[int**][]*[const char[]][3][17][3,17]* x; Andrei
Mar 16 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Derek Parnell wrote:
 Given ...
 
    int[int**][]*[char[]][3][17][3,17]* x;
 
 and I want to make the 'x' immutable and the 'char[]' immutable, how does
 one write the declaration? Where does one place the 'final'/'const'/'super
 const' and where do the parenthesis go?

final int[int**][]*[ const(char[]) ][3][17][3,17]* x;
Mar 16 2007
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 Given ...

    int[int**][]*[char[]][3][17][3,17]* x;

 and I want to make the 'x' immutable and the 'char[]' immutable, how does
 one write the declaration? Where does one place the
 'final'/'const'/'super
 const' and where do the parenthesis go?

final int[int**][]*[ const(char[]) ][3][17][3,17]* x;

Hmm... I think either one of you (Walter and Andrei) got bitten by the "you have to put parens around the type or it'll get final'ed, too" thing, or one of you got bitten by the "is this going to make it final? Jeeze, I dunno; better put parens in just in case." This makes me somewhat worried :P -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Daniel Keep wrote:
 
 Walter Bright wrote:
 Derek Parnell wrote:
 Given ...

    int[int**][]*[char[]][3][17][3,17]* x;

 and I want to make the 'x' immutable and the 'char[]' immutable, how does
 one write the declaration? Where does one place the
 'final'/'const'/'super
 const' and where do the parenthesis go?


Hmm... I think either one of you (Walter and Andrei) got bitten by the "you have to put parens around the type or it'll get final'ed, too" thing, or one of you got bitten by the "is this going to make it final? Jeeze, I dunno; better put parens in just in case."

No, in this context the parens would be simply optional. Only when const occurs at top level it will engulf the symbol too. Andrei
Mar 16 2007
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 15:54:26 -0700, Walter Bright wrote:

 Derek Parnell wrote:
 Given ...
 
    int[int**][]*[char[]][3][17][3,17]* x;
 
 and I want to make the 'x' immutable and the 'char[]' immutable, how does
 one write the declaration? Where does one place the 'final'/'const'/'super
 const' and where do the parenthesis go?

final int[int**][]*[ const(char[]) ][3][17][3,17]* x;

Are you serious???? I want 'x' to be immutable so I need to use 'final' and place it the furtherest away from what is is qualifying. I want 'char[]' to be immutable so I must use 'const' and place it next to what it is qualifying plus use parenthesis around its target too. Why do you think that this is intuitive, consistent, and easy to read? -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 17 2007
next sibling parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Derek Parnell wrote:
 On Fri, 16 Mar 2007 15:54:26 -0700, Walter Bright wrote:
 Derek Parnell wrote:
 Given ...
 
 int[int**][]*[char[]][3][17][3,17]* x;
 
 and I want to make the 'x' immutable and the 'char[]' immutable, how does
  one write the declaration? Where does one place the 
 'final'/'const'/'super const' and where do the parenthesis go?


Are you serious???? I want 'x' to be immutable so I need to use 'final' and place it the furtherest away from what is is qualifying.

Or you want it to be static: static int[int**][]*[char[]][3][17][3,17]* x; Or you want it to be private: private int[int**][]*[char[]][3][17][3,17]* x; Or deprecated: deprecated int[int**][]*[char[]][3][17][3,17]* x; In all cases, you place the attribute the furthest away from what it qualifies. It's quite standard in C-family languages, including D, as I'm sure you know. I don't see why "final"/"const"/"super const" should be any different.
 I want 'char[]' to be immutable so I must use 'const' and place it next to 
 what it is qualifying plus use parenthesis around its target too.

You need it within the same "scope" as the char[]: inside the [] where the char[] is.
 Why do you think that this is intuitive, consistent, and easy to read?

What makes you think anything to do with a 35-character type is intuitive or easy to read? ;-) It's consistent enough for me. -- Remove ".doesnotlike.spam" from the mail address.
Mar 17 2007
prev sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Fri, 16 Mar 2007 15:54:26 -0700, Walter Bright wrote:
 
 Derek Parnell wrote:
 Given ...

    int[int**][]*[char[]][3][17][3,17]* x;

 and I want to make the 'x' immutable and the 'char[]' immutable, how does
 one write the declaration? Where does one place the 'final'/'const'/'super
 const' and where do the parenthesis go?


Are you serious???? I want 'x' to be immutable so I need to use 'final' and place it the furtherest away from what is is qualifying. I want 'char[]' to be immutable so I must use 'const' and place it next to what it is qualifying plus use parenthesis around its target too. Why do you think that this is intuitive, consistent, and easy to read?

Because you didn't suggest a better design. :o) Andrei
Mar 17 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Dan wrote:
 Wow...
 
 I've never put this much thought into const/final stuff.  Ultimately, the
program *cannot* prevent data in memory from being changed.  It can at best
prevent you from accidentally changing it; hence final and const declarations
mean "I don't want anyone to change these, can you raise an error if someone
tries to within D?"
 
 Also, the problem breaks down to 'what am I declaring const?'.  If I declare:
 
 const int* x; // is it the pointer, or the int?
 
 This doesn't seem important until you get to:
 
 const int[int**][]* x; // now what is it?
 
 Ultimately, you have this problem going through each and every pointer, array
and value.  You could theoretically write this:
 
 const int[int**][]*[char[]][3][17][3,17]* x;  and now what are you going to
do?  declare it:
 
 final const final final const const final final final final final const?
 Even final const! super const! final! const! doesn't work, in fact I agree it
makes it worse.
 
 Good luck getting readability with that.

This: const int[int**][]*[char[]][3][17][3,17]* x; is *already* unreadable (plus I don't think that [3,17] part is legal D). If any reasonable programmer really did have a need for such a type then they would probably break it up into aliases with some sort of meaning. --bb
Mar 16 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Bill Baxter wrote:
 This:
   const int[int**][]*[char[]][3][17][3,17]* x;
 
 is *already* unreadable (plus I don't think that [3,17] part is legal 
 D).

--- $ cat test.d import std.stdio; void main() { int[3,17] x; writefln(typeid(typeof(x))); } $ dmd -run test.d int[17] --- http://www.digitalmars.com/d/expression.html#Expressions --- The left operand of the , is evaluated, then the right operand is evaluated. The type of the expression is the type of the right operand, and the result is the result of the right operand. --- This is mostly for C and C++ compatibility, AFAICT. There, it's typically used in macros that need it to perform multiple actions that can only be done in separate expressions, without having the macros expand to full statements. But it's also used in one of the multiply-evaluated clauses of a loop statement (for example, it can be used to increment multiple counters of a for loop on each iteration). It's also horribly abused as an overloaded operator in (amongst others) boost::lambda, where it replaces ';' for lambda expressions...
Mar 16 2007
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Derek Parnell wrote:
 But you are saying that if I don't want 'final', I not
 only have to avoid using 'final', I also have to put some of the phrase
 (and I know I'll never remember which parts) in parenthesis. But wait ...
 there's more. If I do want it to be 'final' all I have to do is not
 use the
 'final' keyword.  Am I getting this right?

 Is it not possible to make reading code simple?

An intuition that I see reasonable is that const by default engulfs everything to its right: const char[] str = "Hi!"; // same as const(char[] str) = "Hi!"; When it engulfs the symbol, it makes it immutable, hence final. So by putting the parens you limit const's power. But I'd be glad to drop this exception. I'm not sure what Walter and people who'll have to modify their code would say though. ... Andrei

$ dmd -v1 With the v1 switch, I don't need to rewrite my code if it's already working and isn't being updated. And even if for some reason I do need to rewrite my code, I'm more than happy to do it in this case, if it gets us a sane, exception-free const system :) Heck, with D being as easy to parse as it is, I imagine that it wouldn't be terribly difficult for someone to write a source migration program; IIRC, Python actually had one of these for a particular version that broke several programs. Yeah, it's going to catch some people, but D !is Cpp, and there will always be things that are different. So long as these breaking changes are for the better, I can't imagine any reasonable person would complain[1]. -- Daniel [1]: Well, OK, they might complain anyway. But it's like how I could complain that my plans of doing car maintenance-related stuff has been ruined by this blasted wet weather! Considering that, as a country, we really need this sort of wet weather more often, and I know it, it's kinda an empty complaint :P -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 16 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Daniel Keep wrote:
 
 $ dmd -v1
 
 With the v1 switch, I don't need to rewrite my code if it's already
 working and isn't being updated.  And even if for some reason I do need
 to rewrite my code, I'm more than happy to do it in this case, if it
 gets us a sane, exception-free const system :)
 
 Heck, with D being as easy to parse as it is, I imagine that it wouldn't
 be terribly difficult for someone to write a source migration program;
 IIRC, Python actually had one of these for a particular version that
 broke several programs.

The python Numpy project has that too. There have been a lot of changes from the old version of numpy called Numeric, but you can fix almost all of the common cases using a script that comes with Numpy. I suspect the script to upgrade D code would be much much MUCH simpler than that.
 
 Yeah, it's going to catch some people, but D !is Cpp, and there will
 always be things that are different.  So long as these breaking changes
 are for the better, I can't imagine any reasonable person would complain[1].

 [1]: Well, OK, they might complain anyway.  But it's like how I could
 complain that my plans of doing car maintenance-related stuff has been
 ruined by this blasted wet weather!
 
 Considering that, as a country, we really need this sort of wet weather
 more often, and I know it, it's kinda an empty complaint :P

Great analogy. --bb
Mar 16 2007
prev sibling parent reply Dave <Dave_member pathlink.com> writes:
Andrei Alexandrescu (See Website For Email) Wrote:
 Derek Parnell wrote:
 On Fri, 16 Mar 2007 02:25:24 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 


<snip>
 
 I was just trying to minimize the amount of broken code. Also, we 

<snip>
 
 An intuition that I see reasonable is that const by default engulfs 
 everything to its right:
 
 const char[] str = "Hi!"; // same as const(char[] str) = "Hi!";
 
 When it engulfs the symbol, it makes it immutable, hence final. So by 
 putting the parens you limit const's power.
 
 But I'd be glad to drop this exception. I'm not sure what Walter and 
 people who'll have to modify their code would say though.
 

<snip>
 
 The EXCEPTION (alarms still ringing)  seems to be saying that 'const char[]
 X' means that X.ptr, X.length and the data in X's RAM cannot be changed.

I am hearing it. :o)

Ok, this maybe somewhat selfish since I haven't used D's 'const' much, but I really have to agree with Derek -- the exceptions need to be dropped <g> Based on past NG discussions on 'const', I believe it is so important to get this right the first time that the exceptions need to be removed in favor of the most intuitive and consistent syntax. Since the current "const char[] str = 'XYZ'"; semantics would prevent both str or its elements from being modified, we could make a reasonable assumption that code already properly tested and running (and just recompiled with a new compiler) would not break anyhow, correct? Thanks, - Dave
Mar 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Dave wrote:
 Ok, this maybe somewhat selfish since I haven't used D's 'const' much, but I
really have to agree with Derek -- the exceptions need to be dropped <g>
 
 Based on past NG discussions on 'const', I believe it is so important to get
this right the first time that the exceptions need to be removed in favor of
the most intuitive and consistent syntax.
 
 Since the current "const char[] str = 'XYZ'"; semantics would prevent both str
or its elements from being modified, we could make a reasonable assumption that
code already properly tested and running (and just recompiled with a new
compiler) would not break anyhow, correct?

I think this is a valid point. Andrei
Mar 23 2007
parent Dave <Dave_member pathlink.com> writes:
Andrei Alexandrescu (See Website For Email) Wrote:
 Dave wrote:
 Ok, this maybe somewhat selfish since I haven't used D's 'const' much, but I
really have to agree with Derek -- the exceptions need to be dropped <g>
 
 Based on past NG discussions on 'const', I believe it is so important to get
this right the first time that the exceptions need to be removed in favor of
the most intuitive and consistent syntax.
 
 Since the current "const char[] str = 'XYZ'"; semantics would prevent both str
or its elements from being modified, we could make a reasonable assumption that
code already properly tested and running (and just recompiled with a new
compiler) would not break anyhow, correct?

I think this is a valid point.

Maybe a few more justifications for getting rid of the exceptions: - It's more effort and complexity to add the exceptions to the compiler, docs., etc... - IIUC, a search-and-replace operation from 'const' to 'final' or 'final const' could 'fix' most current source code. - It would be better to have to add the exceptions later (based on programmer feedback) than to add them and then have to remove them. - The other case you mentioned (i.e.: const real MyPI = 3.14159;) should not be a problem with most currently correct D code (like const char[] str = "XYZ"; above). Could this case (a value type constant) warrant an error with the new compiler? Thanks, - Dave
Mar 24 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage 
 classes" manipulation that Andrei and others where thinking of for D. 
 The last I heard from it was Andrei's max suggestion from his max 
 design challenge, however, I think that suggestion may suffer from 
 some problems in regards to the "maxtype" requirement, plus it is 
 wholly incomplete in regards to how storage classes interact between 
 each other. Like Andrei said, what is a "const inout lazy const 
 char[]", if valid at all? Is there any news here? Is there a 
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.

Does this last category include some of the current use of D const -- a value which is not modifiable, *even in theory*, and may not even have any run-time existence at all -- the C equivalent being a #defined constant. IMHO (b) should be 'readonly' and (c) should be 'const'. But this may be because I'm a physicist, and for me a constant is something like the speed of light, and C++'s const_cast always seemed utterly nonsensical. void alabama() { // Actually compiles! Someone should be shot for this. const double PI = 3.14159265358; *const_cast<double *>(&PI) = 4.0; } Whereas 'readonly' seems to be a much better match for (b). Although it's an extra keyword, it seems to me that the concept discussed here is sufficiently fundamental to justify an extra keyword. Especially if we have a chance to rid of 'lazy'.
Mar 16 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage 
 classes" manipulation that Andrei and others where thinking of for D. 
 The last I heard from it was Andrei's max suggestion from his max 
 design challenge, however, I think that suggestion may suffer from 
 some problems in regards to the "maxtype" requirement, plus it is 
 wholly incomplete in regards to how storage classes interact between 
 each other. Like Andrei said, what is a "const inout lazy const 
 char[]", if valid at all? Is there any news here? Is there a 
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively);


Presumably this can also be an immutable view of immutable (case c) data?
 c) "superconst" - denoted as "const!" or "super const": type qualifier 
 meaning that the data is genuinely unmodifiable.

Does this last category include some of the current use of D const -- a value which is not modifiable, *even in theory*, and may not even have any run-time existence at all -- the C equivalent being a #defined constant. IMHO (b) should be 'readonly' and (c) should be 'const'.

 Whereas 'readonly' seems to be a much better match for (b). Although 
 it's an extra keyword, it seems to me that the concept discussed here is 
 sufficiently fundamental to justify an extra keyword. Especially if we 
 have a chance to rid of 'lazy'.

vote++; 'const' should really means "constant". A new keyword is more than justified here.
Mar 16 2007
next sibling parent reply sclytrack <sclytrack pi.be> writes:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const. It is like the foreach_reverse that we have in D. Why not call it foreachreverse instead. I mean we don't write for_each() { } but we write foreach() { } const! is ugly.
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
sclytrack wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const.

Yet you write static const not staticconst; public static not publicstatic; static if and not staticif; and so on. superconst came up too. But it creates a bad precedent. A healthier precedent is to synthesize phrases, not new keywords, from the existing keywords. Think of "static if" or "final switch". Very beautiful.
 It is like the foreach_reverse that we have in D. Why not
 call it foreachreverse instead.

Probably it's best to call it foreach reverse. It's unambiguous, easy to parse, does not add new keywords, and continues another nice precedent set by extern(language) and by scope(exit). Andrei
Mar 16 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 sclytrack wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const.

Yet you write static const not staticconst; public static not publicstatic; static if and not staticif; and so on. superconst came up too. But it creates a bad precedent. A healthier precedent is to synthesize phrases, not new keywords, from the existing keywords. Think of "static if" or "final switch". Very beautiful.
 It is like the foreach_reverse that we have in D. Why not
 call it foreachreverse instead.

Probably it's best to call it foreach reverse. It's unambiguous, easy to parse, does not add new keywords, and continues another nice precedent set by extern(language) and by scope(exit).

So you mean foreach(reverse) then? I do like that! You're right that it is quite D-like. Too bad you weren't around back when foreach_reverse was introduced? ;-) --bb
Mar 16 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 
 So you mean foreach(reverse) then?  I do like that!  You're right that 
 it is quite D-like.  Too bad you weren't around back when 
 foreach_reverse was introduced?   ;-)

This actually came up in more general terms: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D.announce&artnum=5015 Perhaps I over-complicated it with the talk of parallelism, but the idea was still there :-) Sean
Mar 16 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Bill Baxter wrote:
 So you mean foreach(reverse) then?  I do like that!  You're right that 
 it is quite D-like.  Too bad you weren't around back when 
 foreach_reverse was introduced?   ;-)

This actually came up in more general terms: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D. nnounce&artnum=5015 Perhaps I over-complicated it with the talk of parallelism, but the idea was still there :-)

You have it using foreach!(arg) syntax there. But it's not really a template. Maybe it could be, and if it were then that would be great syntax for it. But otherwise it's just unprecendented. A new special case. But what Andrei points out is that there are two precedents for the keyword(subkeyword) syntax, where the possible subkeywords are pre-defined by the compiler and completely change the behavior of the keyword, or rather select very different behaviors all under the same general umbrella. It's exactly what was needed for foreach and foreach_reverse. --bb
Mar 16 2007
parent Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:
 So you mean foreach(reverse) then?  I do like that!  You're right 
 that it is quite D-like.  Too bad you weren't around back when 
 foreach_reverse was introduced?   ;-)

This actually came up in more general terms: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D. nnounce&artnum=5015 Perhaps I over-complicated it with the talk of parallelism, but the idea was still there :-)

You have it using foreach!(arg) syntax there. But it's not really a template. Maybe it could be, and if it were then that would be great syntax for it. But otherwise it's just unprecendented. A new special case.

The syntax isn't really important. I was mostly looking for a way to avoid creating a new keyword while supporting multi-directional iteration. Sean
Mar 16 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 sclytrack wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const.

Yet you write static const not staticconst; public static not publicstatic; static if and not staticif; and so on. superconst came up too. But it creates a bad precedent. A healthier precedent is to synthesize phrases, not new keywords, from the existing keywords. Think of "static if" or "final switch". Very beautiful.
 It is like the foreach_reverse that we have in D. Why not
 call it foreachreverse instead.

Probably it's best to call it foreach reverse. It's unambiguous, easy to parse, does not add new keywords, and continues another nice precedent set by extern(language) and by scope(exit).

So you mean foreach(reverse) then? I do like that! You're right that it is quite D-like. Too bad you weren't around back when foreach_reverse was introduced? ;-)

Possibly even without the parens: foreach (i ; array) { ... } foreach reverse (i ; array) { ... } I have a feeling Walter is unlikely to change that though :o). Andrei
Mar 16 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 sclytrack wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const.

Yet you write static const not staticconst; public static not publicstatic; static if and not staticif; and so on. superconst came up too. But it creates a bad precedent. A healthier precedent is to synthesize phrases, not new keywords, from the existing keywords. Think of "static if" or "final switch". Very beautiful.
 It is like the foreach_reverse that we have in D. Why not
 call it foreachreverse instead.

Probably it's best to call it foreach reverse. It's unambiguous, easy to parse, does not add new keywords, and continues another nice precedent set by extern(language) and by scope(exit).

So you mean foreach(reverse) then? I do like that! You're right that it is quite D-like. Too bad you weren't around back when foreach_reverse was introduced? ;-)

Possibly even without the parens: foreach (i ; array) { ... } foreach reverse (i ; array) { ... } I have a feeling Walter is unlikely to change that though :o). Andrei

Except then 'reverse' would have to be a keyword too, no? Or would you do away with parens in the extern and scope cases too? I liked the suggestion because it makes foreach and its reverse variant less of a special case. scope and extern can be followed by things that that are keyword-like but that specify a specific variation of the keyword. Likewise, foreach would be the same. But if you're going to special case foreach in the end anyway, I see little point. It would also open the door for some sort of foreach(parallel) construct without having to introduce yet another underscored keyword with double-digit character count. --bb
Mar 16 2007
parent Kevin Bealer <kevinbealer gmail.com> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 sclytrack wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const.

Yet you write static const not staticconst; public static not publicstatic; static if and not staticif; and so on. superconst came up too. But it creates a bad precedent. A healthier precedent is to synthesize phrases, not new keywords, from the existing keywords. Think of "static if" or "final switch". Very beautiful.
 It is like the foreach_reverse that we have in D. Why not
 call it foreachreverse instead.

Probably it's best to call it foreach reverse. It's unambiguous, easy to parse, does not add new keywords, and continues another nice precedent set by extern(language) and by scope(exit).

So you mean foreach(reverse) then? I do like that! You're right that it is quite D-like. Too bad you weren't around back when foreach_reverse was introduced? ;-)

Possibly even without the parens: foreach (i ; array) { ... } foreach reverse (i ; array) { ... } I have a feeling Walter is unlikely to change that though :o). Andrei

Except then 'reverse' would have to be a keyword too, no?

I think the theory is that, like scope(exit), where the 'exit' is not really a keyword, in the foreach xyz (...) {...} expression, there is no legal token that can go where 'xyz' is, so something there doesn't need to be special. You could use "i" or "main" without conflicting with a variable or function name, for instance, because those can't appear there. It's only in places where a user-defined identifier is legal that a given 'adjective' word needs to be keywords. You could define this: if and only if () {} while away the hours () {} ... and not conflict with existing variables named (and, only, away, the, hours). Kevin
 
 --bb

Mar 21 2007
prev sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 sclytrack wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const.

Yet you write static const not staticconst; public static not publicstatic; static if and not staticif; and so on. superconst came up too. But it creates a bad precedent. A healthier precedent is to synthesize phrases, not new keywords, from the existing keywords. Think of "static if" or "final switch". Very beautiful.
 It is like the foreach_reverse that we have in D. Why not
 call it foreachreverse instead.

Probably it's best to call it foreach reverse. It's unambiguous, easy to parse, does not add new keywords, and continues another nice precedent set by extern(language) and by scope(exit). Andrei

How about the '1984' syntax? double + const int; // very very const double + super(); // call parent-of-parent constructor (just kidding!) I've read the description of the three const types and it's not clear to me which is which. It seems like being "immutable per se" (final) and "genuinely unmodifiable" are pretty much synonymous. This may seem backwards, but it would help me to keep them apart if I knew how they were implemented (semantically, not necessarily in terms of code generation). As I understand it, "const" means "readonly view of something", similar to C++'s const but without the big "disable me" button. The data is live but "under glass". "final const" means the compiler never generates code to modify the underlying data and can assume (cache, do constant folding on) the underlying data. Almost like a #define, except that you can take the address of it. "invariant" is just like const, but refers to the symbol (and by extension the data) rather than just the data. I'm guessing that volatile only affects const. An invariant object doesn't need it and a 'final' is also essentially compile time foldable and thus might not even be loaded from memory per se. (Eventually, and subject to quality of implementation, etc.) But before/after a volatile block, a normal "const" might need to be refreshed from memory in case someone else had updated? (Feel free to tell me that some of this has been answered, and I'll look over the thread again.) Kevin
Mar 21 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Kevin Bealer wrote:
 As I understand it, "const" means "readonly view of something", similar 
 to C++'s const but without the big "disable me" button.  The data is 
 live but "under glass".
 
 "final const" means the compiler never generates code to modify the 
 underlying data and can assume (cache, do constant folding on) the 
 underlying data.  Almost like a #define, except that you can take the 
 address of it.
 
 "invariant" is just like const, but refers to the symbol (and by 
 extension the data) rather than just the data.

Swap these two names. Andrei
Mar 21 2007
prev sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Kevin Bealer wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 sclytrack wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.

vote++ Keywords -------- I also think keywords can be written attached to one another. Like if you were to really call it "super const" have it called superconst instead of super_const.

Yet you write static const not staticconst; public static not publicstatic; static if and not staticif; and so on. superconst came up too. But it creates a bad precedent. A healthier precedent is to synthesize phrases, not new keywords, from the existing keywords. Think of "static if" or "final switch". Very beautiful.
 It is like the foreach_reverse that we have in D. Why not
 call it foreachreverse instead.

Probably it's best to call it foreach reverse. It's unambiguous, easy to parse, does not add new keywords, and continues another nice precedent set by extern(language) and by scope(exit). Andrei

How about the '1984' syntax? double + const int; // very very const double + super(); // call parent-of-parent constructor (just kidding!) I've read the description of the three const types and it's not clear to me which is which. It seems like being "immutable per se" (final) and "genuinely unmodifiable" are pretty much synonymous. This may seem backwards, but it would help me to keep them apart if I knew how they were implemented (semantically, not necessarily in terms of code generation). As I understand it, "const" means "readonly view of something", similar to C++'s const but without the big "disable me" button. The data is live but "under glass". "final const" means the compiler never generates code to modify the underlying data and can assume (cache, do constant folding on) the underlying data. Almost like a #define, except that you can take the address of it. "invariant" is just like const, but refers to the symbol (and by extension the data) rather than just the data. I'm guessing that volatile only affects const. An invariant object doesn't need it and a 'final' is also essentially compile time foldable and thus might not even be loaded from memory per se. (Eventually, and subject to quality of implementation, etc.) But before/after a volatile block, a normal "const" might need to be refreshed from memory in case someone else had updated? (Feel free to tell me that some of this has been answered, and I'll look over the thread again.) Kevin

Disclaimer: My current best understanding. Final -> You may not re-bind/re-assign to this symbol. The data bound to this symbol (ie, the object, struct, array, etc) is fully mutable. Its a storage class of the variable, and has no effect on the data itself. Foldable if its value is a basic scalar, such as an int. Const -> Part of the type, provides a read-only view into data owned by code elsewhere with a mutable referance. Invariant -> Part of the type, guaranteed absolutely not to change. Essentially a contract of sorts.. in fact, I'm starting to think of it as an invariant{} contract block which "simply" asserts the data is always in the same state as immediately after construction. Side effects of combinations: 'final const' -> An unbindable read-only view. Could be useful for passing read-only views of data by referance, ie: void foo (final const ref bar) -- foo cannot modify the data referanced by bar, nor can it reassign bar, which would've reassigned the original variable since its by ref. (The data referanced is constant, the referance itself -- ie the symbol -- is not.) 'final invariant' -> Ultimate const'ness. An unbindable variable whose value is a referance to unchanging data. Man I can't wait for all this to finally ship, in the hopes that it will come along with a thorough user manual... -- Chris Nicholson-Sauls
Mar 22 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage 
 classes" manipulation that Andrei and others where thinking of for 
 D. The last I heard from it was Andrei's max suggestion from his max 
 design challenge, however, I think that suggestion may suffer from 
 some problems in regards to the "maxtype" requirement, plus it is 
 wholly incomplete in regards to how storage classes interact between 
 each other. Like Andrei said, what is a "const inout lazy const 
 char[]", if valid at all? Is there any news here? Is there a 
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively);


Presumably this can also be an immutable view of immutable (case c) data?

Yes, super const is trivially convertible to const. Const alone really means "I don't care about the nature of the data, I just promise I have no business writing into it".
 c) "superconst" - denoted as "const!" or "super const": type 
 qualifier meaning that the data is genuinely unmodifiable.

Does this last category include some of the current use of D const -- a value which is not modifiable, *even in theory*, and may not even have any run-time existence at all -- the C equivalent being a #defined constant. IMHO (b) should be 'readonly' and (c) should be 'const'.

 Whereas 'readonly' seems to be a much better match for (b). Although 
 it's an extra keyword, it seems to me that the concept discussed here 
 is sufficiently fundamental to justify an extra keyword. Especially if 
 we have a chance to rid of 'lazy'.

vote++; 'const' should really means "constant". A new keyword is more than justified here.

We're doomed either way. We don't want to confuse C++ programmers. Two levels of strength of const are more descriptive than a new keyword that half of the people will think it's the other way. Andrei
Mar 16 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 We're doomed either way. We don't want to confuse C++ programmers. Two 
 levels of strength of const are more descriptive than a new keyword that 
 half of the people will think it's the other way.

True. If we have readonly and const, I expect endless confusion about which is which.
Mar 16 2007
parent Benji Smith <dlanguage benjismith.net> writes:
Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 We're doomed either way. We don't want to confuse C++ programmers. Two 
 levels of strength of const are more descriptive than a new keyword 
 that half of the people will think it's the other way.

True. If we have readonly and const, I expect endless confusion about which is which.

Not to mention if you also have 'readsometimes'. Nobody'll EVER figure that one out :) --benji
Mar 16 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage 
 classes" manipulation that Andrei and others where thinking of for D. 
 The last I heard from it was Andrei's max suggestion from his max 
 design challenge, however, I think that suggestion may suffer from 
 some problems in regards to the "maxtype" requirement, plus it is 
 wholly incomplete in regards to how storage classes interact between 
 each other. Like Andrei said, what is a "const inout lazy const 
 char[]", if valid at all? Is there any news here? Is there a 
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.

Does this last category include some of the current use of D const -- a value which is not modifiable, *even in theory*, and may not even have any run-time existence at all -- the C equivalent being a #defined constant.

Yes.
 IMHO (b) should be 'readonly' and (c) should be 'const'.
 But this may be because I'm a physicist, and for me a constant is 
 something like the speed of light, and C++'s const_cast always seemed 
 utterly nonsensical.
 
 void alabama() {  // Actually compiles! Someone should be shot for this.
    const double PI = 3.14159265358;
    *const_cast<double *>(&PI) = 4.0;
 }
 
 Whereas 'readonly' seems to be a much better match for (b). Although 
 it's an extra keyword, it seems to me that the concept discussed here is 
 sufficiently fundamental to justify an extra keyword. Especially if we 
 have a chance to rid of 'lazy'.

If we get rid of lazy, I'll sacrifice a lamb :o). We've shoved keywords like "readonly" and "view" for a while, and they just added more confusion than cleared, at a high cost. If (as I suspect) super const appears relatively infrequently, it might be fine to just encode it as the mildly verbose "super const". In fact, it's shorter than "keepyourgrubbyhandsoff". :o) Andrei
Mar 16 2007
next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage 
 classes" manipulation that Andrei and others where thinking of for 
 D. The last I heard from it was Andrei's max suggestion from his max 
 design challenge, however, I think that suggestion may suffer from 
 some problems in regards to the "maxtype" requirement, plus it is 
 wholly incomplete in regards to how storage classes interact between 
 each other. Like Andrei said, what is a "const inout lazy const 
 char[]", if valid at all? Is there any news here? Is there a 
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.

Does this last category include some of the current use of D const -- a value which is not modifiable, *even in theory*, and may not even have any run-time existence at all -- the C equivalent being a #defined constant.

Yes.
 IMHO (b) should be 'readonly' and (c) should be 'const'.
 But this may be because I'm a physicist, and for me a constant is 
 something like the speed of light, and C++'s const_cast always seemed 
 utterly nonsensical.

 void alabama() {  // Actually compiles! Someone should be shot for this.
    const double PI = 3.14159265358;
    *const_cast<double *>(&PI) = 4.0;
 }

 Whereas 'readonly' seems to be a much better match for (b). Although 
 it's an extra keyword, it seems to me that the concept discussed here 
 is sufficiently fundamental to justify an extra keyword. Especially if 
 we have a chance to rid of 'lazy'.

If we get rid of lazy, I'll sacrifice a lamb :o). We've shoved keywords like "readonly" and "view" for a while, and they just added more confusion than cleared, at a high cost. If (as I suspect) super const appears relatively infrequently, it might be fine to just encode it as the mildly verbose "super const". In fact, it's shorter than "keepyourgrubbyhandsoff". :o) Andrei

My problem is, I don't see any connection between "don't touch" and "constant" except that C++ got them confused. In every other other context outside of C++, "constant" means "it will not change". Mathematically, you can "hold something constant" (even though it is actually a variable), but as long as it is being held constant, it can't change. Suppose in a function, you have 'const' access to a global variable. Another thread with write-access to that variable can change it, while the function is executing. It makes sense for a function to have read-only access to a variable, and need to obtain a mutex when it wants to obtain the current value. But that's not a constant in any reasonable sense of the word. Don.
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Don Clugston wrote:
 My problem is, I don't see any connection between "don't touch" and 
 "constant" except that C++ got them confused. In every other other 
 context outside of C++,  "constant" means "it will not change".
 
 Mathematically, you can "hold something constant" (even though it is 
 actually a variable), but as long as it is being held constant, it can't 
 change.
 
 Suppose in a function, you have 'const' access to a global variable.
 Another thread with write-access to that variable can change it, while 
 the function is executing.
 
 It makes sense for a function to have read-only access to a variable, 
 and need to obtain a mutex when it wants to obtain the current value.
 But that's not a constant in any reasonable sense of the word.

The difference between math and computer memory is that all too often in the latter case you want to express modular separation very often. Modular separation (in this context) means that the right to modify a certain object is owned by some part of program, and other parts may be offered only a view of that memory. This is such an important concept, even C adopted it. All of C's library functions that take pointers are anontated in their signature to specify whether or not they modify their input or not. You want to enforce modular mutability cheaply. If D's immutability features fail to achieve that, we've done nothing. Andrei
Mar 16 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 My problem is, I don't see any connection between "don't touch" and 
 "constant" except that C++ got them confused. In every other other 
 context outside of C++,  "constant" means "it will not change".

 Mathematically, you can "hold something constant" (even though it is 
 actually a variable), but as long as it is being held constant, it 
 can't change.

 Suppose in a function, you have 'const' access to a global variable.
 Another thread with write-access to that variable can change it, while 
 the function is executing.

 It makes sense for a function to have read-only access to a variable, 
 and need to obtain a mutex when it wants to obtain the current value.
 But that's not a constant in any reasonable sense of the word.

The difference between math and computer memory is that all too often in the latter case you want to express modular separation very often. Modular separation (in this context) means that the right to modify a certain object is owned by some part of program, and other parts may be offered only a view of that memory. This is such an important concept, even C adopted it. All of C's library functions that take pointers are anontated in their signature to specify whether or not they modify their input or not. You want to enforce modular mutability cheaply. If D's immutability features fail to achieve that, we've done nothing.

I don't think he's arguing against immutable views, just against calling it 'const' ;). In his words, from an earlier post: ===== Don Clugston wrote:
 IMHO (b) should be 'readonly' and (c) should be 'const'.
 But this may be because I'm a physicist, and for me a constant is
 something like the speed of light, and C++'s const_cast always seemed
 utterly nonsensical.

 void alabama() {  // Actually compiles! Someone should be shot for this.
    const double PI = 3.14159265358;
    *const_cast<double *>(&PI) = 4.0;
 }

 Whereas 'readonly' seems to be a much better match for (b). Although
 it's an extra keyword, it seems to me that the concept discussed here is
 sufficiently fundamental to justify an extra keyword. Especially if we
 have a chance to rid of 'lazy'.

In fact, I think most people here will agree that the ability for a function to specify "I won't modify this" enforced by the compiler (modulo pointer casts etc.) is a good thing. Especially if, as you mentioned, the function can return an (im)mutable result depending on whether a parameter was (im)mutable without having to rewrite the function, since that seems to be the biggest annoyance about C++'s constness system.
Mar 16 2007
parent Don Clugston <dac nospam.com.au> writes:
Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 My problem is, I don't see any connection between "don't touch" and 
 "constant" except that C++ got them confused. In every other other 
 context outside of C++,  "constant" means "it will not change".

 Mathematically, you can "hold something constant" (even though it is 
 actually a variable), but as long as it is being held constant, it 
 can't change.

 Suppose in a function, you have 'const' access to a global variable.
 Another thread with write-access to that variable can change it, 
 while the function is executing.

 It makes sense for a function to have read-only access to a variable, 
 and need to obtain a mutex when it wants to obtain the current value.
 But that's not a constant in any reasonable sense of the word.

The difference between math and computer memory is that all too often in the latter case you want to express modular separation very often. Modular separation (in this context) means that the right to modify a certain object is owned by some part of program, and other parts may be offered only a view of that memory. This is such an important concept, even C adopted it. All of C's library functions that take pointers are anontated in their signature to specify whether or not they modify their input or not. You want to enforce modular mutability cheaply. If D's immutability features fail to achieve that, we've done nothing.

I don't think he's arguing against immutable views, just against calling it 'const' ;).

Exactly. Thank you. I'm purely concerned about the name.
Mar 16 2007
prev sibling next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 We've shoved keywords like "readonly" and "view" for a while, and they 
 just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be fine 
 to just encode it as the mildly verbose "super const". In fact, it's 
 shorter than "keepyourgrubbyhandsoff". :o)
 
 Andrei

Really? I'd think super const would be used all the time. Anywhere a class has some invariant field, it'll probably be expressed as super const (if I'm understanding correctly that super const is the equivalent of #define constants in C++ or static final constants in Java). Let me join the ranks of those who hate both versions of the new syntax. const! looks either like a template or like "not const". You guys may think you're saving a keyword, and that might be technically correct from the perspective of the parser. But, for the perspective of the D developer, it still looks like you've introduced a new keyword, but it's a keyword that's A) nearly identical to another keyword, and B) contains a symbol. Ugh. I really. Really. Really. Hate it. Using "super const" is also annoying because all of the other storage classes use a single keyword. Now we've got this weird case that uses two keywords. And the word "super" is now overloaded in two completely unrelated concepts. (By the way, I don't like "static if" either. I think the right choice would have been #if, #foreach, etc. And I think C++ made a mistake by defining "long long" and "double double" types as well. Presumably, that saved some keywords, but we'd all have been better off if the types were defined as int8, int16, int32, int64, float32, float64, etc). I vote for "readonly" and "const". Anything else seems like a mistake. --benji
Mar 16 2007
next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Benji Smith wrote:
 And the word "super" is now overloaded in two completely unrelated concepts.
 

Which isn't that rare, see the following link: http://www.prowiki.org/wiki4d/wiki.cgi?LanguageSpecification/KeywordIndex I find the following examples: auto, in, is, mixin, scope, static. I have come to abhor the keyword "static" largely due to the above. Too bad its various uses are unavoidably useful. <g> But I do agree with you, in a way. After writing the following I find my explanation somewhat unclear even to myself, but bear with me. The problem with "super const" is that "super" modifies the other keyword, "const", instead of saying something about the expression/statement/whatever (in this case, the type). Even "static", in all its horror, can be seen as specifying one of "module-level" or "compile-time". The other keywords mentioned above use their English meaning as such an explanation, and so can be understood without too much thought. "super const" would make "super" another "static": lacking context, you can't be sure about about even the general meaning, let alone the one in a specific instance. Upon reflection it may be moot, since one probably rarely cares about keywords' meanings without context, but I'm sure some psychologist could come up with something about intuitiveness which affects coding speed, or whatever. I'd be happiest to just get the feature as soon as possible; worrying about keyword semantics can come later. Just like the abominations beginning with "on_scope_". <g> -- Remove ".doesnotlike.spam" from the mail address.
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Deewiant wrote:
 Benji Smith wrote:
 And the word "super" is now overloaded in two completely unrelated concepts.

Which isn't that rare, see the following link: http://www.prowiki.org/wiki4d/wiki.cgi?LanguageSpecification/KeywordIndex I find the following examples: auto, in, is, mixin, scope, static. I have come to abhor the keyword "static" largely due to the above. Too bad its various uses are unavoidably useful. <g> But I do agree with you, in a way. After writing the following I find my explanation somewhat unclear even to myself, but bear with me. The problem with "super const" is that "super" modifies the other keyword, "const", instead of saying something about the expression/statement/whatever (in this case, the type). Even "static", in all its horror, can be seen as specifying one of "module-level" or "compile-time". The other keywords mentioned above use their English meaning as such an explanation, and so can be understood without too much thought. "super const" would make "super" another "static": lacking context, you can't be sure about about even the general meaning, let alone the one in a specific instance. Upon reflection it may be moot, since one probably rarely cares about keywords' meanings without context, but I'm sure some psychologist could come up with something about intuitiveness which affects coding speed, or whatever.

Working in NLP myself, I totally disagree. Natural language is rife with homonimy and synonimy, and we're all very, very happy with that. But in this case we don't even have to go that far: "super" is an adjective meaning primarily "of high grade or quality" and qualifies a noun. If there's no noun, you don't know what is "of high grade or quality". It's a fact, and we don't even blink. The reality behind super const is that it expresses a stronger form of const. It makes a lot of sense if I dub it "super const" in natural language, to express it in the same way in the programming language. I thought that that's too verbose and devised "const!" as a shortcut (again with obvious conotations in natural language), but I'm thinking of dropping it and let type inference add "super" to "const" whenever it can. That should ensure exactness of the type system without verbosity in source code.
 I'd be happiest to just get the feature as soon as possible; worrying about
 keyword semantics can come later. Just like the abominations beginning with
 "on_scope_". <g>

Great idea. :o) Andrei
Mar 16 2007
next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Deewiant wrote:
 The problem with "super const" is that "super" modifies the other keyword, 
 "const", instead of saying something about the 
 expression/statement/whatever (in this case, the type). Even "static", in
 all its horror, can be seen as specifying one of "module-level" or
 "compile-time". The other keywords mentioned above use their English
 meaning as such an explanation, and so can be understood without too much
 thought. "super const" would make "super" another "static": lacking
 context, you can't be sure about about even the general meaning, let alone
 the one in a specific instance.
 
 Upon reflection it may be moot, since one probably rarely cares about 
 keywords' meanings without context, but I'm sure some psychologist could
 come up with something about intuitiveness which affects coding speed, or
 whatever.

Working in NLP myself, I totally disagree. Natural language is rife with homonimy and synonimy, and we're all very, very happy with that. But in this case we don't even have to go that far: "super" is an adjective meaning primarily "of high grade or quality" and qualifies a noun. If there's no noun, you don't know what is "of high grade or quality". It's a fact, and we don't even blink.

Oh, I didn't say I was of that opinion myself. In fact, I have a tendency to disagree with psychology in general. <g> You're right and that's good a point.
 The reality behind super const is that it expresses a stronger form of const.
 It makes a lot of sense if I dub it "super const" in natural language, to
 express it in the same way in the programming language. I thought that that's
 too verbose and devised "const!" as a shortcut (again with obvious
 conotations in natural language), but I'm thinking of dropping it and let
 type inference add "super" to "const" whenever it can. That should ensure
 exactness of the type system without verbosity in source code.

"const!" has the advantage of being one word, but not really a new keyword. "super const" is two words. I still can't really express why I see that as a bad thing, but it _is_ better than adding a new keyword like "superconst" (or "keepyourgrubbyhandsoff"). I wouldn't be opposed to something like "readonly" though. Maybe I've just been coding too much Java recently: if I have to write "private final static double" much more I'm going to puke. <g> It's largely a question of what one's used to. Elsewhere in the thread the fact that some languages have "elsif" or "elif" or equivalent instead of "else if" was mentioned. I don't really see much of a difference, except that "elif" takes a bit too long to process for me since the "el" -> "else" relation isn't intuitive. But, just like an eventual "super const" (and the current "static"), coding enough in a language with such will make it intuitive to me, and I won't complain (much). In general, I do agree with your view, found elsewhere in the thread, that using existing keywords is best. That, or constructs like "scope (success)" where success isn't a keyword. I'm just currently a bit repelled by the pair "super const". -- Remove ".doesnotlike.spam" from the mail address.
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Deewiant wrote:
[snip]

 In general, I do agree with your view, found elsewhere in the thread, that
using
 existing keywords is best. That, or constructs like "scope (success)" where
 success isn't a keyword. I'm just currently a bit repelled by the pair "super
 const".

Well how about "real const" then :oD. I think the good news is that you don't need to type it that often. You type: const char[] str = "Hi!"; and the compiler figures out the "super" part itself. Andrei
Mar 16 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Deewiant wrote:
 [snip]
 
 In general, I do agree with your view, found elsewhere in the thread, 
 that using
 existing keywords is best. That, or constructs like "scope (success)" 
 where
 success isn't a keyword. I'm just currently a bit repelled by the pair 
 "super
 const".

Well how about "real const" then :oD. I think the good news is that you don't need to type it that often. You type: const char[] str = "Hi!"; and the compiler figures out the "super" part itself.

Doh! 'real const' of course... how could I leave that one out. 'real const' 'true const' 'double const' 'finally const' 'is const' or 'is finally super double true real const' --bb
Mar 16 2007
parent Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Deewiant wrote:
 [snip]

 In general, I do agree with your view, found elsewhere in the thread, 
 that using
 existing keywords is best. That, or constructs like "scope (success)" 
 where
 success isn't a keyword. I'm just currently a bit repelled by the 
 pair "super
 const".

Well how about "real const" then :oD. I think the good news is that you don't need to type it that often. You type: const char[] str = "Hi!"; and the compiler figures out the "super" part itself.

Doh! 'real const' of course... how could I leave that one out. 'real const' 'true const' 'double const' 'finally const' 'is const' or 'is finally super double true real const'

'const is back and now its really mad' ?
Mar 16 2007
prev sibling next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 
 Working in NLP myself, I totally disagree. Natural language is rife with 
 homonimy and synonimy, and we're all very, very happy with that. But in 
 this case we don't even have to go that far: "super" is an adjective 
 meaning primarily "of high grade or quality" and qualifies a noun. If 
 there's no noun, you don't know what is "of high grade or quality". It's 
 a fact, and we don't even blink.
 
 Andrei

I've also worked in NLP quite a bit, but I don't think the constructs of natural languages are necessarily healthy in artificial languages. Many natural language constructs are only distinguishable through contextual analysis awareness or sense disambiguation (anaphora resolution and prepositional phrase attachment are among the most difficult to disambiguate). Natural languages rife with ambiguity may be easy for native speakers to disambiguate (thanks to the semantic model we develop during early childhood), but those ambiguities make it more difficult for adults to learn new languages. I like artificial languages to be artificial. I like them to be concise and unambiguous. I like them to have context-free grammars free from left-recursion. I don't like the word "static" to mean one thing in one context and something else entirely in another context. I don't like keywords to contain punctuation marks. And I don't like phrasal nouns (like "super const" or the inevitable "super duper const"). Just like highly ambiguous natural languages are difficult for adults to learn, I think synonymy and homonimy make it more difficult for professional developers to learn a new programming language. I used to write a lot of perl code, and the constructs it borrowed from natural language (synonymy, homonimy, anaphora, implied words, pronouns, etc) are some of the things that drove me away from the language toward languages with more explicit semantics (java, C#, python, d). But that's just me. Not everyone will agree with my opinion, and if I'm not in the consensus, I'll concede. But there seem to be a lot of people who agree with me. I reiterate: "const" and "readonly" are the way to go. (Methods which promise not to modify the values of their parameters should use "readonly" rather than "const" in their method signatures.) --benji
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Working in NLP myself, I totally disagree. Natural language is rife 
 with homonimy and synonimy, and we're all very, very happy with that. 
 But in this case we don't even have to go that far: "super" is an 
 adjective meaning primarily "of high grade or quality" and qualifies a 
 noun. If there's no noun, you don't know what is "of high grade or 
 quality". It's a fact, and we don't even blink.

 Andrei

I've also worked in NLP quite a bit, but I don't think the constructs of natural languages are necessarily healthy in artificial languages.

Me neither. Of course you don't want to do WSD to figure out the meaning of a keyword in a context. All I'm saying is that you can be expressive without blowing up your vocabulary by using collocations.
 I used to write a lot of perl code, and the constructs it borrowed from 
 natural language (synonymy, homonimy, anaphora, implied words, pronouns, 
 etc) are some of the things that drove me away from the language toward 
 languages with more explicit semantics (java, C#, python, d).

I think it's pretty clear to many in which places Perl went a little too far :o).
 But that's just me. Not everyone will agree with my opinion, and if I'm 
 not in the consensus, I'll concede. But there seem to be a lot of people 
 who agree with me.

I don't think so. This discussion is very small in numbers, and the strength of the interlocutors' arguments does not increase their number. At the end of the day, if we design a good feature, people (who participated here or not) will be glad to use it; if not, they won't. And that's that.
 I reiterate: "const" and "readonly" are the way to go. (Methods which 
 promise not to modify the values of their parameters should use 
 "readonly" rather than "const" in their method signatures.)

This makes const gratuitously incompatible to C++'s const, and (worse) also adds a keyword that's its equivalent. I don't think this can fly. Andrei
Mar 16 2007
next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 But that's just me. Not everyone will agree with my opinion, and if 
 I'm not in the consensus, I'll concede. But there seem to be a lot of 
 people who agree with me.

I don't think so. This discussion is very small in numbers, and the strength of the interlocutors' arguments does not increase their number.

What???? Blasphemy!!!!
 I reiterate: "const" and "readonly" are the way to go. (Methods which 
 promise not to modify the values of their parameters should use 
 "readonly" rather than "const" in their method signatures.)

This makes const gratuitously incompatible to C++'s const, and (worse) also adds a keyword that's its equivalent. I don't think this can fly.

Good point. I like kris's suggestion to get rid of the "const" keyword entirely and replace it with something else ("constant" seems like a reasonable suggestion). Here are some other synonyms for "constant": http://thesaurus.reference.com/browse/constant There might be something useful in there. I'm personally fond of "chronic". :-) --benji
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 But that's just me. Not everyone will agree with my opinion, and if 
 I'm not in the consensus, I'll concede. But there seem to be a lot of 
 people who agree with me.

I don't think so. This discussion is very small in numbers, and the strength of the interlocutors' arguments does not increase their number.

What???? Blasphemy!!!!
 I reiterate: "const" and "readonly" are the way to go. (Methods which 
 promise not to modify the values of their parameters should use 
 "readonly" rather than "const" in their method signatures.)

This makes const gratuitously incompatible to C++'s const, and (worse) also adds a keyword that's its equivalent. I don't think this can fly.

Good point. I like kris's suggestion to get rid of the "const" keyword entirely and replace it with something else ("constant" seems like a reasonable suggestion). Here are some other synonyms for "constant": http://thesaurus.reference.com/browse/constant There might be something useful in there. I'm personally fond of "chronic". :-)

Believe me, I pored over thesauri for a long, long time. Andrei
Mar 16 2007
prev sibling next sibling parent Walter Bright <newshound digitalmars.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 This makes const gratuitously incompatible to C++'s const, and (worse) 
 also adds a keyword that's its equivalent. I don't think this can fly.

There's some experience with that: the alias and typedef declarations. But that experience makes me wary of doing it in a much bigger way.
Mar 16 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:

 I reiterate: "const" and "readonly" are the way to go. (Methods which 
 promise not to modify the values of their parameters should use 
 "readonly" rather than "const" in their method signatures.)

This makes const gratuitously incompatible to C++'s const, and (worse) also adds a keyword that's its equivalent. I don't think this can fly.

What about 'immutable' for 'really can't change'/'superconst', and const can stay as is? --bb
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:

 I reiterate: "const" and "readonly" are the way to go. (Methods which 
 promise not to modify the values of their parameters should use 
 "readonly" rather than "const" in their method signatures.)

This makes const gratuitously incompatible to C++'s const, and (worse) also adds a keyword that's its equivalent. I don't think this can fly.

What about 'immutable' for 'really can't change'/'superconst', and const can stay as is?

I like it, and suggested it at a point. Walter said that it's bad marketing to define keywords in terms of a negative. Andrei
Mar 16 2007
parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:

 Benji Smith wrote:

 I reiterate: "const" and "readonly" are the way to go. (Methods 
 which promise not to modify the values of their parameters should 
 use "readonly" rather than "const" in their method signatures.)

This makes const gratuitously incompatible to C++'s const, and (worse) also adds a keyword that's its equivalent. I don't think this can fly.

What about 'immutable' for 'really can't change'/'superconst', and const can stay as is?

I like it, and suggested it at a point. Walter said that it's bad marketing to define keywords in terms of a negative.

So, "invariant" is already a keyword ... what about that? "Invariant" is currently used to stipulate a condition that must remain constant (or true) for the extent of the enclosing/relevant scope. Right now, it is used within a class only, but the semantics could presumably extend elsewhere too: invariant int * p = &x; void myFunc (invariant char[] arg) {} in both these cases, the "invariance" should remain for the extent of the relevant scope?
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:

 Andrei Alexandrescu (See Website For Email) wrote:

 Benji Smith wrote:

 I reiterate: "const" and "readonly" are the way to go. (Methods 
 which promise not to modify the values of their parameters should 
 use "readonly" rather than "const" in their method signatures.)

This makes const gratuitously incompatible to C++'s const, and (worse) also adds a keyword that's its equivalent. I don't think this can fly.

What about 'immutable' for 'really can't change'/'superconst', and const can stay as is?

I like it, and suggested it at a point. Walter said that it's bad marketing to define keywords in terms of a negative.

So, "invariant" is already a keyword ... what about that? "Invariant" is currently used to stipulate a condition that must remain constant (or true) for the extent of the enclosing/relevant scope. Right now, it is used within a class only, but the semantics could presumably extend elsewhere too: invariant int * p = &x; void myFunc (invariant char[] arg) {} in both these cases, the "invariance" should remain for the extent of the relevant scope?

I completely missed that one. I think it's a good idea to look into it as a candidate for a qualifier name. Thank you. Andrei
Mar 16 2007
next sibling parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:

 So, "invariant" is already a keyword ... what about that?

 "Invariant" is currently used to stipulate a condition that must 
 remain constant (or true) for the extent of the enclosing/relevant 
 scope. Right now, it is used within a class only, but the semantics 
 could presumably extend elsewhere too:

 invariant int * p = &x;

 void myFunc (invariant char[] arg) {}

 in both these cases, the "invariance" should remain for the extent of 
 the relevant scope?

I completely missed that one. I think it's a good idea to look into it as a candidate for a qualifier name. Thank you.

yw If it gets tossed for whatever reason, It might be worthwhile renaming the current "invariant" to whatever is settled upon? That may reclaim a keyword at least.
Mar 16 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:

 So, "invariant" is already a keyword ... what about that?

 "Invariant" is currently used to stipulate a condition that must 
 remain constant (or true) for the extent of the enclosing/relevant 
 scope. Right now, it is used within a class only, but the semantics 
 could presumably extend elsewhere too:

 invariant int * p = &x;

 void myFunc (invariant char[] arg) {}

 in both these cases, the "invariance" should remain for the extent of 
 the relevant scope?

I completely missed that one. I think it's a good idea to look into it as a candidate for a qualifier name. Thank you.


Typically with invariants in CS the meaning is that "you can change all you want as long as you put things back the way you found them when you're done". And that's definitely not the connotation desired here.
 If it gets tossed for whatever reason, It might be worthwhile renaming 
 the current "invariant" to whatever is settled upon? That may reclaim a 
 keyword at least.

Invariant can refer to an abstract property that doesn't change, like "full.length + empty.length == buffer.length". The individual constituents of that expression can change all they want. So calling it constant/readonly/immutable doesn't make as much sense as 'invariant'. --bb
Mar 16 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 So, "invariant" is already a keyword ... what about that?

as a candidate for a qualifier name. Thank you.

I agree. I think: final const invariant for the three cases looks pretty good.
Mar 17 2007
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sat, 17 Mar 2007 19:09:16 -0700, Walter Bright wrote:

I'm sorry I'm so thick, but have I got the idea right yet ... ?
 
 final

done by either the compiler or at run time depending on the amount of knowledge the compiler has about the location's usage.
 const

declaration from being able to modify the item being declared.
 invariant

the declaration from being able to modify the item being declared. As you can see, I'm confused as to how the qualifier effects which code is allowed to change which items. Even more so when it comes to reference items ... 'cos I'm not sure how to use these qualifiers to specify whether the reference and/or the data being referenced can be changed, and by whom. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 17 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Derek Parnell wrote:
 final

done by either the compiler or at run time depending on the amount of knowledge the compiler has about the location's usage.

No. This applies to rebinding of a name: final x = 3; x = 4; // error, x is final i.e. final applies to the declared name, not the type.
 const

declaration from being able to modify the item being declared.

No. This means that it is a readonly view of data - other views of the same data may change it. char[] s = "hello".dup; const char[] t = s; t[0] = 'j'; // error, const char[] is a readonly view s[0] = 'j'; // ok writefln(t); // prints "jello" t = s[1..2]; // ok, as t is not final Note that const applies to the type, not the name.
 invariant

the declaration from being able to modify the item being declared.

Almost right. It isn't the declaration, but the *type* that is invariant. Invariant applies to the type, not the name.
 As you can see, I'm confused as to how the qualifier effects which code is
 allowed to change which items. Even more so when it comes to reference
 items ... 'cos I'm not sure how to use these qualifiers to specify whether
 the reference and/or the data being referenced can be changed, and by whom.

'final' is a storage class, like 'static'. It doesn't apply to the type of the symbol, only the symbol itself. 'const' and 'invariant' apply to the type of the symbol, not the symbol.
Mar 17 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 Derek Parnell wrote:

 invariant

application as the declaration from being able to modify the item being declared.

Almost right. It isn't the declaration, but the *type* that is invariant. Invariant applies to the type, not the name.

By the way, "invariant" is a lifesaver. Everybody and their sister in the coffee shop liked it at the first sight. The girl at the counter asked for kris' email :o). Yes, "invariant" is essentially a synonym to "constant", but here are two simple mnemonic devices to remember which is which: a) "const" is shorter because it's used more often; b) "const" is similar to C++'s homonym. Easier to remember than perl's $|. Andrei
Mar 17 2007
parent reply Alex Burton <alexibu mac.com> writes:
Andrei Alexandrescu (See Website For Email) Wrote:

 Walter Bright wrote:
 Derek Parnell wrote:

 invariant

application as the declaration from being able to modify the item being declared.

Almost right. It isn't the declaration, but the *type* that is invariant. Invariant applies to the type, not the name.

By the way, "invariant" is a lifesaver. Everybody and their sister in the coffee shop liked it at the first sight. The girl at the counter asked for kris' email :o). Yes, "invariant" is essentially a synonym to "constant", but here are two simple mnemonic devices to remember which is which: a) "const" is shorter because it's used more often; b) "const" is similar to C++'s homonym. Easier to remember than perl's $|. Andrei

Another option I'd like to have at least considered is the finding of a character that can be used to denote const. Adding various constnesses to all the code could easily add a whole lot of typing to the task of getting things done in D. Could we find a unicode character that wouldn't be too much trouble for editors to insert ? Might mean that D starts to require unicode source - but is that such a bad thing ? Are there any ascii characters that can be found that wont confuse the parser ? - like how the use of ! works for templates Alex
Mar 18 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Alex Burton wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:
 
 Walter Bright wrote:
 Derek Parnell wrote:

 invariant

application as the declaration from being able to modify the item being declared.

invariant. Invariant applies to the type, not the name.

the coffee shop liked it at the first sight. The girl at the counter asked for kris' email :o). Yes, "invariant" is essentially a synonym to "constant", but here are two simple mnemonic devices to remember which is which: a) "const" is shorter because it's used more often; b) "const" is similar to C++'s homonym. Easier to remember than perl's $|. Andrei

Another option I'd like to have at least considered is the finding of a character that can be used to denote const. Adding various constnesses to all the code could easily add a whole lot of typing to the task of getting things done in D. Could we find a unicode character that wouldn't be too much trouble for editors to insert ? Might mean that D starts to require unicode source - but is that such a bad thing ? Are there any ascii characters that can be found that wont confuse the parser ? - like how the use of ! works for templates

Type inference will reduce the need for littering code with "const" a great deal (when compared to today's C++ code, at least). You just write: auto var = expression; and the const will propagate. Initial experience will show whether this is enough. Andrei
Mar 18 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Alex Burton wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:

 Walter Bright wrote:
 Derek Parnell wrote:

 invariant

application as the declaration from being able to modify the item being declared.

invariant. Invariant applies to the type, not the name.

the coffee shop liked it at the first sight. The girl at the counter asked for kris' email :o). Yes, "invariant" is essentially a synonym to "constant", but here are two simple mnemonic devices to remember which is which: a) "const" is shorter because it's used more often; b) "const" is similar to C++'s homonym. Easier to remember than perl's $|. Andrei

Another option I'd like to have at least considered is the finding of a character that can be used to denote const. Adding various constnesses to all the code could easily add a whole lot of typing to the task of getting things done in D. Could we find a unicode character that wouldn't be too much trouble for editors to insert ? Might mean that D starts to require unicode source - but is that such a bad thing ? Are there any ascii characters that can be found that wont confuse the parser ? - like how the use of ! works for templates

Type inference will reduce the need for littering code with "const" a great deal (when compared to today's C++ code, at least). You just write: auto var = expression; and the const will propagate. Initial experience will show whether this is enough.

What about the ability to deduce only parts of a type? Declaring types is also a kind of documentation / assertion that can help maintainers of the code later. I might want to specify const and let the type be deduced, or I might want to specify the type I'm expecting and let const be deduced. Maybe declaration followed by is-test will be necessary for those cases. If they aren't too common I guess that's ok. But I would hate to see auto var = expression; assert(is(non_const_typeof(var)==Something)); become a standard idiom. ... how will one do that is() check for base type sans type constructors? --bb
Mar 18 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Alex Burton wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:

 Walter Bright wrote:
 Derek Parnell wrote:

 invariant

application as the declaration from being able to modify the item being declared.

invariant. Invariant applies to the type, not the name.

in the coffee shop liked it at the first sight. The girl at the counter asked for kris' email :o). Yes, "invariant" is essentially a synonym to "constant", but here are two simple mnemonic devices to remember which is which: a) "const" is shorter because it's used more often; b) "const" is similar to C++'s homonym. Easier to remember than perl's $|. Andrei

Another option I'd like to have at least considered is the finding of a character that can be used to denote const. Adding various constnesses to all the code could easily add a whole lot of typing to the task of getting things done in D. Could we find a unicode character that wouldn't be too much trouble for editors to insert ? Might mean that D starts to require unicode source - but is that such a bad thing ? Are there any ascii characters that can be found that wont confuse the parser ? - like how the use of ! works for templates

Type inference will reduce the need for littering code with "const" a great deal (when compared to today's C++ code, at least). You just write: auto var = expression; and the const will propagate. Initial experience will show whether this is enough.

What about the ability to deduce only parts of a type? Declaring types is also a kind of documentation / assertion that can help maintainers of the code later. I might want to specify const and let the type be deduced, or I might want to specify the type I'm expecting and let const be deduced. Maybe declaration followed by is-test will be necessary for those cases. If they aren't too common I guess that's ok. But I would hate to see auto var = expression; assert(is(non_const_typeof(var)==Something)); become a standard idiom. ... how will one do that is() check for base type sans type constructors?

assert(is(const typeof(var) == typeof(var))); Only time will show whether this will be needed frequently. I don't think so: in general var will engender an error upon first mutation, which is close to its definition. Andrei
Mar 18 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Alex Burton wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:

 Walter Bright wrote:
 Derek Parnell wrote:

 invariant

application as the declaration from being able to modify the item being declared.

invariant. Invariant applies to the type, not the name.

in the coffee shop liked it at the first sight. The girl at the counter asked for kris' email :o). Yes, "invariant" is essentially a synonym to "constant", but here are two simple mnemonic devices to remember which is which: a) "const" is shorter because it's used more often; b) "const" is similar to C++'s homonym. Easier to remember than perl's $|. Andrei

Another option I'd like to have at least considered is the finding of a character that can be used to denote const. Adding various constnesses to all the code could easily add a whole lot of typing to the task of getting things done in D. Could we find a unicode character that wouldn't be too much trouble for editors to insert ? Might mean that D starts to require unicode source - but is that such a bad thing ? Are there any ascii characters that can be found that wont confuse the parser ? - like how the use of ! works for templates

Type inference will reduce the need for littering code with "const" a great deal (when compared to today's C++ code, at least). You just write: auto var = expression; and the const will propagate. Initial experience will show whether this is enough.

What about the ability to deduce only parts of a type? Declaring types is also a kind of documentation / assertion that can help maintainers of the code later. I might want to specify const and let the type be deduced, or I might want to specify the type I'm expecting and let const be deduced. Maybe declaration followed by is-test will be necessary for those cases. If they aren't too common I guess that's ok. But I would hate to see auto var = expression; assert(is(non_const_typeof(var)==Something)); become a standard idiom. ... how will one do that is() check for base type sans type constructors?

assert(is(const typeof(var) == typeof(var))); Only time will show whether this will be needed frequently. I don't think so: in general var will engender an error upon first mutation, which is close to its definition. Andrei

Why not just const auto var = expression; ? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 19 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Sat, 17 Mar 2007 21:33:16 -0700, Walter Bright wrote:

 Derek Parnell wrote:
 final

done by either the compiler or at run time depending on the amount of knowledge the compiler has about the location's usage.

No. This applies to rebinding of a name: final x = 3; x = 4; // error, x is final i.e. final applies to the declared name, not the type.
 const

declaration from being able to modify the item being declared.

No. This means that it is a readonly view of data - other views of the same data may change it. char[] s = "hello".dup; const char[] t = s; t[0] = 'j'; // error, const char[] is a readonly view s[0] = 'j'; // ok writefln(t); // prints "jello" t = s[1..2]; // ok, as t is not final Note that const applies to the type, not the name.
 invariant

the declaration from being able to modify the item being declared.

Almost right. It isn't the declaration, but the *type* that is invariant. Invariant applies to the type, not the name.
 As you can see, I'm confused as to how the qualifier effects which code is
 allowed to change which items. Even more so when it comes to reference
 items ... 'cos I'm not sure how to use these qualifiers to specify whether
 the reference and/or the data being referenced can be changed, and by whom.

'final' is a storage class, like 'static'. It doesn't apply to the type of the symbol, only the symbol itself. 'const' and 'invariant' apply to the type of the symbol, not the symbol.

I'm sure its just a terminology problem I'm having, but I still can't understand what you are trying to tell me. I'm sorry I'm so thick! To me the word 'type' refers to the data type associated with a symbol - like 'int', 'float', 'char[]' , etc... So when you say "'const' and 'invariant' apply to the type" it sounds like you are saying that 'const' prevents a symbol from changing types - which doesn't make sense to me. In D, a symbol can never change types once it has been declared. So, I'm now wondering if by "type" you are actually meaning the data represented by the symbol and not its data-type. However that doesn't quite make sense either because that's what I was saying but you disagreed with me!? Please bear with me. Oh, and anyone else can chime in to help make it clear to me too, please. Let's take 'final' again. Walter said:
 No. This applies to rebinding of a name:
 	final x = 3;
 	x = 4;		// error, x is final
 i.e. final applies to the declared name, not the type.

Now by 'binding' I assume you mean 'having the compiler associate a value with a symbol'. So "final x = 3" is sort of equivalent to C's "#define x (3)", but in D there are scope and data-type implications. I expect that in D we will also be able to do ... final double z = 4.288392; but what about things like ... int y = 3; final x = y; final q = someFunc(); And is ... final s = "qwerty"; char[] t = s; identical to ... char[] t = "qwerty".dup; I presume this would fail at compile time ... void XYZZY(inout char[] x) { . . . } . . . final s = "qwerty"; XYZZY(s); ***** Okay, now let's revisit 'const'. I said ...
  This is applied to declarations to prevent code in the same scope as the
 declaration from being able to modify the item being declared.


And you said ...
 No. This means that it is a readonly view of data - other views of the 
 same data may change it.
 	char[] s = "hello".dup;
 	const char[] t = s;
 	t[0] = 'j';	// error, const char[] is a readonly view
 	s[0] = 'j';	// ok
 	writefln(t);	// prints "jello"
 	t = s[1..2];	// ok, as t is not final
 Note that const applies to the type, not the name.

Apart from the last sentence, which still confuses the hell out of me, I think we almost are saying the same thing. Using your example code, I was saying that the declaration "const char[] t" means that you can't change 't' (i.e. t.ptr and t.length cannot be altered) but you can change the data that t.ptr points to. However, I see by your explanation that I got this the wrong way around. I now read you saying that 'const char[] t' means that program can change t.ptr and/or t.length but it cannot use 't' to change the data it points to. I said nothing about getting to the data via another symbol. Also, I see that you are saying "final const char[] t" means that neither 't' nor its data can be altered. Is there any difference between "final const char[] t" and "const final char[] t"? Is "const int x = 4" and "final int x = 4" mean the same thing (not a reference type this time)? Would "final const x = 5" compile? Would it mean anything different from just 'final' or just 'const'? It would seem that 'const' is primarily used to protect referenced data rather than the reference itself. Is that right? Will this be allowed ... void XYZZY(const char[] s) { char[] t; t = s; // fails here? t[0] = 'a'; } ***** And now to 'invariant' ... You said ...
 It isn't the declaration, but the *type* that is 
 invariant. Invariant applies to the type, not the name.

I have no idea what you mean by that statement! I still can't see that data-types can change. But maybe you mean data rather than data-type, but then isn't that what 'const' is doing? And I'm sure you *do not* mean that somehow you can rename symbols during compilation!? So I can't see what is "invariant" - as in - what is "not changing". If I code "invariant char[] t", ... (A) are changes to t.ptr/t.length prevented? (B) are changes to the data that 't' points to prevented? How is 'invariant' different from 'final'? How is 'invariant' different from 'const'? How is 'invariant' different from 'final const'? Is combining 'invariant' with 'final' and/or 'const' meaningful? If so, how? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 19/03/2007 11:55:33 AM
Mar 18 2007
next sibling parent reply James Dennett <jdennett acm.org> writes:
Derek Parnell wrote:
 On Sat, 17 Mar 2007 21:33:16 -0700, Walter Bright wrote:
 
 Derek Parnell wrote:
 final

done by either the compiler or at run time depending on the amount of knowledge the compiler has about the location's usage.

final x = 3; x = 4; // error, x is final i.e. final applies to the declared name, not the type.
 const

declaration from being able to modify the item being declared.

same data may change it. char[] s = "hello".dup; const char[] t = s; t[0] = 'j'; // error, const char[] is a readonly view s[0] = 'j'; // ok writefln(t); // prints "jello" t = s[1..2]; // ok, as t is not final Note that const applies to the type, not the name.
 invariant

the declaration from being able to modify the item being declared.

invariant. Invariant applies to the type, not the name.
 As you can see, I'm confused as to how the qualifier effects which code is
 allowed to change which items. Even more so when it comes to reference
 items ... 'cos I'm not sure how to use these qualifiers to specify whether
 the reference and/or the data being referenced can be changed, and by whom.

of the symbol, only the symbol itself. 'const' and 'invariant' apply to the type of the symbol, not the symbol.

I'm sure its just a terminology problem I'm having, but I still can't understand what you are trying to tell me. I'm sorry I'm so thick! To me the word 'type' refers to the data type associated with a symbol - like 'int', 'float', 'char[]' , etc... So when you say "'const' and 'invariant' apply to the type" it sounds like you are saying that 'const' prevents a symbol from changing types - which doesn't make sense to me. In D, a symbol can never change types once it has been declared.

It's just saying that "T" and "const T" are different types (unless T was already const); they have different permitted operations. I'd have to say that I find Walter's ideas on const/final to be rather... distinctive... and we've debated terminology at some length but with no resolution. It's his language, he can use terms as he wishes. It may impede communication. This may be a case of "final" being a thing that Walter likes to think of as a storage class rather than a type modifier (though in a more general sense storage classes are just a special/restricted case of type modifiers). "final", I think, is a workaround for handling types that are accessed through automatically-dereferenced GC'd pointers in D. The auto-deref property hides the actual type of those pointers from us, so we can't const- qualify them; we just end up adding const to the type to which they point. C++ doesn't work this way, and so const does everything that final does and more in C++. It's just different. If you want indirection via a reference/pointer, C++ makes you say so, but gives you the chance to apply type modifiers to that pointer. D does it invisibly, but then requires extra syntax to be able to stop the name being rebindable. -- James
Mar 18 2007
next sibling parent Derek Parnell <derek nomail.afraid.org> writes:
On Sun, 18 Mar 2007 20:46:58 -0700, James Dennett wrote:

Walter says 
 "'const' and 'invariant' apply to the type"


 It's just saying that "T" and "const T" are different types
 (unless T was already const); they have different permitted
 operations.

Oh?! Is that all. That is easy to understand, but it does not help me understand how Walter sees it or 'invariant' being used. So "int", "const int" and "invariant int" are all different types and as such have in-built rules concerning implicit conversions etc...That's fine. I can understand that. But what then are the built-rules? Are the rules different for POD, Objects, and Arrays?
 I'd have to say that I find Walter's ideas on const/final
 to be rather... distinctive... and we've debated terminology
 at some length but with no resolution.  It's his language,
 he can use terms as he wishes.  It may impede communication.

I'm fine with that too. Sure, its Walter (and Andrei's) language and that can make up syntax and keywords as they see fit. I only hope that they get some good documentation experts to help people like me understand it.
 This may be a case of "final" being a thing that Walter likes
 to think of as a storage class rather than a type modifier
 (though in a more general sense storage classes are just a
 special/restricted case of type modifiers).
 
 "final", I think, is a workaround for handling types
 that are accessed through automatically-dereferenced
 GC'd pointers in D.  The auto-deref property hides the
 actual type of those pointers from us, so we can't const-
 qualify them; we just end up adding const to the type to
 which they point.  C++ doesn't work this way, and so
 const does everything that final does and more in C++.
 It's just different.  If you want indirection via a
 reference/pointer, C++ makes you say so, but gives you
 the chance to apply type modifiers to that pointer.  D
 does it invisibly, but then requires extra syntax to be
 able to stop the name being rebindable.

Interesting ... but what does all that sound like in English <G> I don't really know C++ all that well so I don't quite get the comparisons. Can anyone explain this without referring to other programming languages, please? Just common everyday English would be nice - oh, but with example <G>. But let's see if I can translate it first ... "storage class" means how something's value is stored in RAM. Which could be in the data space or as immediate literals in the code space. Thus 'final' is a description of how the compiler sees the value stored in the program. One rule is that it is impossible for any code to alter the value associated (bound) a 'final' symbol. For all practical purposes, from the compiler's point of view, the symbol *is* the value. When applied to reference types, the symbol's value is the reference and not that which is being referenced. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 19/03/2007 3:24:59 PM
Mar 18 2007
prev sibling parent reply jovo <jovo at.home> writes:
James Dennett Wrote:
 
 "final", I think, is a workaround for handling types
 that are accessed through automatically-dereferenced
 GC'd pointers in D.  The auto-deref property hides the
 actual type of those pointers from us, so we can't const-
 qualify them; we just end up adding const to the type to
 which they point.  C++ doesn't work this way, and so
 const does everything that final does and more in C++.
 It's just different.  If you want indirection via a
 reference/pointer, C++ makes you say so, but gives you
 the chance to apply type modifiers to that pointer.  D
 does it invisibly, but then requires extra syntax to be
 able to stop the name being rebindable.
 

But I think it is'nt key point here. As I understand, const will afect all from the specified stage in the reference chain, unlike C++ const? Code analogous to this legal C++ code will be compile time error? struct S { int *x; }; int a = 12; S s = { &a }; const S *p = &s; *p->x = 20; // will be error? jovo
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
jovo wrote:
 James Dennett Wrote:
 "final", I think, is a workaround for handling types
 that are accessed through automatically-dereferenced
 GC'd pointers in D.  The auto-deref property hides the
 actual type of those pointers from us, so we can't const-
 qualify them; we just end up adding const to the type to
 which they point.  C++ doesn't work this way, and so
 const does everything that final does and more in C++.
 It's just different.  If you want indirection via a
 reference/pointer, C++ makes you say so, but gives you
 the chance to apply type modifiers to that pointer.  D
 does it invisibly, but then requires extra syntax to be
 able to stop the name being rebindable.

But I think it is'nt key point here. As I understand, const will afect all from the specified stage in the reference chain, unlike C++ const? Code analogous to this legal C++ code will be compile time error? struct S { int *x; }; int a = 12; S s = { &a }; const S *p = &s; *p->x = 20; // will be error?

Yes. D's const is transitive. Andrei
Mar 19 2007
parent reply jovo <jovo at.home> writes:
Andrei Alexandrescu (See Website For Email) Wrote:
 
 Yes. D's const is transitive.
 

So: finall = stage0_nontransitive_const? const = stage1_transitive_const? May be beter to separete concepts? Do we need stage1_const? Do we need nontransitive_const? jovo
Mar 19 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
jovo wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:
 Yes. D's const is transitive.

So: finall = stage0_nontransitive_const? const = stage1_transitive_const?

If I understand the above properly, yes.
 May be beter to separete concepts?
 
 Do we need stage1_const?
 Do we need nontransitive_const?

So rarely that we decided they can be ruled out. They'd complicate the type system gravely, for very little benefit. Andrei
Mar 19 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Derek Parnell wrote:
 On Sat, 17 Mar 2007 21:33:16 -0700, Walter Bright wrote:
 
 Derek Parnell wrote:
 final

done by either the compiler or at run time depending on the amount of knowledge the compiler has about the location's usage.

final x = 3; x = 4; // error, x is final i.e. final applies to the declared name, not the type.
 const

declaration from being able to modify the item being declared.

same data may change it. char[] s = "hello".dup; const char[] t = s; t[0] = 'j'; // error, const char[] is a readonly view s[0] = 'j'; // ok writefln(t); // prints "jello" t = s[1..2]; // ok, as t is not final Note that const applies to the type, not the name.
 invariant

the declaration from being able to modify the item being declared.

invariant. Invariant applies to the type, not the name.
 As you can see, I'm confused as to how the qualifier effects which code is
 allowed to change which items. Even more so when it comes to reference
 items ... 'cos I'm not sure how to use these qualifiers to specify whether
 the reference and/or the data being referenced can be changed, and by whom.

of the symbol, only the symbol itself. 'const' and 'invariant' apply to the type of the symbol, not the symbol.

I'm sure its just a terminology problem I'm having, but I still can't understand what you are trying to tell me. I'm sorry I'm so thick!

It's likely the following: By "It doesn't apply to the type of the symbol, only the symbol itself." it means it applies to the immediate-value, and by "apply to the type of the symbol, not the symbol" it means it applies to the referenced-data, where: "immediate-value" means the value that is changed on assignments, which in primitives is the primitive value, and on references is the pointer (memory location). "referenced-data" is the data obtained through references (i.e., data pointed to) IMO I think that terminology is faulty, since, for starters, one can have an expression that is not assignable (like the return value of functions), which is the same as being 'final', yet there is no associated symbol. The same applies to saying 'final' "applies to rebinding of a name", like in the example below:
 
 Walter said:
 No. This applies to rebinding of a name:
 	final x = 3;
 	x = 4;		// error, x is final
 i.e. final applies to the declared name, not the type.

Now by 'binding' I assume you mean 'having the compiler associate a value with a symbol'. So "final x = 3" is sort of equivalent to C's "#define x (3)", but in D there are scope and data-type implications.

The differences are that the 'final' variable exists at runtime (unlike #define) and as such the initializer doesn't have to be a constant, and the var can be referenced.
 I expect that in D we will also be able to do ...
 
   final double z = 4.288392;
 
 but what about things like ...
 
   int y = 3;
   final x = y;
   final q = someFunc();
 

Yes.
 And is ...
 
     final s = "qwerty";
     char[] t = s;
 
 identical to ...
 
     char[] t = "qwerty".dup;
 

No, I don't think so, why would it be?
 
 I presume this would fail at compile time ...
 
     void XYZZY(inout char[] x) { . . . }
     . . .
     final s = "qwerty";
     XYZZY(s);
 
 

Yup.
 *****
 Okay, now let's revisit 'const'.
 
 I said ...
  This is applied to declarations to prevent code in the same scope as the
 declaration from being able to modify the item being declared.


And you said ...
 No. This means that it is a readonly view of data - other views of the 
 same data may change it.
 	char[] s = "hello".dup;
 	const char[] t = s;
 	t[0] = 'j';	// error, const char[] is a readonly view
 	s[0] = 'j';	// ok
 	writefln(t);	// prints "jello"
 	t = s[1..2];	// ok, as t is not final
 Note that const applies to the type, not the name.

Apart from the last sentence, which still confuses the hell out of me, I think we almost are saying the same thing. Using your example code, I was saying that the declaration "const char[] t" means that you can't change 't' (i.e. t.ptr and t.length cannot be altered) but you can change the data that t.ptr points to.

That's what 'final' does (i.e. "t.ptr and t.length cannot be altered").
However, I see by your
 explanation that I got this the wrong way around. I now read you saying
 that 'const char[] t' means that program can change t.ptr and/or t.length
 but it cannot use 't' to change the data it points to. I said nothing about
 getting to the data via another symbol. Also, I see that you are saying
 "final const char[] t" means that neither 't' nor its data can be altered.
 
 Is there any difference between "final const char[] t" and "const final
 char[] t"?

Nope.
 Is "const int x = 4" and "final int x = 4" mean the same thing (not a
 reference type this time)? 
 

I suspect no, since 'const' for primitives is either ignored or not allowed, but I'm not sure what Andrei is planning it to be.
 Would "final const x = 5" compile? Would it mean anything different from
 just 'final' or just 'const'?
 

Same as above, dunno.
 It would seem that 'const' is primarily used to protect referenced data
 rather than the reference itself. Is that right?
 

Yup.
 And now to 'invariant' ...
 

For what I got 'invariant' means the data doesn't change at all, like compile time constants, or ROM data. But I didn't understand if it applies to the "immediate-value" only (like 'final'), or transitively to the referenced data too (like 'const'). Clarify? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 19 2007
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Bruno Medeiros wrote:
 
 For what I got 'invariant' means the data doesn't change at all, like 
 compile time constants, or ROM data. But I didn't understand if it 
 applies to the "immediate-value" only (like 'final'), or transitively to 
 the referenced data too (like 'const'). Clarify?
 

Its my understanding that 'invariant' becomes part of the type, like 'const'. So in your terms, it applies to "referenced data". invariant ANSWER = 42 ; bool question (invariant int guess) { return guess is ANSWER; } ;) -- Chris Nicholson-Sauls
Mar 19 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
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).
Mar 20 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
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
Mar 20 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 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>

You may want to modernize. "Assign" doesn't quite catch the notion of indirect reference, and from a couple of posts I understand that this is a source of confusion. A very useful way to see "int x = 4;" is that the symbol x is bound to the Platonic number 4. The 4 itself cannot change. You can rebind x by, say, writing ++x. That unbinds x from Plato 4 and binds it to Plato 5. Once this is clear, the notions of values and references clarifies a lot.
 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.

The meaning was simple. When you say "poor fella Jim", "poor" applies to Jim, not to "fella".
 '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

They all fail. The last fails because invariance is transitive. All that could be done is to rebind f to another invariant object. Andrei
Mar 20 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 20 Mar 2007 17:24:49 -0700, Andrei Alexandrescu (See Website For
Email) wrote:

 Derek Parnell wrote:
 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>

You may want to modernize. "Assign" doesn't quite catch the notion of indirect reference,

Okay, if you say so, but I'm not having an issue with the concept of assigning value to something directly or indirectly.
 and from a couple of posts I understand that this is 
 a source of confusion.

Not for me, but thanks anyway.
 A very useful way to see "int x = 4;" is that the symbol x is bound to 
 the Platonic number 4. The 4 itself cannot change. You can rebind x by, 
 say, writing ++x. That unbinds x from Plato 4 and binds it to Plato 5. 

Huh? Just looks like a fancy way of saying 'x' first gets assigned the value 4 then later, using ++x, gets assigned the value 5. By whatever, I'll not get hung up as I think we are on the same track here.
 Once this is clear, the notions of values and references clarifies a lot.

It does? How? If pressed, I'd say that the only difference between 'assign' and 'bind' is that 'assign' maybe implies an 'assignment statement' was used to change the value, but 'bind' doesn't care how the change is caused.
 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.

The meaning was simple. When you say "poor fella Jim", "poor" applies to Jim, not to "fella".

Oh, okay ... its not a 'static int' (a type) but a 'static x' (a value represented by the symbol 'x').
 '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

They all fail. The last fails because invariance is transitive. All that could be done is to rebind f to another invariant object.

Thanks. I'm just trying to get all this straight in my mind. So the definition should be more along the lines of ... 'invariant' is a guarantee that any data accessible from that type will never change. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 21/03/2007 11:52:49 AM
Mar 20 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Tue, 20 Mar 2007 17:24:49 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 
 Derek Parnell wrote:
 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;

okay or must I modernize <G>

indirect reference,

Okay, if you say so, but I'm not having an issue with the concept of assigning value to something directly or indirectly.
 and from a couple of posts I understand that this is 
 a source of confusion.

Not for me, but thanks anyway.
 A very useful way to see "int x = 4;" is that the symbol x is bound to 
 the Platonic number 4. The 4 itself cannot change. You can rebind x by, 
 say, writing ++x. That unbinds x from Plato 4 and binds it to Plato 5. 

Huh? Just looks like a fancy way of saying 'x' first gets assigned the value 4 then later, using ++x, gets assigned the value 5. By whatever, I'll not get hung up as I think we are on the same track here.
 Once this is clear, the notions of values and references clarifies a lot.

It does? How? If pressed, I'd say that the only difference between 'assign' and 'bind' is that 'assign' maybe implies an 'assignment statement' was used to change the value, but 'bind' doesn't care how the change is caused.

No. The difference is major. The binding to Platonic values clarifies that values are exactly the same thing as references to immutable objects. Once that is understood, it's very easy to understand all of D's immutability minutiae. Andrei
Mar 20 2007
prev sibling next sibling parent jovo <jovo at.home> writes:
Andrei Alexandrescu (See Website For Email) Wrote:
 You may want to modernize. "Assign" doesn't quite catch the notion of 
 indirect reference, and from a couple of posts I understand that this is 
 a source of confusion.
 
 A very useful way to see "int x = 4;" is that the symbol x is bound to 
 the Platonic number 4. The 4 itself cannot change. You can rebind x by, 
 say, writing ++x. That unbinds x from Plato 4 and binds it to Plato 5. 
 Once this is clear, the notions of values and references clarifies a lot.
 

Actually, when I use this term: struct S { int a; int* b; } const S s; s.a = 1; // not allowed s.b = some_address; // not allowed *s.b = 1; // not allowed s = some_S; // allowed don't look alogical, and what is more important, produces, I think at first look, better result. jovo
Mar 20 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Derek Parnell wrote:
 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>

You may want to modernize. "Assign" doesn't quite catch the notion of indirect reference, and from a couple of posts I understand that this is a source of confusion.

Huh, " "Assign" doesn't quite catch the notion of indirect reference", what do you mean by that? I too was thinking that "assign" is a much better term than "binding".
 A very useful way to see "int x = 4;" is that the symbol x is bound to 
 the Platonic number 4. The 4 itself cannot change. You can rebind x by, 
 say, writing ++x. That unbinds x from Plato 4 and binds it to Plato 5. 
 Once this is clear, the notions of values and references clarifies a lot.
 

Dear Gods. Yes, that conceptualization is correct, but again, how is it any better than "assign"/"assignability"? A very useful way to see "int x = 4;" is that the symbol x is assigned to the value 4. You can reassign x by writing ++x. That assigns x to the value x+1. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Derek Parnell wrote:
 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>

You may want to modernize. "Assign" doesn't quite catch the notion of indirect reference, and from a couple of posts I understand that this is a source of confusion.

Huh, " "Assign" doesn't quite catch the notion of indirect reference", what do you mean by that? I too was thinking that "assign" is a much better term than "binding".
 A very useful way to see "int x = 4;" is that the symbol x is bound to 
 the Platonic number 4. The 4 itself cannot change. You can rebind x 
 by, say, writing ++x. That unbinds x from Plato 4 and binds it to 
 Plato 5. Once this is clear, the notions of values and references 
 clarifies a lot.

Dear Gods. Yes, that conceptualization is correct, but again, how is it any better than "assign"/"assignability"? A very useful way to see "int x = 4;" is that the symbol x is assigned to the value 4. You can reassign x by writing ++x. That assigns x to the value x+1.

That is what really happens, but won't help understanding references. So it is not useful for that purpose. Andrei
Mar 23 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Derek Parnell wrote:
 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>

You may want to modernize. "Assign" doesn't quite catch the notion of indirect reference, and from a couple of posts I understand that this is a source of confusion.

Huh, " "Assign" doesn't quite catch the notion of indirect reference", what do you mean by that? I too was thinking that "assign" is a much better term than "binding".
 A very useful way to see "int x = 4;" is that the symbol x is bound 
 to the Platonic number 4. The 4 itself cannot change. You can rebind 
 x by, say, writing ++x. That unbinds x from Plato 4 and binds it to 
 Plato 5. Once this is clear, the notions of values and references 
 clarifies a lot.

Dear Gods. Yes, that conceptualization is correct, but again, how is it any better than "assign"/"assignability"? A very useful way to see "int x = 4;" is that the symbol x is assigned to the value 4. You can reassign x by writing ++x. That assigns x to the value x+1.

That is what really happens, but won't help understanding references. So it is not useful for that purpose. Andrei

Yes, that you've said already, what I would like to know is why. :P What's the issue with assign and references? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 23 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Derek Parnell wrote:
 '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;

Since the above case wasn't answered, I will. The "Foo g = f;" statement isn't allowed at all, because you are casting a const (think readonly) reference to a non-const reference. That isn't allowed, by design, since the very purpose of const (think readonly), is to disallow the holder of that reference to do any changes on the reference data. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 23 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 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).

So 'final' will be a storage class and not a type modifier? That's one of the questions I still have with regards to your design, because I'm not seeing how 'final' can also not-be a type modifier. In particular, again, what is typeof(&foo) where foo is: final Foo foo; ? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 23 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Bruno Medeiros wrote:
 Walter Bright wrote:
 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).

So 'final' will be a storage class and not a type modifier? That's one of the questions I still have with regards to your design, because I'm not seeing how 'final' can also not-be a type modifier. In particular, again, what is typeof(&foo) where foo is: final Foo foo; ?

I imagine it would be (invariant Foo*): an invariant pointer to a reference to a Foo. Since *no one* is ever allowed to change final storage, then a pointer to it must be invariant. -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 23 2007
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Daniel Keep wrote:
 
 Bruno Medeiros wrote:
 Walter Bright wrote:
 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).

of the questions I still have with regards to your design, because I'm not seeing how 'final' can also not-be a type modifier. In particular, again, what is typeof(&foo) where foo is: final Foo foo; ?

I imagine it would be (invariant Foo*): an invariant pointer to a reference to a Foo. Since *no one* is ever allowed to change final storage, then a pointer to it must be invariant. -- Daniel

No, that's not possible. It was mentioned before in another post: news://news.digitalmars.com:119/etv81l$m94$1 digitalmars.com Since invariant is (planned to be) transitive, then that typeof can't be (invariant Foo*), since the contents of foo can change (altough the immediate-value won't). -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 24 2007
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Bruno Medeiros wrote:
 Daniel Keep wrote:
 Bruno Medeiros wrote:
 Walter Bright wrote:
 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).

of the questions I still have with regards to your design, because I'm not seeing how 'final' can also not-be a type modifier. In particular, again, what is typeof(&foo) where foo is: final Foo foo; ?

I imagine it would be (invariant Foo*): an invariant pointer to a reference to a Foo. Since *no one* is ever allowed to change final storage, then a pointer to it must be invariant. -- Daniel

No, that's not possible. It was mentioned before in another post: news://news.digitalmars.com:119/etv81l$m94$1 digitalmars.com Since invariant is (planned to be) transitive, then that typeof can't be (invariant Foo*), since the contents of foo can change (altough the immediate-value won't).

You are, of course, right. Damnit. Just when I thought I understood all this... :( -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 24 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 So, "invariant" is already a keyword ... what about that?

as a candidate for a qualifier name. Thank you.

I agree. I think: final const invariant for the three cases looks pretty good.

Assuming that 'invariant' = really constant, 'const' = C++ pseudo-const... It's better than super const. However: (1) I would want to go through all my existing D code and change 100% of my usages of 'const' to 'invariant'. (2) although (1) could be avoided with the rule that 'const' on a declaration implicitly means 'invariant', this would then mean that to match a 'const' value, you use 'invariant'; but to match a non-const value, you use 'const'. That's horribly confusing. (3) I concede the problem of association with 'readonly' and ROM. But the association between 'const' and 'constant' is pretty strong. The problem remains that in: const int a = 2; void f(const int b) { } a really is a constant, but there is nothing constant about 'b'! How about 'protected'? It seems to cover the 'don't touch' meaning pretty well...
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Don Clugston wrote:
 Assuming that 'invariant' = really constant, 'const' = C++ pseudo-const...

Yah.
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% of 
 my usages of 'const' to 'invariant'.

Great to hear that!
 (2) although (1) could be avoided with the rule that 'const' on a 
 declaration implicitly means 'invariant', this would then mean that to 
 match a 'const' value, you use 'invariant'; but to match a non-const 
 value, you use 'const'. That's horribly confusing.

This I don't understand. Could you give an example?
 (3) I concede the problem of association with 'readonly' and ROM. But 
 the association between 'const' and 'constant' is pretty strong.

I think there's universal agreement that you can't express tersely the two notions "const-as-in-C++" and "const-as-in-we-really-mean-it". Probably it's best to just go with two terms and move on.
 The problem remains that in:
 
 const int a = 2;
 
 void f(const int b)
 {
 
 }
 
 a really is a constant, but there is nothing constant about 'b'!

The code is incorrect. Const and invariant refer to indirectly-addressed memory. Probably you mean: final int a = 2; void f(final int b) { } The choice of "final" in the first case makes "a" bound to "2" for eternity. 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. The two uses of "final" are reasonably consistent with one another.
 How about 'protected'?
 It seems to cover the 'don't touch' meaning pretty well...

class Foo { protected: protected char[] find(protected char[] haystack, protected char[] needle); } 'Nuff said :o). Andrei
Mar 19 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% 
 of my usages of 'const' to 'invariant'.

Great to hear that!

I think one of us misunderstood. I parsed that sentence as "I'd have to go through all my D code to keep it working properly", while judging by your reaction you seem to have parsed it as "I'd be happy to go through all my D code and make the necessary changes". (Note that the connotation(?) is a bit different; neutral/slightly negative vs quite positive)
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% 
 of my usages of 'const' to 'invariant'.

Great to hear that!

I think one of us misunderstood. I parsed that sentence as "I'd have to go through all my D code to keep it working properly", while judging by your reaction you seem to have parsed it as "I'd be happy to go through all my D code and make the necessary changes". (Note that the connotation(?) is a bit different; neutral/slightly negative vs quite positive)

Well, I looked up "want" in my dictionary and parsed the sentence accordingly :o). Andrei
Mar 19 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 
 100% of my usages of 'const' to 'invariant'.

Great to hear that!

I think one of us misunderstood. I parsed that sentence as "I'd have to go through all my D code to keep it working properly", while judging by your reaction you seem to have parsed it as "I'd be happy to go through all my D code and make the necessary changes". (Note that the connotation(?) is a bit different; neutral/slightly negative vs quite positive)

Well, I looked up "want" in my dictionary and parsed the sentence accordingly :o).

Did you look up "would" as well? :P I read that to mean "if this was changed ..."
Mar 19 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 Assuming that 'invariant' = really constant, 'const' = C++ 
 pseudo-const...

Yah.
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% 
 of my usages of 'const' to 'invariant'.

Great to hear that!

Great perhaps, but it does suggest that 'const' should perhaps be the signifier for what 'invariant' has been proposed for (the literal meaning of each term notwithstanding)?
 (2) although (1) could be avoided with the rule that 'const' on a 
 declaration implicitly means 'invariant', this would then mean that to 
 match a 'const' value, you use 'invariant'; but to match a non-const 
 value, you use 'const'. That's horribly confusing.

This I don't understand. Could you give an example?

invariant int* x; int* y; void fn( const int* z ) {} fn( x ); fn( y ); Assuming the above is legal, 'const' is the qualifier which binds to all reference types, ie. "to match a non-const value, you use 'const'."
 (3) I concede the problem of association with 'readonly' and ROM. But 
 the association between 'const' and 'constant' is pretty strong.

I think there's universal agreement that you can't express tersely the two notions "const-as-in-C++" and "const-as-in-we-really-mean-it". Probably it's best to just go with two terms and move on.
 The problem remains that in:

 const int a = 2;

 void f(const int b)
 {

 }

 a really is a constant, but there is nothing constant about 'b'!

The code is incorrect. Const and invariant refer to indirectly-addressed memory. Probably you mean: final int a = 2; void f(final int b) { } The choice of "final" in the first case makes "a" bound to "2" for eternity. 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. The two uses of "final" are reasonably consistent with one another.
 How about 'protected'?
 It seems to cover the 'don't touch' meaning pretty well...

class Foo { protected: protected char[] find(protected char[] haystack, protected char[] needle); } 'Nuff said :o).

It's too bad too, since this word makes the most semantic sense :-) Sean
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 Assuming that 'invariant' = really constant, 'const' = C++ 
 pseudo-const...

Yah.
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% 
 of my usages of 'const' to 'invariant'.

Great to hear that!

Great perhaps, but it does suggest that 'const' should perhaps be the signifier for what 'invariant' has been proposed for (the literal meaning of each term notwithstanding)?

No. There will be far more uses for const than for invariant. You can take my word for that.
 (2) although (1) could be avoided with the rule that 'const' on a 
 declaration implicitly means 'invariant', this would then mean that 
 to match a 'const' value, you use 'invariant'; but to match a 
 non-const value, you use 'const'. That's horribly confusing.

This I don't understand. Could you give an example?

invariant int* x; int* y; void fn( const int* z ) {} fn( x ); fn( y ); Assuming the above is legal, 'const' is the qualifier which binds to all reference types, ie. "to match a non-const value, you use 'const'."

That is correct. Andrei
Mar 19 2007
parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:

 Don Clugston wrote:

 Assuming that 'invariant' = really constant, 'const' = C++ 
 pseudo-const...

Yah.
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 
 100% of my usages of 'const' to 'invariant'.

Great to hear that!

Great perhaps, but it does suggest that 'const' should perhaps be the signifier for what 'invariant' has been proposed for (the literal meaning of each term notwithstanding)?

No. There will be far more uses for const than for invariant. You can take my word for that.
 (2) although (1) could be avoided with the rule that 'const' on a 
 declaration implicitly means 'invariant', this would then mean that 
 to match a 'const' value, you use 'invariant'; but to match a 
 non-const value, you use 'const'. That's horribly confusing.

This I don't understand. Could you give an example?

invariant int* x; int* y; void fn( const int* z ) {} fn( x ); fn( y ); Assuming the above is legal, 'const' is the qualifier which binds to all reference types, ie. "to match a non-const value, you use 'const'."

That is correct.

How about some further example? Given these sigs: struct Foo { int a; } class Bar { char[] b; } void fooish (inout Foo foo) {} char[] barish (Bar bar) {return bar.b;} 1) how do I modify the function decls to have the compiler *enforce* readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be mutated within the function body? 2) how does the fooish() decl change to have the compiler *enforce* a readonly-view of the returned bar.b? 3) where fooish() has a Foo* rather than an inout Foo, is there any change to the modified decl vis-a-vis #1? - Kris
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
(Reposted after cleanup :o))

kris wrote:
 How about some further example? Given these sigs:
 
 struct Foo
 {
    int a;
 }
 
 class Bar
 {
    char[] b;
 }
 
 void fooish (inout Foo foo) {}
 
 char[] barish (Bar bar) {return bar.b;}
 
 
 1) how do I modify the function decls to have the compiler *enforce* 
 readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be 
 mutated within the function body?

void fooish (const ref Foo foo) {} char[] barish (const Bar bar) {return bar.b;}
 2) how does the fooish() decl change to have the compiler *enforce* a 
 readonly-view of the returned bar.b?

const char[] barish (const Bar bar) {return bar.b;}
 3) where fooish() has a Foo* rather than an inout Foo, is there any 
 change to the modified decl vis-a-vis #1?

void fooish (const Foo* foo) {} By the way, a couple of days ago I've had an idea. Many people here ask for a "string" alias because char[] is a tad hard to type and uneasy on the eyes. Probably a good candidate would be: alias invariant char[] string; The resulting type is pretty damn slick: 1. You can alias it and slice it til you get blue in the face - no confusion ever, because nobody can overwrite the contents underneath your alias. 2. The EnsureCString(ref string) function will reallocate and copy the string at most once and only for a slice, never for the "original" string which was zero-terminated during construction. 3. The ~= operator works (somewhat surprisingly). 4. It's very, very rare that you want to modify some random character in a string, and when you do, use a char[] and then copy it back into a string, or rebuild the string from slices! 5. The Java experience (where String is immutable and StringBuffer is the mutable, build-me-piecemeal object) turned out to be largely successful, modulo syntax details that are not so elegant. 6. It meshes great with literals, which are exactly invariant arrays of characters: string hi = "Hello!"; // no dup needed! And it's 100% safe! So it looks like we have solved the string issue in an unexpectedly elegant way - by simply combining two features: invariant and array. Andrei
Mar 19 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 
 By the way, a couple of days ago I've had an idea. Many people here ask
 for a "string" alias because char[] is a tad hard to type and uneasy on
 the eyes. Probably a good candidate would be:
 
 alias invariant char[] string;
 
 The resulting type is pretty damn slick:

 3. The ~= operator works (somewhat surprisingly).

I assume this is because a char[] is actually a struct containing a pointer, so 'invariant' allows that pointer to be reassigned--it simply protects the referenced string? This is the sticking point for me. Assume we have this: struct Foo { int x; int* y; } const Foo foo; // is this legal? foo.x = 1; // this is allowed *foo.y = 2; // this is not allowed Is that correct, or do 'const' and 'invariant' truly only apply to pure reference types?
 4. It's very, very rare that you want to modify some random character in
 a string, and when you do, use a char[] and then copy it back into a
 string, or rebuild the string from slices!

This may be true for a string, but for arrays in general I think this is fairly common.
 5. The Java experience (where String is immutable and StringBuffer is
 the mutable, build-me-piecemeal object) turned out to be largely
 successful, modulo syntax details that are not so elegant.
 
 6. It meshes great with literals, which are exactly invariant arrays of
 characters:
 
 string hi = "Hello!"; // no dup needed! And it's 100% safe!
 
 So it looks like we have solved the string issue in an unexpectedly
 elegant way - by simply combining two features: invariant and array.

Agreed. This is pretty nice :-) Sean
Mar 19 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 By the way, a couple of days ago I've had an idea. Many people here ask
 for a "string" alias because char[] is a tad hard to type and uneasy on
 the eyes. Probably a good candidate would be:

 alias invariant char[] string;

 The resulting type is pretty damn slick:

 3. The ~= operator works (somewhat surprisingly).

I assume this is because a char[] is actually a struct containing a pointer, so 'invariant' allows that pointer to be reassigned--it simply protects the referenced string? This is the sticking point for me.

That is correct. For an understanding of a ~= b, think of it as a = a ~ b. Do I modify the stuff indirectly referenced by "a"? No, Sir. I just rebind a to a larger entity that embeds a copy of its former self. In practice, the concatenation will not always produce a copy. The implementation will work "as if" you always produce a copy.
 Assume we have this:
 
     struct Foo
     {
         int  x;
         int* y;
     }
 
     const Foo foo; // is this legal?

Yes.
     foo.x  = 1;    // this is allowed

Yes.
     *foo.y = 2;    // this is not allowed

It's not allowed.
 Is that correct, or do 'const' and 'invariant' truly only apply to pure 
 reference types?

No. They apply to any type that embeds references. "final" applies to all types and guards the bits of the symbol itself. "final const" guards symbol bits + referenced bits, and "final invariant" is as opposed to change as the 19th century British education system. (Did I get that right?)
 4. It's very, very rare that you want to modify some random character in
 a string, and when you do, use a char[] and then copy it back into a
 string, or rebuild the string from slices!

This may be true for a string, but for arrays in general I think this is fairly common.

Yes. That's why arrays will stay in place :o). Andrei
Mar 19 2007
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 (Reposted after cleanup :o))
 
 kris wrote:
 How about some further example? Given these sigs:

 struct Foo
 {
    int a;
 }

 class Bar
 {
    char[] b;
 }

 void fooish (inout Foo foo) {}

 char[] barish (Bar bar) {return bar.b;}


 1) how do I modify the function decls to have the compiler *enforce*
 readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be
 mutated within the function body?

void fooish (const ref Foo foo) {} char[] barish (const Bar bar) {return bar.b;}
 2) how does the fooish() decl change to have the compiler *enforce* a
 readonly-view of the returned bar.b?

const char[] barish (const Bar bar) {return bar.b;}

Hang on... wouldn't that need to be the signature of barish in the first place? If const is transitive, bar is declared as a const Bar and you're returning bar.b, isn't bar.b const as well?
 3) where fooish() has a Foo* rather than an inout Foo, is there any
 change to the modified decl vis-a-vis #1?

void fooish (const Foo* foo) {} By the way, a couple of days ago I've had an idea. Many people here ask for a "string" alias because char[] is a tad hard to type and uneasy on the eyes. Probably a good candidate would be: alias invariant char[] string; The resulting type is pretty damn slick: 1. You can alias it and slice it til you get blue in the face - no confusion ever, because nobody can overwrite the contents underneath your alias. 2. The EnsureCString(ref string) function will reallocate and copy the string at most once and only for a slice, never for the "original" string which was zero-terminated during construction. 3. The ~= operator works (somewhat surprisingly). 4. It's very, very rare that you want to modify some random character in a string, and when you do, use a char[] and then copy it back into a string, or rebuild the string from slices! 5. The Java experience (where String is immutable and StringBuffer is the mutable, build-me-piecemeal object) turned out to be largely successful, modulo syntax details that are not so elegant. 6. It meshes great with literals, which are exactly invariant arrays of characters: string hi = "Hello!"; // no dup needed! And it's 100% safe! So it looks like we have solved the string issue in an unexpectedly elegant way - by simply combining two features: invariant and array. Andrei

You're a cruel, cruel man, Andrei. Now we're going to have to wait patiently for this... :'( Seriously, though, this is cool stuff. -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 19 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Daniel Keep wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:
 (Reposted after cleanup :o))

 kris wrote:
 How about some further example? Given these sigs:

 struct Foo
 {
    int a;
 }

 class Bar
 {
    char[] b;
 }

 void fooish (inout Foo foo) {}

 char[] barish (Bar bar) {return bar.b;}


 1) how do I modify the function decls to have the compiler *enforce*
 readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be
 mutated within the function body?

char[] barish (const Bar bar) {return bar.b;}
 2) how does the fooish() decl change to have the compiler *enforce* a
 readonly-view of the returned bar.b?


Hang on... wouldn't that need to be the signature of barish in the first place? If const is transitive, bar is declared as a const Bar and you're returning bar.b, isn't bar.b const as well?

"Nobody said all examples must compile" -- Andrei :o) Andrei
Mar 19 2007
prev sibling next sibling parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 (Reposted after cleanup :o))
 
 kris wrote:
 
 How about some further example? Given these sigs:

 struct Foo
 {
    int a;
 }

 class Bar
 {
    char[] b;
 }

 void fooish (inout Foo foo) {}

 char[] barish (Bar bar) {return bar.b;}


 1) how do I modify the function decls to have the compiler *enforce* 
 readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be 
 mutated within the function body?

void fooish (const ref Foo foo) {} char[] barish (const Bar bar) {return bar.b;}
 2) how does the fooish() decl change to have the compiler *enforce* a 
 readonly-view of the returned bar.b?

const char[] barish (const Bar bar) {return bar.b;}
 3) where fooish() has a Foo* rather than an inout Foo, is there any 
 change to the modified decl vis-a-vis #1?

void fooish (const Foo* foo) {}

Good; thanks. BTW: #1 was a tricky question, as Daniel spotted, since it would seem that the above answer permits an escaping mutable ref. I imagine that's not covered in the *cough* scope of the const domain since it can be sidestepped easily?
 By the way, a couple of days ago I've had an idea. Many people here ask
 for a "string" alias because char[] is a tad hard to type and uneasy on
 the eyes. Probably a good candidate would be:
 
 alias invariant char[] string;
 
 The resulting type is pretty damn slick:
 
 1. You can alias it and slice it til you get blue in the face - no
 confusion ever, because nobody can overwrite the contents underneath
 your alias.
 
 2. The EnsureCString(ref string) function will reallocate and copy the
 string at most once and only for a slice, never for the "original"
 string which was zero-terminated during construction.
 
 3. The ~= operator works (somewhat surprisingly).
 
 4. It's very, very rare that you want to modify some random character in
 a string, and when you do, use a char[] and then copy it back into a
 string, or rebuild the string from slices!
 
 5. The Java experience (where String is immutable and StringBuffer is
 the mutable, build-me-piecemeal object) turned out to be largely
 successful, modulo syntax details that are not so elegant.
 
 6. It meshes great with literals, which are exactly invariant arrays of
 characters:
 
 string hi = "Hello!"; // no dup needed! And it's 100% safe!
 
 So it looks like we have solved the string issue in an unexpectedly
 elegant way - by simply combining two features: invariant and array.

These are some of the reasons why a handful of us have been bitchin' about immutable views for so long. Regarding the alias, you'd likely have to consider w/dchar variations also ... it becomes a bit messy as things progress, which is one of the reasons we never had a string alias in the first place -- at least now there's more weight to the argument for an alias
Mar 19 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 (Reposted after cleanup :o))

 kris wrote:

 How about some further example? Given these sigs:

 struct Foo
 {
    int a;
 }

 class Bar
 {
    char[] b;
 }

 void fooish (inout Foo foo) {}

 char[] barish (Bar bar) {return bar.b;}


 1) how do I modify the function decls to have the compiler *enforce* 
 readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be 
 mutated within the function body?

void fooish (const ref Foo foo) {} char[] barish (const Bar bar) {return bar.b;}
 2) how does the fooish() decl change to have the compiler *enforce* a 
 readonly-view of the returned bar.b?

const char[] barish (const Bar bar) {return bar.b;}
 3) where fooish() has a Foo* rather than an inout Foo, is there any 
 change to the modified decl vis-a-vis #1?

void fooish (const Foo* foo) {}

Good; thanks. BTW: #1 was a tricky question, as Daniel spotted, since it would seem that the above answer permits an escaping mutable ref. I imagine that's not covered in the *cough* scope of the const domain since it can be sidestepped easily?

Nobody said all examples must compile :o). In short, you won't be able to go const -> ~const without a cast, and the cast is never defined to harbor defined behavior.
 By the way, a couple of days ago I've had an idea. Many people here ask
 for a "string" alias because char[] is a tad hard to type and uneasy on
 the eyes. Probably a good candidate would be:

 alias invariant char[] string;

 The resulting type is pretty damn slick:

 1. You can alias it and slice it til you get blue in the face - no
 confusion ever, because nobody can overwrite the contents underneath
 your alias.

 2. The EnsureCString(ref string) function will reallocate and copy the
 string at most once and only for a slice, never for the "original"
 string which was zero-terminated during construction.

 3. The ~= operator works (somewhat surprisingly).

 4. It's very, very rare that you want to modify some random character in
 a string, and when you do, use a char[] and then copy it back into a
 string, or rebuild the string from slices!

 5. The Java experience (where String is immutable and StringBuffer is
 the mutable, build-me-piecemeal object) turned out to be largely
 successful, modulo syntax details that are not so elegant.

 6. It meshes great with literals, which are exactly invariant arrays of
 characters:

 string hi = "Hello!"; // no dup needed! And it's 100% safe!

 So it looks like we have solved the string issue in an unexpectedly
 elegant way - by simply combining two features: invariant and array.

These are some of the reasons why a handful of us have been bitchin' about immutable views for so long. Regarding the alias, you'd likely have to consider w/dchar variations also ... it becomes a bit messy as things progress, which is one of the reasons we never had a string alias in the first place -- at least now there's more weight to the argument for an alias

Yah. Probably the aliases will come as a troika, string, wstring, and dstring (for lack of more inspired names). Andrei
Mar 19 2007
prev sibling next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 19 Mar 2007 15:05:29 -0700, Andrei Alexandrescu (See Website For
Email) wrote:


I wish it wasn't so hard to get a simple straight answer from the experts.

So far I think /The W&A Show/ is saying that 'const' and 'invariant' only
apply to reference data types (objects, variable-length arrays, and
pointers) and structs. They do not apply *in any way, shape or form* to any
other data type.

  const char[] s;    // ok
  const char   c;    // meaningless, the 'const' is ignored.
  const classFoo f;  // okay
  const double* b;   // okay;
  const double  d;   // meaningless, the 'const' is ignored.
  const structBar q; // okay

'const' means you cannot change the stuff the reference is referencing or
the members of the struct, *but* for reference types you can change the
reference value.

  s[0] = 'a'; // fails
  s    = "new data"; //ok
  s.length = 2; // ok

  c    = 'a'; // ok

  f.bar = 'a'; // fails
  f = new classFoo(); // okay

  *b    = 1.0; // fails
  b     = &someDouble; // ok

  d     = 1.0; // okay

  q.foo = 'a'; // fails

'invariant' means you cannot change the stuff the reference is referencing
or the members of the struct, *and* for reference types you cannot change
the reference value.

 (If you one used 'invariant' rather than 'const' in above declarations...)
  s[0] = 'a'; // fails
  s    = "new data"; // fails
  s.length = 2; // fails

  c    = 'a'; // ok

  f.bar = 'a'; // fails
  f = new classFoo(); // fails

  *b    = 1.0; // fails
  b     = &someDouble; // fails

  d     = 1.0; // okay

  q.foo = 'a'; // fails


However, if this is the interpretation of 'invariant', how does one ever
get to set the 'invariant' thing's value?

   invariant byte[] vCodes;
   . . . 
   vCodes = LoadCodesFromFile();  // fails, no???


 By the way, a couple of days ago I've had an idea. Many people here ask
 for a "string" alias because char[] is a tad hard to type and uneasy on
 the eyes. Probably a good candidate would be:
 
 alias invariant char[] string;

 The resulting type is pretty damn slick:
 
 1. You can alias it and slice it til you get blue in the face - no
 confusion ever, because nobody can overwrite the contents underneath
 your alias.

void func( string s ) { string t; char[] u; u = s; u[0] = 'a'; // fails. u = s[1..4]; u[0] = 'a'; // fails u = s.dup; u[0] = 'a' // ok t = s; // fails??? }
 3. The ~= operator works (somewhat surprisingly).

{ string t; char[] u; u ~= s; // ok s ~= 'a'; // fails ??? t ~= s; // fails??? }
 4. It's very, very rare that you want to modify some random character in
 a string, and when you do, use a char[] and then copy it back into a
 string, or rebuild the string from slices!

It is not so rare in the type of applications that I do. Are you saying that I can do this...? string func( string s ) { char[] u; u = s.dup; u[somerandomspot] = 'a'; return u; } or this ... ? void func( string ref s ) { char[] u; u = s.dup; u[somerandomspot] = 'a'; s = u; } or this ... ? string func( string ref s ) { char[] u; string t; u = s.dup; u[somerandomspot] = 'a'; t = u; } What I don't get here is the phrase "copy it back into a string". This sounds ambiguous to me. It could mean, "construct a new string containing the updated data", or "replace the original string reference with a reference to the updated data". But I was starting to think that 'invariant' meant that you couldn't change the reference value at all, so I don't see how one could change an 'invariant' parameter or construct an 'invariant' local /variable/.
 So it looks like we have solved the string issue in an unexpectedly
 elegant way - by simply combining two features: invariant and array.

I hope so. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 20/03/2007 9:28:14 AM
Mar 19 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 I wish it wasn't so hard to get a simple straight answer from the experts.

It's not like anybody tries to obfuscate their writing.
 So far I think /The W&A Show/ is saying that 'const' and 'invariant' only
 apply to reference data types (objects, variable-length arrays, and
 pointers) and structs. They do not apply *in any way, shape or form* to any
 other data type.
 
   const char[] s;    // ok
   const char   c;    // meaningless, the 'const' is ignored.
   const classFoo f;  // okay
   const double* b;   // okay;
   const double  d;   // meaningless, the 'const' is ignored.
   const structBar q; // okay
 
 'const' means you cannot change the stuff the reference is referencing or
 the members of the struct, *but* for reference types you can change the
 reference value.
 
   s[0] = 'a'; // fails
   s    = "new data"; //ok
   s.length = 2; // ok
 
   c    = 'a'; // ok
 
   f.bar = 'a'; // fails
   f = new classFoo(); // okay
 
   *b    = 1.0; // fails
   b     = &someDouble; // ok
 
   d     = 1.0; // okay
 
   q.foo = 'a'; // fails

Correct.
 'invariant' means you cannot change the stuff the reference is referencing
 or the members of the struct, *and* for reference types you cannot change
 the reference value.

No. Invariant means that the data referenced indirectly is never modifiable by any part of the program. What you called 'invariant' above is 'final const'. [snip]
 However, if this is the interpretation of 'invariant', how does one ever
 get to set the 'invariant' thing's value?
 
    invariant byte[] vCodes;
    . . . 
    vCodes = LoadCodesFromFile();  // fails, no???

Right now you can only initialize invariant with literals. This aspect of the language is still under development. (Things like the result of some_string.dup also come to mind.)
  void func( string s )
  {
 
      string t;
      char[] u;
      u = s;
      u[0] = 'a'; // fails.
      u = s[1..4];
      u[0] = 'a'; // fails
      u = s.dup;
      u[0] = 'a' // ok
 
      t = s; // fails???
 
  }

You can't make the assignment u = s.
 3. The ~= operator works (somewhat surprisingly).

{ string t; char[] u; u ~= s; // ok s ~= 'a'; // fails ??? t ~= s; // fails??? }

s ~= a; works and is equivalent to s = s ~ a, i.e. "rebind s to the concatenation of s and a". It's an operation that does not mutate s's contents.
 4. It's very, very rare that you want to modify some random character in
 a string, and when you do, use a char[] and then copy it back into a
 string, or rebuild the string from slices!

It is not so rare in the type of applications that I do.

You use char[] for that, and when you are done modifying, you can put things in a string.
 Are you saying that I can do this...?
 
  string func( string s )
  {
      char[] u;
      u = s.dup;
      u[somerandomspot] = 'a';
      return u;
  }

This is sound, but tricky to typecheck. I'll have to get back to you on that one.
 or this ... ?
 
  void func( string ref s )
  {
      char[] u;
      u = s.dup;
      u[somerandomspot] = 'a';
      s = u;
  }

Same as above.
 or this ... ?
 
  string func( string ref s )
  {
      char[] u;
      string t;
      u = s.dup;
      u[somerandomspot] = 'a';
      t = u;
  }

These last three examples work because u is temporary and not aliased with anything. If it were, the code would be unsound. Such cases are tricky to typecheck in the general case, but we'll look into a solution that allows such expressive idioms without also taking risks.
 What I don't get here is the phrase "copy it back into a string". This
 sounds ambiguous to me. It could mean, "construct a new string containing
 the updated data", or "replace the original string reference with a
 reference to the updated data". But I was starting to think that
 'invariant' meant that you couldn't change the reference value at all, so I
 don't see how one could change an 'invariant' parameter or construct an
 'invariant' local /variable/.

Indeed, 'invariant' meant that the referenced data is invariant. The actual variable can be rebound to any other invariant data. It's like watching TV: you can always change your choice of watching, but you can't modify the actual contents of programs - unless you take special measures :o). Andrei
Mar 19 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 19 Mar 2007 16:20:04 -0700, Andrei Alexandrescu (See Website For
Email) wrote:

 Derek Parnell wrote:
 I wish it wasn't so hard to get a simple straight answer from the experts.

It's not like anybody tries to obfuscate their writing.

LOL ... I realize that. Some people are just naturals at it.
 So far I think /The W&A Show/ is saying that 'const' and 'invariant' only
 apply to reference data types (objects, variable-length arrays, and
 pointers) and structs. They do not apply *in any way, shape or form* to any
 other data type.
 
   const char[] s;    // ok
   const char   c;    // meaningless, the 'const' is ignored.
   const classFoo f;  // okay
   const double* b;   // okay;
   const double  d;   // meaningless, the 'const' is ignored.
   const structBar q; // okay
 
 'const' means you cannot change the stuff the reference is referencing or
 the members of the struct, *but* for reference types you can change the
 reference value.
 
   s[0] = 'a'; // fails
   s    = "new data"; //ok
   s.length = 2; // ok
 
   c    = 'a'; // ok
 
   f.bar = 'a'; // fails
   f = new classFoo(); // okay
 
   *b    = 1.0; // fails
   b     = &someDouble; // ok
 
   d     = 1.0; // okay
 
   q.foo = 'a'; // fails

Correct.

YAY!!!! Finally something has sunk in to my head <G>
 'invariant' means you cannot change the stuff the reference is referencing
 or the members of the struct, *and* for reference types you cannot change
 the reference value.

No. Invariant means that the data referenced indirectly is never modifiable by any part of the program. What you called 'invariant' above is 'final const'.

Hmmm ... so the difference between 'const' and 'invariant' is that const is only effective for one level deep, but 'invariant' is effective for all levels, right? struct Foo { int a; int* b; Foo c; Foo* d; } const Foo f_const; invariant Foo f_invar; int x; // assuming we can initialize f_const and f_invar at run time somehow, // then are the following subsequent assignments as stated? f_const.a = 1; // fails f_const.b = &x; // fails *f_const.b = 1; // okay; f_const.c.a = 1; // okay f_const.c.b = &x; // okay *f_const.c.b = 1; // okay f_const.d = new Foo; // fails f_const.d.a = 1; // okay f_const.d.b = &x; // okay *f_const.d.b = 1; // okay f_invar.a = 1; // fails f_invar.b = &x; // fails *f_invar.b = 1; // fails; f_invar.c.a = 1; // fails f_invar.c.b = &x; // fails *f_invar.c.b = 1; // fails f_invar.d = new Foo; // fails f_invar.d.a = 1; // fails f_invar.d.b = &x; // fails *f_invar.d.b = 1; // fails
 [snip]
 However, if this is the interpretation of 'invariant', how does one ever
 get to set the 'invariant' thing's value?
 
    invariant byte[] vCodes;
    . . . 
    vCodes = LoadCodesFromFile();  // fails, no???

Right now you can only initialize invariant with literals. This aspect of the language is still under development. (Things like the result of some_string.dup also come to mind.)

Oh ... okay. I'll wait for that mind bender then.
  void func( string s )
  {
      char[] u;
      u = s;

You can't make the assignment u = s.

Oh, okay. The compiler will stop at "u = s;" because that would have made access to 's' data via the 'u' symbol. Got it.
 3. The ~= operator works (somewhat surprisingly).

{ string t; char[] u; u ~= s; // ok s ~= 'a'; // fails ??? t ~= s; // fails??? }

s ~= a; works and is equivalent to s = s ~ a, i.e. "rebind s to the concatenation of s and a". It's an operation that does not mutate s's contents.

So, could this be the mechanism to set an invariant array? invariant ubyte[] b; b ~= Load_Stuff_From_File();
 4. It's very, very rare that you want to modify some random character in
 a string, and when you do, use a char[] and then copy it back into a
 string, or rebuild the string from slices!

It is not so rare in the type of applications that I do.

You use char[] for that, and when you are done modifying, you can put things in a string.

Oops. I was using "string" in the normal sense and not your new alias idea. In the context of the alias 'string' then what you say makes a lot of sense.
 It's like watching TV: you can always change your choice of watching

(Not if my wife has the remote <G>) -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 20/03/2007 10:53:00 AM
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 Hmmm ... so the difference between 'const' and 'invariant' is that const is
 only effective for one level deep, but 'invariant' is effective for all
 levels, right?

Nonono. Const means: it might have originated as a variable, and somebody, somewhere, might hold a mutating reference to this and call it "my precious". Invariant is: nobody ever holds a mutating reference to it. Example: void Fun(const char[] a, char[] b) { char c = a[0]; b[0] = 'z'; assert(a[0] == c); // will fail } void main() { char[] x = "Wyda".dup; Fun(x, x); } Notice how the data referenced to by a is changing from under a's feet. Now let's rewrite Fun: void Gun(invariant char[] a, char[] b) { char c = a[0]; b[0] = 'z'; assert(a[0] == c); // never, ever, ever, ever fails } void main() { char[] x = "Wyda".dup; Fun(x, x); // can't compile! // mutating references can't be bound to invariant references } Andrei
Mar 19 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 19 Mar 2007 17:50:28 -0700, Andrei Alexandrescu (See Website For
Email) wrote:

 Derek Parnell wrote:
 Hmmm ... so the difference between 'const' and 'invariant' is that const is
 only effective for one level deep, but 'invariant' is effective for all
 levels, right?

Nonono. Const means: it might have originated as a variable, and somebody, somewhere, might hold a mutating reference to this and call it "my precious". Invariant is: nobody ever holds a mutating reference to it.

Yeah ... but that's not what I was talking about. But be that as it may, it seems you are saying that the *only* difference between 'const' and 'invariant' is that 'invariant' means that every write attempt (direct and indirect) is denied, but 'const' only prevents direct write attempts. By 'direct' I mean explicitly using the symbol in the statement, and not another symbol that happens to contain the same address of the data. But I was talking earlier about the situation where something contains reference types or structs, and what effect 'const' and 'invariant' have on the things indirectly accessible via member data. struct Foo { char[] s; } void Func(const Foo fc, invariant Foo fi) { fc.s = "abc"; // fails ??? fc.s[0] = 'a'; // okay ??? fi.s = "abc"; // fails ??? fi.s[0] = 'a'; // fails ??? } Foo a; Foo b; Func(a,b); // fails ??? const Foo ac; const Foo bc; Func(ac,bc); // fails ??? invariant Foo ai; invariant Foo bi; Func(ai,bi); // fails ??? Func(ac,bi); // okay ??? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 20/03/2007 12:04:01 PM
Mar 19 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Mon, 19 Mar 2007 17:50:28 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 
 Derek Parnell wrote:
 Hmmm ... so the difference between 'const' and 'invariant' is that const is
 only effective for one level deep, but 'invariant' is effective for all
 levels, right?

somebody, somewhere, might hold a mutating reference to this and call it "my precious". Invariant is: nobody ever holds a mutating reference to it.

Yeah ... but that's not what I was talking about. But be that as it may, it seems you are saying that the *only* difference between 'const' and 'invariant' is that 'invariant' means that every write attempt (direct and indirect) is denied, but 'const' only prevents direct write attempts. By 'direct' I mean explicitly using the symbol in the statement, and not another symbol that happens to contain the same address of the data. But I was talking earlier about the situation where something contains reference types or structs, and what effect 'const' and 'invariant' have on the things indirectly accessible via member data. struct Foo { char[] s; } void Func(const Foo fc, invariant Foo fi) { fc.s = "abc"; // fails ???

Works. Understand that fc is the private copy owned by Func.
     fc.s[0] = 'a'; // okay ???

Fails. fc would change bits not belonging to itself, and "const" makes those bits unchangeable.
     fi.s = "abc"; // fails ???

Works. Modifies Func's own private copy of fi.
     fi.s[0] = 'a'; // fails ???

Fails.
  }
 
  Foo a;
  Foo b;
  Func(a,b); // fails ???

Fails. Can't bind modifiable b to an invariant Foo.
  const Foo ac;
  const Foo bc;
  Func(ac,bc); // fails ???

Fails. Can't bind const b (which conservatively may be aliased to a modifiable view) to an invariant Foo.
  invariant Foo ai;
  invariant Foo bi;
  Func(ai,bi); // fails ???

Works. No problem to bind ai to a const Foo.
  Func(ac,bi); // okay ???

Perfect. Andrei
Mar 19 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Derek Parnell wrote:
 On Mon, 19 Mar 2007 15:05:29 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 
 
 I wish it wasn't so hard to get a simple straight answer from the experts.

I think discussion would much improved if big changes like this actually came with a detailed proposal on a website somewhere that people could refer to and which would actually be updated to reflect vague points clarified during discussion. As it is, there are a lot of points that have been clarified, and questions answered, but they're strewn across 100 different posts. I'm pretty lost. I gave up trying to follow this myself. I'm hoping you'll figure it out and post something coherent. :-) On the bright side, thanks to Andrei, at least there *is* an open discussion going on about the feature before it just shows up in the change log as a done deal. --bb
Mar 19 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Derek Parnell wrote:
 On Mon, 19 Mar 2007 15:05:29 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:


 I wish it wasn't so hard to get a simple straight answer from the 
 experts.

I think discussion would much improved if big changes like this actually came with a detailed proposal on a website somewhere that people could refer to and which would actually be updated to reflect vague points clarified during discussion. As it is, there are a lot of points that have been clarified, and questions answered, but they're strewn across 100 different posts. I'm pretty lost. I gave up trying to follow this myself. I'm hoping you'll figure it out and post something coherent. :-) On the bright side, thanks to Andrei, at least there *is* an open discussion going on about the feature before it just shows up in the change log as a done deal.

I agree. (How couldn't I? Flattery goes a long way :o).) Maybe we could imitate what other languages do, e.g. Perl has the famous "apocalypses" that describe each language feature in detail. Andrei
Mar 19 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 Derek Parnell wrote:
 On Mon, 19 Mar 2007 15:05:29 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:


 I wish it wasn't so hard to get a simple straight answer from the 
 experts.

I think discussion would much improved if big changes like this actually came with a detailed proposal on a website somewhere that people could refer to and which would actually be updated to reflect vague points clarified during discussion. As it is, there are a lot of points that have been clarified, and questions answered, but they're strewn across 100 different posts. I'm pretty lost. I gave up trying to follow this myself. I'm hoping you'll figure it out and post something coherent. :-) On the bright side, thanks to Andrei, at least there *is* an open discussion going on about the feature before it just shows up in the change log as a done deal.

I agree. (How couldn't I? Flattery goes a long way :o).) Maybe we could imitate what other languages do, e.g. Perl has the famous "apocalypses" that describe each language feature in detail.

I was thinking more of Python's PEPs, but yeh, same deal. I like PEPs in that anyone can submit one, get it assigned an offical number. As opposed to Larry's stone tablets brought down from the mountain top for the rest of us to ooh and ahh over. Python's way is a little more egalitarian. Semi-official Enhancement Proposals are also a good way to handle the many repeated requests for 'feature X'. Just tell 'em -- great idea, now go write a DEP for it. If they actually do it, then great. Next time someone asks for the feature you just point 'em to the previous DEP and the BFD ruling on it. My impression is that PEPs get weighted somehow based on how fleshed-out they are. For instance it seems a majority of PEPs (at least the ones I've read) come complete with prototype implementations. So PEP is more than just a vague wish like "Please implement Java-style serialization". It has to contain the nitty gritty details. --bb
Mar 19 2007
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 
 Bill Baxter wrote:

 Derek Parnell wrote:

 On Mon, 19 Mar 2007 15:05:29 -0700, Andrei Alexandrescu (See Website 
 For
 Email) wrote:


 I wish it wasn't so hard to get a simple straight answer from the 
 experts.

I think discussion would much improved if big changes like this actually came with a detailed proposal on a website somewhere that people could refer to and which would actually be updated to reflect vague points clarified during discussion. As it is, there are a lot of points that have been clarified, and questions answered, but they're strewn across 100 different posts. I'm pretty lost. I gave up trying to follow this myself. I'm hoping you'll figure it out and post something coherent. :-) On the bright side, thanks to Andrei, at least there *is* an open discussion going on about the feature before it just shows up in the change log as a done deal.

I agree. (How couldn't I? Flattery goes a long way :o).) Maybe we could imitate what other languages do, e.g. Perl has the famous "apocalypses" that describe each language feature in detail.

I was thinking more of Python's PEPs, but yeh, same deal. I like PEPs in that anyone can submit one, get it assigned an offical number. As opposed to Larry's stone tablets brought down from the mountain top for the rest of us to ooh and ahh over. Python's way is a little more egalitarian. Semi-official Enhancement Proposals are also a good way to handle the many repeated requests for 'feature X'. Just tell 'em -- great idea, now go write a DEP for it. If they actually do it, then great. Next time someone asks for the feature you just point 'em to the previous DEP and the BFD ruling on it. My impression is that PEPs get weighted somehow based on how fleshed-out they are. For instance it seems a majority of PEPs (at least the ones I've read) come complete with prototype implementations. So PEP is more than just a vague wish like "Please implement Java-style serialization". It has to contain the nitty gritty details. --bb

The PEP numbers are also a convenient way to refer to certain issues or features: "Oh the 'with' statement? That's PEP 343." The PEP can also serve as a starting point for the official documentation of the feature. Most importantly, they serve as a fixed locus of discussion, and can vastly improve the low signal-to-noise ratio that running around in circles on a mailing list or newsgroup can cause. In other words: DEPs++ -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Mar 19 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Kirk McDonald wrote:
 Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 Bill Baxter wrote:

 Derek Parnell wrote:

 On Mon, 19 Mar 2007 15:05:29 -0700, Andrei Alexandrescu (See 
 Website For
 Email) wrote:


 I wish it wasn't so hard to get a simple straight answer from the 
 experts.

I think discussion would much improved if big changes like this actually came with a detailed proposal on a website somewhere that people could refer to and which would actually be updated to reflect vague points clarified during discussion. As it is, there are a lot of points that have been clarified, and questions answered, but they're strewn across 100 different posts. I'm pretty lost. I gave up trying to follow this myself. I'm hoping you'll figure it out and post something coherent. :-) On the bright side, thanks to Andrei, at least there *is* an open discussion going on about the feature before it just shows up in the change log as a done deal.

I agree. (How couldn't I? Flattery goes a long way :o).) Maybe we could imitate what other languages do, e.g. Perl has the famous "apocalypses" that describe each language feature in detail.

I was thinking more of Python's PEPs, but yeh, same deal. I like PEPs in that anyone can submit one, get it assigned an offical number. As opposed to Larry's stone tablets brought down from the mountain top for the rest of us to ooh and ahh over. Python's way is a little more egalitarian. Semi-official Enhancement Proposals are also a good way to handle the many repeated requests for 'feature X'. Just tell 'em -- great idea, now go write a DEP for it. If they actually do it, then great. Next time someone asks for the feature you just point 'em to the previous DEP and the BFD ruling on it. My impression is that PEPs get weighted somehow based on how fleshed-out they are. For instance it seems a majority of PEPs (at least the ones I've read) come complete with prototype implementations. So PEP is more than just a vague wish like "Please implement Java-style serialization". It has to contain the nitty gritty details. --bb

The PEP numbers are also a convenient way to refer to certain issues or features: "Oh the 'with' statement? That's PEP 343." The PEP can also serve as a starting point for the official documentation of the feature. Most importantly, they serve as a fixed locus of discussion, and can vastly improve the low signal-to-noise ratio that running around in circles on a mailing list or newsgroup can cause. In other words: DEPs++

I, too, think that's a great idea. Andrei
Mar 19 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 20 Mar 2007 08:29:31 +0900, Bill Baxter wrote:

 On the bright side, thanks to Andrei, at least there *is* an open 
 discussion going on about the feature before it just shows up in the 
 change log as a done deal.

Amen to that. A *HUGE* thank you to Andrei for being so available and willing to suffer fools gladly. This discussions extremely important for everybody an the big W just can't do it all. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 20/03/2007 10:49:22 AM
Mar 19 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Tue, 20 Mar 2007 08:29:31 +0900, Bill Baxter wrote:
 
 On the bright side, thanks to Andrei, at least there *is* an open 
 discussion going on about the feature before it just shows up in the 
 change log as a done deal.

Amen to that. A *HUGE* thank you to Andrei for being so available and willing to suffer fools gladly. This discussions extremely important for everybody an the big W just can't do it all.

I thank you folks. Sharing is great. Studying alone is learning from a fool. Andrei
Mar 19 2007
prev sibling parent reply jovo <jovo at.home> writes:
Derek Parnell Wrote:
 
   const char[] s;    // ok
   ...
   s[0] = 'a'; // fails
   s    = "new data"; //ok
   s.length = 2; // ok
 

No, it can not. const int[] a = [1, 2, 3, 4]; a.length = 2; // ok? some_var = a[3]; // oops! But length is not field. jovo
Mar 20 2007
parent jovo <jovo at.home> writes:
jovo Wrote:

 Derek Parnell Wrote:
 
   const char[] s;    // ok
   ...
   s[0] = 'a'; // fails
   s    = "new data"; //ok
   s.length = 2; // ok
 

No, it can not. const int[] a = [1, 2, 3, 4]; a.length = 2; // ok? some_var = a[3]; // oops!

Ooops! :) May be it's more logical then I was thinking. There are will be always tension between consistency, convenience and efficiency of generated code. Actually I like general D's approach. jovo
Mar 20 2007
prev sibling next sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 By the way, a couple of days ago I've had an idea. Many people here ask
 for a "string" alias because char[] is a tad hard to type and uneasy on
 the eyes. Probably a good candidate would be:
 
 alias invariant char[] string;
 

Now that I like. Reminds me very much of Python. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Mar 19 2007
prev sibling parent reply jovo <jovo at.home> writes:
Andrei Alexandrescu (See Website For Email) Wrote:

 (Reposted after cleanup :o))
 
 kris wrote:
 How about some further example? Given these sigs:
 
 struct Foo
 {
    int a;
 }
 
 class Bar
 {
    char[] b;
 }
 
 void fooish (inout Foo foo) {}
 
 char[] barish (Bar bar) {return bar.b;}
 
 
 1) how do I modify the function decls to have the compiler *enforce* 
 readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be 
 mutated within the function body?

void fooish (const ref Foo foo) {}

void fooish(const ref Foo foo) { foo.a = 1; // not allowed foo = sam_Foo; // allowed? jovo
Mar 20 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
jovo wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:
 
 (Reposted after cleanup :o))

 kris wrote:
 How about some further example? Given these sigs:

 struct Foo
 {
    int a;
 }

 class Bar
 {
    char[] b;
 }

 void fooish (inout Foo foo) {}

 char[] barish (Bar bar) {return bar.b;}


 1) how do I modify the function decls to have the compiler *enforce* 
 readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be 
 mutated within the function body?


void fooish(const ref Foo foo) { foo.a = 1; // not allowed

Not allowed indeed.
     foo = sam_Foo;           // allowed?

Not allowed. The type of foo is const(ref(Foo)). When you try to assign to foo, you'll assign to indirectly referenced bits. If you only replace Foo with int (which makes sense because Foo is a struct), you'll see how trying to assign the int would indirectly modify memory outside the function. Now consider: void Fun(ref const Foo foo) { } The type of foo is ref(const(Foo)). So the function has a reference to a constant object. It can modify the direct fields of the object, but nothing beyond that. It's not a very useful case. We might even choose to prohibit it statically unless compelling use cases show up. I actually already think of a couple :o). Andrei
Mar 20 2007
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 Assuming that 'invariant' = really constant, 'const' = C++ 
 pseudo-const...

Yah.
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% 
 of my usages of 'const' to 'invariant'.

Great to hear that!

Sorry, I didn't explain myself very well. I meant that this proposal breaks 100% of all existing D uses of const. >> (3) I concede the problem of association with 'readonly' and ROM. But
 the association between 'const' and 'constant' is pretty strong.

I think there's universal agreement that you can't express tersely the two notions "const-as-in-C++" and "const-as-in-we-really-mean-it". Probably it's best to just go with two terms and move on.

I completely agree with this. The gripe I have is that 'const' is a completely inappropriate term for 'const-as-in-C++'. It's a bizarre misnomer to use 'const' to mean "don't touch". The only benefit (albeit a considerable one) of this nomenclature is compatibility with C++ nomenclature, especially the term "const correctness". D currently has a 'const' which actually means "constant", it seems dreadful to exchange that for a clearly inferior "don't touch" meaning. I'm concerned that you may have spent so much time in C++ that you've become accustomed to "const" == "don't touch"; all of my previous posts have been arguing that this is a grossly unnatural association. It is possible that the C++ misnomer is so entrenched that D needs to perpetuate it, even at the expense of breaking all existing D code that uses 'const'. 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.
Mar 20 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% 
 of my usages of 'const' to 'invariant'.

Great to hear that!

Sorry, I didn't explain myself very well. I meant that this proposal breaks 100% of all existing D uses of const.

Ah, so I /did/ read that right.
  >> (3) I concede the problem of association with 'readonly' and ROM. But
 the association between 'const' and 'constant' is pretty strong.

I think there's universal agreement that you can't express tersely the two notions "const-as-in-C++" and "const-as-in-we-really-mean-it". Probably it's best to just go with two terms and move on.

I completely agree with this. The gripe I have is that 'const' is a completely inappropriate term for 'const-as-in-C++'. It's a bizarre misnomer to use 'const' to mean "don't touch". The only benefit (albeit a considerable one) of this nomenclature is compatibility with C++ nomenclature, especially the term "const correctness". D currently has a 'const' which actually means "constant", it seems dreadful to exchange that for a clearly inferior "don't touch" meaning. I'm concerned that you may have spent so much time in C++ that you've become accustomed to "const" == "don't touch"; all of my previous posts have been arguing that this is a grossly unnatural association.

I completely agree with *that* :).
 It is possible that the C++ misnomer is so entrenched that D needs to 
 perpetuate it, even at the expense of breaking all existing D code that 
 uses 'const'.

And I really hope this is not the case.
 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,

> 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? (Do this with reference types on the other hand, and I could see them being a bit confused) The thing to keep in mind is that 'b' is a new variable (kept on the stack) that happens to have the same value as whatever was passed to the function. If that variable happens not to be changed by the code where it's in scope, that doesn't effect other code in any way at all. In other words: There's no way to tell the difference from outside of the function, so why would it change the signature?
Mar 20 2007
parent reply Don Clugston <dac nospam.com.au> writes:
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,

> 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.
Mar 20 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
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,

> 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
Mar 20 2007
next sibling parent Walter Bright <newshound digitalmars.com> writes:
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.)
Mar 20 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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,

> 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
Mar 20 2007
parent reply Tyler Knott <tywebmail mailcity.com> writes:
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.
Mar 20 2007
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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
Mar 20 2007
parent reply Tyler Knott <tywebmail mailcity.com> writes:
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.
Mar 20 2007
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Tyler Knott wrote:
 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.

Well, yes, but is 'invariant' transitive like const? I think it is (but I'm not sure, I'm getting a bit lost in the thread), and if it is, those semantics won't work. I.e., "typeof(&foo) " in my example can't be invariant anything. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 22 2007
next sibling parent Tyler Knott <tywebmail mailcity.com> writes:
Bruno Medeiros wrote:
 Well, yes, but is 'invariant' transitive like const? I think it is (but 
 I'm not sure, I'm getting a bit lost in the thread), and if it is, those 
 semantics won't work. I.e., "typeof(&foo) " in my example can't be 
 invariant anything.
 

D'oh, of course! It's legal to modify data referenced by foo, which makes it incompatible with invariant pointers, but which is fine for const pointers. I think const pointer is the correct answer for typeof(&foo).
Mar 22 2007
prev sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Bruno Medeiros wrote:
 Tyler Knott wrote:
 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.

Well, yes, but is 'invariant' transitive like const? I think it is (but I'm not sure, I'm getting a bit lost in the thread), and if it is, those semantics won't work. I.e., "typeof(&foo) " in my example can't be invariant anything.

Actually, having thought over it a few times, what I would really expect is not a "const pointer to a Foo", but rather a "const(pointer to a Foo)"... final Foo foo; auto ptr = &foo; // const(Foo*) Hmm. Baffling. I almost give up. -- Chris Nicholson-Sauls
Mar 23 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Chris Nicholson-Sauls wrote:
 Bruno Medeiros wrote:
 Tyler Knott wrote:
 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.

Well, yes, but is 'invariant' transitive like const? I think it is (but I'm not sure, I'm getting a bit lost in the thread), and if it is, those semantics won't work. I.e., "typeof(&foo) " in my example can't be invariant anything.

Actually, having thought over it a few times, what I would really expect is not a "const pointer to a Foo", but rather a "const(pointer to a Foo)"... final Foo foo; auto ptr = &foo; // const(Foo*) Hmm. Baffling. I almost give up.

Don't. It's entirely understandable to be confused. Extremely little information has been given to date. Once that information will come in proper book format, it will be very easy to understand. Andrei
Mar 23 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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.

Let's suppose it's const Foo* (the less restrictive option). Even so, you will be restricting the type more than necessary, thus loosing some range of freedom. Because with const Foo* you can't change members of Foo*, while with final Foo, you can change members of foo. I.e.: final Foo foo; foo.x = 2; // ok typeof(&foo) fooptr = &foo; (*fooptr.x) = 2; // not allowed -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 22 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Bruno Medeiros wrote:
 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.

Let's suppose it's const Foo* (the less restrictive option). Even so, you will be restricting the type more than necessary, thus loosing some range of freedom. Because with const Foo* you can't change members of Foo*, while with final Foo, you can change members of foo. I.e.: final Foo foo; foo.x = 2; // ok typeof(&foo) fooptr = &foo; (*fooptr.x) = 2; // not allowed

I guess the only way to solve that is to parameterise const and invariant...
 final Foo foo;
 final invariant Foo bar;
 writefln("%s", typeinfo(typeof(&foo)));
 writefln("%s", typeinfo(typeof(&bar)));

 --> invariant(1) Foo
     invariant(*) Foo

Honestly, I don't think that's going to happen :P -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 22 2007
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Daniel Keep wrote:
 
 Bruno Medeiros wrote:
 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)
 ?

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.

you will be restricting the type more than necessary, thus loosing some range of freedom. Because with const Foo* you can't change members of Foo*, while with final Foo, you can change members of foo. I.e.: final Foo foo; foo.x = 2; // ok typeof(&foo) fooptr = &foo; (*fooptr.x) = 2; // not allowed

I guess the only way to solve that is to parameterise const and invariant...
 final Foo foo;
 final invariant Foo bar;
 writefln("%s", typeinfo(typeof(&foo)));
 writefln("%s", typeinfo(typeof(&bar)));

 --> invariant(1) Foo
     invariant(*) Foo

Honestly, I don't think that's going to happen :P -- Daniel

No, another way to solve that is to have 'final' be a type modifier as well: typeof(&foo) --> (final Foo)* This is essentially exactly like C++'s 'const'. (and like the 'rdonly' in my "hobbyist" design.) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 23 2007
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Bruno Medeiros wrote:
 Daniel Keep wrote:
 Bruno Medeiros wrote:
 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)
 ?

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.

you will be restricting the type more than necessary, thus loosing some range of freedom. Because with const Foo* you can't change members of Foo*, while with final Foo, you can change members of foo. I.e.: final Foo foo; foo.x = 2; // ok typeof(&foo) fooptr = &foo; (*fooptr.x) = 2; // not allowed

I guess the only way to solve that is to parameterise const and invariant...
 final Foo foo;
 final invariant Foo bar;
 writefln("%s", typeinfo(typeof(&foo)));
 writefln("%s", typeinfo(typeof(&bar)));

 --> invariant(1) Foo
     invariant(*) Foo

Honestly, I don't think that's going to happen :P -- Daniel

No, another way to solve that is to have 'final' be a type modifier as well: typeof(&foo) --> (final Foo)* This is essentially exactly like C++'s 'const'. (and like the 'rdonly' in my "hobbyist" design.)

I think the problem with that is that final pi = 3.1415...; then doesn't do what you think it does. If pi is now of type final real, then you would still be able to re-assign it. final e = 2.14...; pi = e; We probably *could* make it so that you can't assign to a variable that's got a final'ed type, but then it's acting like a storage class, not as a type. I dunno. This whole thread is just giving me a headache... I *thought* I understood it after the first post, but now my head hurts and I'm not so sure :P -- Daniel -- int getRandomNumber() { return 4; // chosen by fair dice roll. // guaranteed to be random. } http://xkcd.com/ v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 23 2007
prev sibling next sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Don Clugston wrote:

 I completely agree with this. The gripe I have is that 'const' is a
 completely inappropriate term for 'const-as-in-C++'. It's a bizarre
 misnomer to use 'const' to mean "don't touch". The only benefit (albeit
 a considerable one) of this nomenclature is compatibility with C++
 nomenclature, especially the term "const correctness".
 D currently has a 'const' which actually means "constant", it seems
 dreadful to exchange that for a clearly inferior "don't touch" meaning.
 I'm concerned that you may have spent so much time in C++ that you've
 become accustomed to "const" == "don't touch"; all of my previous posts
 have been arguing that this is a grossly unnatural association.
 
 It is possible that the C++ misnomer is so entrenched that D needs to
 perpetuate it, even at the expense of breaking all existing D code that
 uses 'const'.

I agree with Don here. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Mar 20 2007
parent reply kris <foo bar.com> writes:
Lars Ivar Igesund wrote:
 Don Clugston wrote:
 
 
I completely agree with this. The gripe I have is that 'const' is a
completely inappropriate term for 'const-as-in-C++'. It's a bizarre
misnomer to use 'const' to mean "don't touch". The only benefit (albeit
a considerable one) of this nomenclature is compatibility with C++
nomenclature, especially the term "const correctness".
D currently has a 'const' which actually means "constant", it seems
dreadful to exchange that for a clearly inferior "don't touch" meaning.
I'm concerned that you may have spent so much time in C++ that you've
become accustomed to "const" == "don't touch"; all of my previous posts
have been arguing that this is a grossly unnatural association.

It is possible that the C++ misnomer is so entrenched that D needs to
perpetuate it, even at the expense of breaking all existing D code that
uses 'const'.

I agree with Don here.

So do I Was somewhat hoping invariant would be used in place of what has now taken shape as "const", and "constant" would be used in place of where "invariant" has landed. Then there would be no "const" to confuse anyone. Almost seems as though shortening the name by a couple of bytes is actually more important than the meaning of the words in use? I'm not sure about "breaking all existing D code" ... there would seem to be very little use of "const" at this time, because the current semantics are really kinda worthless; e.g. one should probably use enum for most things 'constant', except you can't have "enum : char[]" making enum notably less useful than it could otherwise be. Where does enum fit in this picture anyway? Can it recover some duty from "const" and/or "final" in a logical and consistent manner?
Mar 20 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Lars Ivar Igesund wrote:
 Don Clugston wrote:


 I completely agree with this. The gripe I have is that 'const' is a
 completely inappropriate term for 'const-as-in-C++'. It's a bizarre
 misnomer to use 'const' to mean "don't touch". The only benefit (albeit
 a considerable one) of this nomenclature is compatibility with C++
 nomenclature, especially the term "const correctness".
 D currently has a 'const' which actually means "constant", it seems
 dreadful to exchange that for a clearly inferior "don't touch" meaning.
 I'm concerned that you may have spent so much time in C++ that you've
 become accustomed to "const" == "don't touch"; all of my previous posts
 have been arguing that this is a grossly unnatural association.

 It is possible that the C++ misnomer is so entrenched that D needs to
 perpetuate it, even at the expense of breaking all existing D code that
 uses 'const'.

I agree with Don here.

So do I Was somewhat hoping invariant would be used in place of what has now taken shape as "const", and "constant" would be used in place of where "invariant" has landed. Then there would be no "const" to confuse anyone. Almost seems as though shortening the name by a couple of bytes is actually more important than the meaning of the words in use?

I think the 5 letters to 9 letters a difference, especially when repeated many times. And again: invariant and const are synonims. Merriam-Webster lists "constant" as the first meaning of "invariant": http://m-w.com/dictionary/invariant
 I'm not sure about "breaking all existing D code" ... there would seem 
 to be very little use of "const" at this time, because the current 
 semantics are really kinda worthless; e.g. one should probably use enum 
 for most things 'constant', except you can't have "enum : char[]" making 
 enum notably less useful than it could otherwise be.
 
 Where does enum fit in this picture anyway? Can it recover some duty 
 from "const" and/or "final" in a logical and consistent manner?

If I had my way, I'd make the language powerful enough to express enumerated types in stdlib, then I'd deprecate the built-in one. Andrei
Mar 20 2007
parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:

 Was somewhat hoping invariant would be used in place of what has now 
 taken shape as "const", and "constant" would be used in place of where 
 "invariant" has landed. Then there would be no "const" to confuse 
 anyone. Almost seems as though shortening the name by a couple of 
 bytes is actually more important than the meaning of the words in use?

I think the 5 letters to 9 letters a difference, especially when repeated many times. And again: invariant and const are synonims. Merriam-Webster lists "constant" as the first meaning of "invariant": http://m-w.com/dictionary/invariant

Yeah, I know. Except there's three flies in the ointment: 1) you've already suggested "const" won't get used all that much (or can be implied/inferred instead). 2) Then there's the question of whether "constant" is a more appropriate term for what "invariant" is slated to do. 3) Additionally, it may well be better to dump "const" altogether simply to alleviate potential confusion. It's hardly a life or death scenario, yet the choices being made really do appear to be slanted towards "something" that just doesn't seem entirely sensible
 I'm not sure about "breaking all existing D code" ... there would seem 
 to be very little use of "const" at this time, because the current 
 semantics are really kinda worthless; e.g. one should probably use 
 enum for most things 'constant', except you can't have "enum : char[]" 
 making enum notably less useful than it could otherwise be.

 Where does enum fit in this picture anyway? Can it recover some duty 
 from "const" and/or "final" in a logical and consistent manner?

If I had my way, I'd make the language powerful enough to express enumerated types in stdlib, then I'd deprecate the built-in one.

Would be good to make the type system more flexible and extensible, but that's getting away from the point: enum represents 'constant' values, so presumably has some bearing upon this discussion?
Mar 20 2007
prev sibling next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 Assuming that 'invariant' = really constant, 'const' = C++ 
 pseudo-const...

Yah.
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100% 
 of my usages of 'const' to 'invariant'.

Great to hear that!

Sorry, I didn't explain myself very well. I meant that this proposal breaks 100% of all existing D uses of const.

I don't think so. Const becomes less precise, but maybe code wasn't using it with all the precision anyway. I actually suspect the new semantics will break very little code.
  >> (3) I concede the problem of association with 'readonly' and ROM. But
 the association between 'const' and 'constant' is pretty strong.

I think there's universal agreement that you can't express tersely the two notions "const-as-in-C++" and "const-as-in-we-really-mean-it". Probably it's best to just go with two terms and move on.

I completely agree with this. The gripe I have is that 'const' is a completely inappropriate term for 'const-as-in-C++'. It's a bizarre misnomer to use 'const' to mean "don't touch".

The problem is that we don't have a "nomer". Every good short name we considered would be as imprecise or more. There's no obvious choice that everybody's like, "those idiots! why did they _refuse_ to do it that way?" And it's telling that every single person who didn't like "const" with the intended meaning failed to come with an obviously better choice.
 The only benefit (albeit 
 a considerable one) of this nomenclature is compatibility with C++ 
 nomenclature, especially the term "const correctness".

Right. Imagine us coming with the "dontouch correctness" :o).
 D currently has a 'const' which actually means "constant", it seems 
 dreadful to exchange that for a clearly inferior "don't touch" meaning.

It's not "inferior". If it were inferior, we'd take it out of the language. Today's "const" semantics are restrictive in ways that severely limit its usefulness (e.g. you can't pass data modifiable in a module to functions guaranteed to NOT modify data in another module). The new const will allow that, and that's great. It's not inferior.
 I'm concerned that you may have spent so much time in C++ that you've 
 become accustomed to "const" == "don't touch"; all of my previous posts 
 have been arguing that this is a grossly unnatural association.

I completely hear you. Yet you are failing to follow with, "...when it's so obvious to everyone that the natural association is..."
 It is possible that the C++ misnomer is so entrenched that D needs to 
 perpetuate it, even at the expense of breaking all existing D code that 
 uses 'const'.
 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,

> 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.

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. Andrei
Mar 20 2007
next sibling parent torhu <fake address.dude> writes:
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.
Mar 20 2007
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 I think there's universal agreement that you can't express tersely 
 the two notions "const-as-in-C++" and 
 "const-as-in-we-really-mean-it". Probably it's best to just go with 
 two terms and move on.

I completely agree with this. The gripe I have is that 'const' is a completely inappropriate term for 'const-as-in-C++'. It's a bizarre misnomer to use 'const' to mean "don't touch".

The problem is that we don't have a "nomer". Every good short name we considered would be as imprecise or more. There's no obvious choice that everybody's like, "those idiots! why did they _refuse_ to do it that way?" And it's telling that every single person who didn't like "const" with the intended meaning failed to come with an obviously better choice.

Actually, I have not tried to come up with a better choice. I still think 'readonly' captures it pretty well. I've changed my mind about the validity of the argument about "it's not obvious which means truly constant, 'const' or 'readonly'" since I feel that it applies just as strongly to 'const' vs 'invariant'. My intuition would be that 'invariant' means read-only. The usage of 'invariant' in design-by-contract is for something that can be modified temporarily but must ultimately be preserved; this is weaker than 'constant'.
 The only benefit (albeit a considerable one) of this nomenclature is 
 compatibility with C++ nomenclature, especially the term "const 
 correctness".

Right. Imagine us coming with the "dontouch correctness" :o).
 D currently has a 'const' which actually means "constant", it seems 
 dreadful to exchange that for a clearly inferior "don't touch" meaning.

It's not "inferior". If it were inferior, we'd take it out of the language. Today's "const" semantics are restrictive in ways that severely limit its usefulness (e.g. you can't pass data modifiable in a module to functions guaranteed to NOT modify data in another module). The new const will allow that, and that's great. It's not inferior.

I'm arguing about the name, not the semantics. The use of 'const' in the original post has caused no end of confusion, since it is different from the current D const. To rephrase: 'const' is a very appropriate word for what 'const' does in D now: it defines a constant. The proposed const has an inferior connection to the word 'constant'. Actually, there's a feature of the existing const which seems to be missing in the new proposal. Currently, if you have const int x = 4; it is illegal to write &x. (just as it is illegal to write &4). This means that the existing const cannot be subverted, even by asm code. (#define x 4 is similar, except untyped). Can you do this with the new proposal?
 I'm concerned that you may have spent so much time in C++ that you've 
 become accustomed to "const" == "don't touch"; all of my previous 
 posts have been arguing that this is a grossly unnatural association.

I completely hear you. Yet you are failing to follow with, "...when it's so obvious to everyone that the natural association is..."

The natural association is "const" == "never changes, under any circumstances". "readonly" == "don't touch". Even for read-only memory, if you've ever used an EPROM burner, or flashed firmware, you know that someone can write to it. I've changed readonly memory, I've never changed a constant. The concept of 'read-only' access is pretty widely understood.
Mar 21 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Don Clugston wrote:

 The natural association is "const" == "never changes, under any 
 circumstances". "readonly" == "don't touch".
 Even for read-only memory, if you've ever used an EPROM burner, or 
 flashed firmware, you know that someone can write to it. I've changed 
 readonly memory, I've never changed a constant.
 The concept of 'read-only' access is pretty widely understood.

I dunno about that. Yes if you're talking about physical constants or constants like Pi, they never ever change. But 'constant' is also used frequently in mathematics to mean something that's simply "not variable in the current context". Like when doing partial differentiation with respect to y, all variables besides y are treated as constants. And if you don't agree with me, the very first section of the Wikipedia on "Constant" (http://en.wikipedia.org/wiki/Constant) talks about "unspecified constants", referring to parts of an equation that simply aren't variables. But you can definitely change them if you want (the example they use is the Pythagorean theorem). They are only constant for one particular instance of the problem. Even if the Wikipedia article is way off base (the Pythagorean theorem seems a very odd choice to demonstrate the concept of 'constant') the fact that such text managed to make it onto Wikipedia indicates that the common understanding of the word "constant" includes more territory than you're apparently allowing for. Also your definition of constant (i.e. something like Pi which absolutely cannot change) seems to be a more a replacement for the proposed 'final' as in 'final Pi = 3.14;' than for a reference whose referent cannot change. So personally I'm of the opinion we should pick two words that make at least a modicum of sense in English, forget about what they might mean in C++ or Java or whatever, make sure the common one is short, easy to type, and easy on the eyes, and just go with it. --bb
Mar 21 2007
parent Don Clugston <dac nospam.com.au> writes:
Bill Baxter wrote:
 Don Clugston wrote:
 
 The natural association is "const" == "never changes, under any 
 circumstances". "readonly" == "don't touch".
 Even for read-only memory, if you've ever used an EPROM burner, or 
 flashed firmware, you know that someone can write to it. I've changed 
 readonly memory, I've never changed a constant.
 The concept of 'read-only' access is pretty widely understood.

I dunno about that. Yes if you're talking about physical constants or constants like Pi, they never ever change. But 'constant' is also used frequently in mathematics to mean something that's simply "not variable in the current context". Like when doing partial differentiation with respect to y, all variables besides y are treated as constants.

I discussed that already. The proposed 'const' is variable in the current context, since (for example) another thread can change it. If you read a value described as 'const', then read it again later, it is not safe to assume that it has not changed. Therefore, it is not constant in any meaningful sense.
 Also your definition of constant (i.e. something like Pi which 
 absolutely cannot change) seems to be a more a replacement for the 
 proposed 'final' as in 'final Pi = 3.14;' than for a reference whose 
 referent cannot change.

True. I just can't see much use for 'const' as a keyword.
 So personally I'm of the opinion we should pick two words that make at 
 least a modicum of sense in English, forget about what they might mean 
 in C++ or Java or whatever, make sure the common one is short, easy to 
 type, and easy on the eyes, and just go with it.
 
 --bb

Mar 22 2007
prev sibling parent James Dennett <jdennett acm.org> writes:
Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Don Clugston wrote:
 Assuming that 'invariant' = really constant, 'const' = C++
 pseudo-const...

Yah.
 It's better than super const. However:
 (1) I would want to go through all my existing D code and change 100%
 of my usages of 'const' to 'invariant'.

Great to hear that!

Sorry, I didn't explain myself very well. I meant that this proposal breaks 100% of all existing D uses of const. >> (3) I concede the problem of association with 'readonly' and ROM. But
 the association between 'const' and 'constant' is pretty strong.

I think there's universal agreement that you can't express tersely the two notions "const-as-in-C++" and "const-as-in-we-really-mean-it". Probably it's best to just go with two terms and move on.

I completely agree with this. The gripe I have is that 'const' is a completely inappropriate term for 'const-as-in-C++'. It's a bizarre misnomer to use 'const' to mean "don't touch".

Indeed, and seems to represent a misunderstand of what const *does* mean in C++. In this context: int const x(3); it means "x is constant, i.e., cannot be changed". In this context: int const *y(&x); it means "y is a pointer to a (possibly constant) int". Without the const, y would not be allowed to point to a constant int. It doesn't mean "don't touch" -- it means "cannot be used to alter", i.e., "leaves const if indeed it was const". It's closer to "won't touch". It's clear though that the meaning confuses too many people. What's not clear to me is whether the proposed terminology for D is any better. Certainly much of the discussion seems to be based on misstatements of how C++ works. There's scope to do better than C++, but it does require understanding that on which we hope to improve. -- James
Mar 20 2007
prev sibling parent Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribi:
 Andrei Alexandrescu (See Website For Email) wrote:
 kris wrote:
 So, "invariant" is already a keyword ... what about that?

as a candidate for a qualifier name. Thank you.

I agree. I think: final const invariant for the three cases looks pretty good.

I haven't finished reading everything on this thread yet, so I don't know if this has been asked. Also, I'm failing to fully understand this, but I still have this question: how would the following be understood by the compiler? class A { invariant { int a = 4; float b = -10; } } What is that? A class invariant or two invariant class members? (If the latter even makes sense at all...) -- Carlos Santander Bernal
Mar 19 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Deewiant wrote:
 Benji Smith wrote:
 And the word "super" is now overloaded in two completely unrelated 
 concepts.

Which isn't that rare, see the following link: http://www.prowiki.org/wiki4d/wiki.cgi?LanguageSpecification/KeywordIndex I find the following examples: auto, in, is, mixin, scope, static. I have come to abhor the keyword "static" largely due to the above. Too bad its various uses are unavoidably useful. <g> But I do agree with you, in a way. After writing the following I find my explanation somewhat unclear even to myself, but bear with me. The problem with "super const" is that "super" modifies the other keyword, "const", instead of saying something about the expression/statement/whatever (in this case, the type). Even "static", in all its horror, can be seen as specifying one of "module-level" or "compile-time". The other keywords mentioned above use their English meaning as such an explanation, and so can be understood without too much thought. "super const" would make "super" another "static": lacking context, you can't be sure about about even the general meaning, let alone the one in a specific instance. Upon reflection it may be moot, since one probably rarely cares about keywords' meanings without context, but I'm sure some psychologist could come up with something about intuitiveness which affects coding speed, or whatever.

Working in NLP myself, I totally disagree. Natural language is rife with homonimy and synonimy, and we're all very, very happy with that. But in this case we don't even have to go that far: "super" is an adjective meaning primarily "of high grade or quality" and qualifies a noun. If there's no noun, you don't know what is "of high grade or quality". It's a fact, and we don't even blink. The reality behind super const is that it expresses a stronger form of const. It makes a lot of sense if I dub it "super const" in natural language, to express it in the same way in the programming language. I thought that that's too verbose and devised "const!" as a shortcut (again with obvious conotations in natural language), but I'm thinking of dropping it and let type inference add "super" to "const" whenever it can. That should ensure exactness of the type system without verbosity in source code.
 I'd be happiest to just get the feature as soon as possible; worrying 
 about
 keyword semantics can come later. Just like the abominations beginning 
 with
 "on_scope_". <g>

Great idea. :o) Andrei

If you just want something that sounds like more const than const using existing keywords, there are other options: 'true const' 'double const' 'finally const' 'is const' or 'is finally super double true const' I kinda like 'true const' myself. Sounds less dorky than super const to me. :-) --bb
Mar 16 2007
prev sibling next sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 We've shoved keywords like "readonly" and "view" for a while, and they 
 just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be fine 
 to just encode it as the mildly verbose "super const". In fact, it's 
 shorter than "keepyourgrubbyhandsoff". :o)

 Andrei

Really? I'd think super const would be used all the time. Anywhere a class has some invariant field, it'll probably be expressed as super const (if I'm understanding correctly that super const is the equivalent of #define constants in C++ or static final constants in Java). Let me join the ranks of those who hate both versions of the new syntax. const! looks either like a template or like "not const". You guys may think you're saving a keyword, and that might be technically correct from the perspective of the parser. But, for the perspective of the D developer, it still looks like you've introduced a new keyword, but it's a keyword that's A) nearly identical to another keyword, and B) contains a symbol. Ugh. I really. Really. Really. Hate it. Using "super const" is also annoying because all of the other storage classes use a single keyword. Now we've got this weird case that uses two keywords. And the word "super" is now overloaded in two completely unrelated concepts. (By the way, I don't like "static if" either. I think the right choice would have been #if, #foreach, etc. And I think C++ made a mistake by defining "long long" and "double double" types as well. Presumably, that saved some keywords, but we'd all have been better off if the types were defined as int8, int16, int32, int64, float32, float64, etc). I vote for "readonly" and "const". Anything else seems like a mistake.

I think that sums up my take on this pretty well. Thanks for saving me the typing :).
Mar 16 2007
prev sibling next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
I should also add that, in my opinion, any design where "const" means 
something other than "value resolved at compile time and immutable at 
runtime" would be a mistake.

(I'm not crazy about using "const" to mean "value resolved at runtime, 
and immutable after its first assignment", but I don't hate it either.)

--benji
Mar 16 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away? Andrei
Mar 16 2007
next sibling parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away?

there are explicit names for that sort of thing; "immutable", "readonly" , "view" etc. Reading through the various replies gives the impression that you're concerned about propogating the broken C++ semantics than ensuring D corrects those faults ;) I think it's worthwhile ensuring D gets it right instead, and I'll happily change all of Tango to reflect that if necessary. Breaking source-code for a keyword that is currently used rarely is not a problem. Incorrect or dubious/confusing semantics are. It may be worthwhile dumping the "const" keyword altogether, to limit confusion. BTW, you've also mentioned changing "inout" to "ref" instead? That would probably break more code than a renaming of const; I know both Tango and Mango use inout quite a bit, but I'm happy to change those as necessary. Other people likely feel the same way. - Kris
Mar 16 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:

 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away?

there are explicit names for that sort of thing; "immutable", "readonly" , "view" etc. Reading through the various replies gives the impression that you're concerned about propogating the broken C++ semantics than ensuring D corrects those faults ;) I think it's worthwhile ensuring D gets it right instead, and I'll happily change all of Tango to reflect that if necessary. Breaking source-code for a keyword that is currently used rarely is not a problem. Incorrect or dubious/confusing semantics are. It may be worthwhile dumping the "const" keyword altogether, to limit confusion. BTW, you've also mentioned changing "inout" to "ref" instead? That would probably break more code than a renaming of const; I know both Tango and Mango use inout quite a bit, but I'm happy to change those as necessary. Other people likely feel the same way.

++vote For what it's worth, I like: 'final', 'view', and 'const' the best. They're all short and meaningful, given the intent for each. Sean
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.

The problem with 'view' is that it's a noun, while final and const are adjectives. It's very awkward to use 'view': view int * p = &x; // huh? Andrei
Mar 16 2007
next sibling parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.

The problem with 'view' is that it's a noun, while final and const are adjectives. It's very awkward to use 'view': view int * p = &x; // huh?

yuh, fair point :(
Mar 16 2007
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 14:03:13 -0700, Andrei Alexandrescu (See Website For
Email) wrote:

 Sean Kelly wrote:
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.

The problem with 'view' is that it's a noun,

As in "to view or not to view, that is the question" <g>
 while final and const are adjectives. 

So how does the argument from NLP about 'super' being used to qualify nouns sound now?
 It's very awkward to use 'view':
 view int * p = &x; // huh?

But this isn't ... ? super const int * p = &x; How about ... const int * const p = &x; -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Fri, 16 Mar 2007 14:03:13 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 
 Sean Kelly wrote:
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.


As in "to view or not to view, that is the question" <g>
 while final and const are adjectives. 

So how does the argument from NLP about 'super' being used to qualify nouns sound now?

I must not see the connection. "super const int" is a suggestive collocation; "view int" is not.
 It's very awkward to use 'view':
 view int * p = &x; // huh?

But this isn't ... ? super const int * p = &x; How about ... const int * const p = &x;

That's final and expresses something entirely different. Andrei
Mar 16 2007
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.

The problem with 'view' is that it's a noun, while final and const are adjectives. It's very awkward to use 'view': view int * p = &x; // huh?

It's probably not terribly helpful, but 'view' is also a verb. Sean
Mar 16 2007
prev sibling next sibling parent reply kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.

The problem with 'view' is that it's a noun, while final and const are adjectives. It's very awkward to use 'view': view int * p = &x; // huh?

How about this? Using existing keywords, with a rename of "const": 1) final - rebinding of a value to a name 2) invariant - a read-only view of a data structure 3) constant - a value that never changes
Mar 16 2007
next sibling parent Benji Smith <dlanguage benjismith.net> writes:
kris wrote:
 How about this? Using existing keywords, with a rename of "const":
 
 1) final       - rebinding of a value to a name
 2) invariant   - a read-only view of a data structure
 3) constant    - a value that never changes

Actually, that's pretty good too. Getting rid of the keyword "const" would definitely highlight that something is different between D's const-ness and the const-ness in other (anachronistic ;-) ) languages. --benji
Mar 16 2007
prev sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
kris wrote:
 How about this? Using existing keywords, with a rename of "const":
 
 1) final       - rebinding of a value to a name

 3) constant    - a value that never changes

These two are fine by me.
 2) invariant   - a read-only view of a data structure

No. Just No. For one thing, the data structure can vary :) : anyone with a mutable reference can change the value from under your nose, so "does not vary" simply isn't applicable here. But more importantly, invariant has a very clear and defined meaning in computer science. It should not be overloaded like this.
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 kris wrote:
 How about this? Using existing keywords, with a rename of "const":

 1) final       - rebinding of a value to a name

 3) constant    - a value that never changes

These two are fine by me. > 2) invariant - a read-only view of a data structure No. Just No. For one thing, the data structure can vary :) : anyone with a mutable reference can change the value from under your nose, so "does not vary" simply isn't applicable here. But more importantly, invariant has a very clear and defined meaning in computer science. It should not be overloaded like this.

Much of the terminological problem is that it's hard to express const's imprecision (as opposed to super const's precision) tersely. One of my ideas was using "const" for truly set in stone, and using "const?" to mean that the object may or may not have originated as const. But code with question marks everywhere would look odd. Andrei
Mar 16 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.

The problem with 'view' is that it's a noun, while final and const are adjectives. It's very awkward to use 'view': view int * p = &x; // huh? Andrei

And 'view' is also probably a very popular name for a variable thanks to things like the "Model-View-Controller" paradigm. http://www.google.com/codesearch?q=view&hl=en&btnG=Search+Code --bb
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 For what it's worth, I like: 'final', 'view', and 'const' the best. 
 They're all short and meaningful, given the intent for each.

The problem with 'view' is that it's a noun, while final and const are adjectives. It's very awkward to use 'view': view int * p = &x; // huh? Andrei

And 'view' is also probably a very popular name for a variable thanks to things like the "Model-View-Controller" paradigm. http://www.google.com/codesearch?q=view&hl=en&btnG=Search+Code

However, viewonly is a possibility that's particularly expressive. As much as I'm wary of collapsed words, this is the contender I like the most. It's a bit long though, and given that it's also the most heavily used, it would lead to long lines. But, as Walter put it very wisely: addressing immutability is a big problem, and syntax is only a fraction of it. Andrei
Mar 16 2007
prev sibling next sibling parent reply Walter Bright <newshound digitalmars.com> writes:
kris wrote:
 BTW, you've also mentioned changing "inout" to "ref" instead? That would 
 probably break more code than a renaming of const; I know both Tango and 
 Mango use inout quite a bit, but I'm happy to change those as necessary. 
 Other people likely feel the same way.

Changing inout to ref would be a very long process in several stages: 1) Adding ref as a keyword and documenting its usage 2) Recommending changing usage from 'inout' to 'ref' 3) Removing 'inout' from the documentation 4) Producing a warning on 'inout' with -w switch 5) Deprecating 'inout' 6) Removing 'inout' as keyword Also, 'inout' is easily greppable, and very likely is unique enough that using nothing more than a global search/replace will work.
Mar 16 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 kris wrote:
 BTW, you've also mentioned changing "inout" to "ref" instead? That 
 would probably break more code than a renaming of const; I know both 
 Tango and Mango use inout quite a bit, but I'm happy to change those 
 as necessary. Other people likely feel the same way.

Changing inout to ref would be a very long process in several stages: 1) Adding ref as a keyword and documenting its usage 2) Recommending changing usage from 'inout' to 'ref' 3) Removing 'inout' from the documentation 4) Producing a warning on 'inout' with -w switch 5) Deprecating 'inout' 6) Removing 'inout' as keyword Also, 'inout' is easily greppable, and very likely is unique enough that using nothing more than a global search/replace will work.

I will notice that that process didn't start for the infamous magic variable "length". It's still in http://digitalmars.com/d/expression.html :o(. Andrei
Mar 16 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 kris wrote:
 BTW, you've also mentioned changing "inout" to "ref" instead? That 
 would probably break more code than a renaming of const; I know both 
 Tango and Mango use inout quite a bit, but I'm happy to change those 
 as necessary. Other people likely feel the same way.

Changing inout to ref would be a very long process in several stages: 1) Adding ref as a keyword and documenting its usage 2) Recommending changing usage from 'inout' to 'ref' 3) Removing 'inout' from the documentation 4) Producing a warning on 'inout' with -w switch 5) Deprecating 'inout' 6) Removing 'inout' as keyword Also, 'inout' is easily greppable, and very likely is unique enough that using nothing more than a global search/replace will work.

Why not just stop at '1'? Inout is very descriptive if what you're planning to do is modify the input in a way that the caller can see the changes. Just make it a synonym for 'ref'. And instead of telling people not to use it, tell them to use it only when inout behavior is the actual intent. Otherwise use 'const ref'. And make 'const inout' be an error. But in terms of the syntax tree generated, both 'inout' and 'ref' will be 'ref's. --bb
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 kris wrote:
 BTW, you've also mentioned changing "inout" to "ref" instead? That 
 would probably break more code than a renaming of const; I know both 
 Tango and Mango use inout quite a bit, but I'm happy to change those 
 as necessary. Other people likely feel the same way.

Changing inout to ref would be a very long process in several stages: 1) Adding ref as a keyword and documenting its usage 2) Recommending changing usage from 'inout' to 'ref' 3) Removing 'inout' from the documentation 4) Producing a warning on 'inout' with -w switch 5) Deprecating 'inout' 6) Removing 'inout' as keyword Also, 'inout' is easily greppable, and very likely is unique enough that using nothing more than a global search/replace will work.

Why not just stop at '1'? Inout is very descriptive if what you're planning to do is modify the input in a way that the caller can see the changes. Just make it a synonym for 'ref'. And instead of telling people not to use it, tell them to use it only when inout behavior is the actual intent. Otherwise use 'const ref'. And make 'const inout' be an error.

That's very awkward. Exact synonyms fare real bad in all languages I know. It's chaff without any nutritional benefit. Everybody would have preferred that "=>" in Perl is something different from ",", or that template<typename T> and template<class T> would be different things in C++. Andrei
Mar 16 2007
prev sibling next sibling parent Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 11:48:47 -0700, kris wrote:

 ... but I'm happy to change those as necessary. 
 Other people likely feel the same way.

I am more than happy to change my source code if the result is easier to read and an improved D. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
prev sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
kris wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:

 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away?

there are explicit names for that sort of thing; "immutable", "readonly" , "view" etc. Reading through the various replies gives the impression that you're concerned about propogating the broken C++ semantics than ensuring D corrects those faults ;)

No, just no gratuitous incompatibilities and misunderstanding. For example, "readonly" reminds some of ROM, which is really immutable. Then, "view" is too common a word to steal it as a keyword. Finally, "immutable" is again ambiguous.
 I think it's worthwhile ensuring D gets it right instead, and I'll 
 happily change all of Tango to reflect that if necessary. Breaking 
 source-code for a keyword that is currently used rarely is not a 
 problem. Incorrect or dubious/confusing semantics are. It may be 
 worthwhile dumping the "const" keyword altogether, to limit confusion.

You know, that might be a good idea.
 BTW, you've also mentioned changing "inout" to "ref" instead? That would 
 probably break more code than a renaming of const; I know both Tango and 
 Mango use inout quite a bit, but I'm happy to change those as necessary. 
 Other people likely feel the same way.

Thanks, that's good to hear. Dropping inout would be one of the harder decisions to make. Andrei
Mar 16 2007
prev sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away? Andrei

I have no special love (nor contempt, of course) for C++ immigrants and don't really care whether D conforms to their expectations. I thought the point of D was to fix broken constructs from C++, and most C++ programmers I know consider its implementation of const (and especially how casting affects const) to be one of the prime offenders. --benji
Mar 16 2007
next sibling parent Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 12:38:00 -0700, Benji Smith wrote:

 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away? Andrei

I have no special love (nor contempt, of course) for C++ immigrants and don't really care whether D conforms to their expectations. I thought the point of D was to fix broken constructs from C++, and most C++ programmers I know consider its implementation of const (and especially how casting affects const) to be one of the prime offenders.

Thank you Benji ... I've been trying to write a response to that but everything I wrote came out as too abusive or obnoxious. You have nailed it though. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
prev sibling next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away? Andrei

I have no special love (nor contempt, of course) for C++ immigrants and don't really care whether D conforms to their expectations.

Well, you see - quot capita, tot sententiae. :o) It's not about expectations as much as taking the good without the bad, while avoiding gratuitous incompatibilities. This is, I understand, high on Walter's priority list.
 I thought the point of D was to fix broken constructs from C++, and most 
 C++ programmers I know consider its implementation of const (and 
 especially how casting affects const) to be one of the prime offenders.

We know why C++ const is broken. The fact that it does not mean super const is not the reason. Andrei
Mar 16 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away? Andrei

I have no special love (nor contempt, of course) for C++ immigrants and don't really care whether D conforms to their expectations. I thought the point of D was to fix broken constructs from C++, and most C++ programmers I know consider its implementation of const (and especially how casting affects const) to be one of the prime offenders. --benji

Same opinion here. What makes D good is fixing broken constructs from C++. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" 
 means something other than "value resolved at compile time and 
 immutable at runtime" would be a mistake.

Then how would you call "value that's not mutable through this alias" in a way that's not going to turn C++ immigrants away? Andrei

I have no special love (nor contempt, of course) for C++ immigrants and don't really care whether D conforms to their expectations. I thought the point of D was to fix broken constructs from C++, and most C++ programmers I know consider its implementation of const (and especially how casting affects const) to be one of the prime offenders. --benji

Same opinion here. What makes D good is fixing broken constructs from C++.

Same opinion here too. Andrei
Mar 16 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

I hear you. I've always disliked C++'s mutable const. The most important thing at this stage is not the keyword, however, but getting the design right. Andrei, myself and Bartosz have been to hell and back several times over this over the last few months, and have filled about two notebooks with scribblings. It's a difficult problem. There are 3 distinct, and very different, flavors of constant: 1) rebinding of a value to a name 2) a read-only view of a data structure 3) a value that never changes C++ tries to do all three with one keyword, and makes a confusing hash of it. For the purposes of discussion, and to avoid confusing ourselves, we adopted working names of: 1) final 2) const 3) super const The hard part is to figure out how these 3 interact, how they look & feel, how they work with type deduction, type inference, type combining, how do we sidestep the clumsiness in C++'s handling of it, etc. Once that's all done, picking the right keywords is a whole 'nother discussion, with several considerations: 1) common use of 'const' in C++, and the expectations and comfort people coming from C++ have with it 2) your point above, that const should be constant 3) keyword minimization 4) avoid stepping on commonly used names 5) ease and economy of typing
Mar 16 2007
next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Walter Bright wrote:
 There are 3 distinct, and very different, flavors of constant:
 
 1) rebinding of a value to a name
 2) a read-only view of a data structure
 3) a value that never changes
 
 C++ tries to do all three with one keyword, and makes a confusing hash 
 of it.
 
 For the purposes of discussion, and to avoid confusing ourselves, we 
 adopted working names of:
 1) final
 2) const
 3) super const

I appreciate the thought you guys have put into it. And the implementation sounds exactly right, from a semantic perspective. It's just the syntax that's slightly wrong, to me. Using your mapping above, I'd tweak it slightly to read: 1) final 2) readonly 3) const On a side note: if the existing proposal was enacted, I think the exclamation point would be the most commonly overloaded symbol (possibly only competing with * for multiplication, pointer declaration, and pointer dereferencing), while other symbols (like ^ or %) are currently only single-purpose. Any overriding philosophy on the usage and overloading of symbol characters? --benji
Mar 16 2007
parent Walter Bright <newshound digitalmars.com> writes:
Benji Smith wrote:
 Any overriding philosophy on the usage and
 overloading of symbol characters?

Not that I can think of.
Mar 16 2007
prev sibling next sibling parent kris <foo bar.com> writes:
Walter Bright wrote:
 Benji Smith wrote:
 
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

I hear you. I've always disliked C++'s mutable const. The most important thing at this stage is not the keyword, however, but getting the design right. Andrei, myself and Bartosz have been to hell and back several times over this over the last few months, and have filled about two notebooks with scribblings. It's a difficult problem. There are 3 distinct, and very different, flavors of constant: 1) rebinding of a value to a name 2) a read-only view of a data structure 3) a value that never changes C++ tries to do all three with one keyword, and makes a confusing hash of it. For the purposes of discussion, and to avoid confusing ourselves, we adopted working names of: 1) final 2) const 3) super const The hard part is to figure out how these 3 interact, how they look & feel, how they work with type deduction, type inference, type combining, how do we sidestep the clumsiness in C++'s handling of it, etc. Once that's all done, picking the right keywords is a whole 'nother discussion, with several considerations: 1) common use of 'const' in C++, and the expectations and comfort people coming from C++ have with it 2) your point above, that const should be constant 3) keyword minimization 4) avoid stepping on commonly used names 5) ease and economy of typing

Glad to hear you guys are open to considerations. For purpose of discussion, you /could/ adopt the following instead: 1) final - rebinding of a value to a name 2) view - a read-only view of a data structure 3) constant - a value that never changes Not only is that pretty clear, it also dumps the "const" keyword that may otherwise cause some "adjustment" for C++ people
Mar 16 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

I hear you. I've always disliked C++'s mutable const. The most important thing at this stage is not the keyword, however, but getting the design right. Andrei, myself and Bartosz have been to hell and back several times over this over the last few months, and have filled about two notebooks with scribblings. It's a difficult problem.

I very much agree. But with little information about how these new flavors of const will work, it seems we're mostly stuck debating how they will look :-) I'd welcome discussing the former issue, but I suspect that anything which is said here will have already been discussed between you three.
 There are 3 distinct, and very different, flavors of constant:
 
 1) rebinding of a value to a name
 2) a read-only view of a data structure
 3) a value that never changes

It seems like #1 is only useful for reference types, which currently means pointers and class handles. Further, I don't think I've ever found a need for this particular class of immutability in C++. How often does it really matter whether someone can reassign a pointer? As for the other two, it seems like #3 is a storage class while #2 is an attribute on the variable itself. So #3 implies #2. I haven't given this much thought, but is it truly necessary to have two separate keywords for #2 and #3? Is this to aid the compiler in optimizing, or is there a perceived value here from a user perspective? Please don't get me wrong, I very much appreciate the desire for a comprehensive solution, but at the same time I wonder whether the struggle to integrate three fairly diverse concepts might be part of the reason why this all seems so complicated. Are they all truly necessary? And is it necessary to distinguish all three by separate keywords?
 C++ tries to do all three with one keyword, and makes a confusing hash 
 of it.

Agreed, despite how the above likely sounds. Sean
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" means 
 something other than "value resolved at compile time and immutable at 
 runtime" would be a mistake.

I hear you. I've always disliked C++'s mutable const. The most important thing at this stage is not the keyword, however, but getting the design right. Andrei, myself and Bartosz have been to hell and back several times over this over the last few months, and have filled about two notebooks with scribblings. It's a difficult problem.

I very much agree. But with little information about how these new flavors of const will work, it seems we're mostly stuck debating how they will look :-) I'd welcome discussing the former issue, but I suspect that anything which is said here will have already been discussed between you three.
 There are 3 distinct, and very different, flavors of constant:

 1) rebinding of a value to a name
 2) a read-only view of a data structure
 3) a value that never changes

It seems like #1 is only useful for reference types, which currently means pointers and class handles.

#1 is essential for expressing numeric constants.
 Further, I don't think I've ever 
 found a need for this particular class of immutability in C++.  How 
 often does it really matter whether someone can reassign a pointer?

Very important. Often you want to bind a pointer to something and make sure nothing can change that.
 As for the other two, it seems like #3 is a storage class

No, because the value may embed pointers that you want to pass to functions. Sorry for sounding dismissive, but we've been all of these dead alleys for so long, it's all green codes: I can see the damn Matrix now. Blonde, brunette, const, final...
 while #2 is an 
 attribute on the variable itself.  So #3 implies #2.

No. #3 implies #2. If a value is super const, then it can be seen as const. Essentially "const" loses a little of the precision that "super const" has.
 I haven't given 
 this much thought, but is it truly necessary to have two separate 
 keywords for #2 and #3?

It's not! That's what I tried to do all along...
 Is this to aid the compiler in optimizing, or 
 is there a perceived value here from a user perspective?

It will be absolutely essential for the well-definedness of functional-style threads.
 Please don't get me wrong, I very much appreciate the desire for a 
 comprehensive solution, but at the same time I wonder whether the 
 struggle to integrate three fairly diverse concepts might be part of the 
 reason why this all seems so complicated.  Are they all truly necessary? 

I'd think so.
  And is it necessary to distinguish all three by separate keywords?

It's not necessary :o). Andrei
Mar 16 2007
parent reply Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" 
 means something other than "value resolved at compile time and 
 immutable at runtime" would be a mistake.

I hear you. I've always disliked C++'s mutable const. The most important thing at this stage is not the keyword, however, but getting the design right. Andrei, myself and Bartosz have been to hell and back several times over this over the last few months, and have filled about two notebooks with scribblings. It's a difficult problem.

I very much agree. But with little information about how these new flavors of const will work, it seems we're mostly stuck debating how they will look :-) I'd welcome discussing the former issue, but I suspect that anything which is said here will have already been discussed between you three.
 There are 3 distinct, and very different, flavors of constant:

 1) rebinding of a value to a name
 2) a read-only view of a data structure
 3) a value that never changes

It seems like #1 is only useful for reference types, which currently means pointers and class handles.

#1 is essential for expressing numeric constants.

Could you please provide an example?
 Further, I don't think I've ever found a need for this particular 
 class of immutability in C++.  How often does it really matter whether 
 someone can reassign a pointer?

Very important. Often you want to bind a pointer to something and make sure nothing can change that.

Perhaps this should be rewritten as "often one wants to..." I just said that I have never felt a need for this :-) Of the times where I've used this feature in C/C++, I think every single time has been simply out of diligence rather than because there was any risk of the pointer actually being reassigned. A pertinent point to me is that D uses pass-by-value so the chance of a reference being silently reassigned "somewhere in the program" is typically almost nil. About the only place where this is a real risk is with global variables, and since free functions support the property syntax, the programmer has a ready option to prevent such a reassignment from happening. Besides, free globals are bad programming style anyway.
 As for the other two, it seems like #3 is a storage class

No, because the value may embed pointers that you want to pass to functions. Sorry for sounding dismissive, but we've been all of these dead alleys for so long, it's all green codes: I can see the damn Matrix now. Blonde, brunette, const, final...

That's fine. You may have been down all these roads before, but I haven't. I'm mostly asking to gain a better understanding of some of the background.
 while #2 is an attribute on the variable itself.  So #3 implies #2.

No. #3 implies #2. If a value is super const, then it can be seen as const. Essentially "const" loses a little of the precision that "super const" has.

I'm confused. I said "so #3 imples #2" and you replied "no, #3 implies #2." What does the "no" signify?
 I haven't given this much thought, but is it truly necessary to have 
 two separate keywords for #2 and #3?

It's not! That's what I tried to do all along...

(see below)
 Is this to aid the compiler in optimizing, or is there a perceived 
 value here from a user perspective?

It will be absolutely essential for the well-definedness of functional-style threads.

Okay, that's what I figured, though this seems to contradict what you said above. So in essence, "super const" is actually necessary in some situations to indicate to the programmer that the data he is manipulating is truly immutable. I almost feel like this feature should exist separately from the type mechanism, so there was a way to test for this instead.
  And is it necessary to distinguish all three by separate keywords?

It's not necessary :o).

So it sounds like it truly isn't necessary to separately distinguish #2 and #3 via type qualifiers. Is this correct? I can appreciate the need for both, but I very much do not want all of this to become a confusing mess when applied to actual code. Sean
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 Benji Smith wrote:
 I should also add that, in my opinion, any design where "const" 
 means something other than "value resolved at compile time and 
 immutable at runtime" would be a mistake.

I hear you. I've always disliked C++'s mutable const. The most important thing at this stage is not the keyword, however, but getting the design right. Andrei, myself and Bartosz have been to hell and back several times over this over the last few months, and have filled about two notebooks with scribblings. It's a difficult problem.

I very much agree. But with little information about how these new flavors of const will work, it seems we're mostly stuck debating how they will look :-) I'd welcome discussing the former issue, but I suspect that anything which is said here will have already been discussed between you three.
 There are 3 distinct, and very different, flavors of constant:

 1) rebinding of a value to a name
 2) a read-only view of a data structure
 3) a value that never changes

It seems like #1 is only useful for reference types, which currently means pointers and class handles.

#1 is essential for expressing numeric constants.

Could you please provide an example?

final int x = 42;
 Further, I don't think I've ever found a need for this particular 
 class of immutability in C++.  How often does it really matter 
 whether someone can reassign a pointer?

Very important. Often you want to bind a pointer to something and make sure nothing can change that.

Perhaps this should be rewritten as "often one wants to..." I just said that I have never felt a need for this :-) Of the times where I've used this feature in C/C++, I think every single time has been simply out of diligence rather than because there was any risk of the pointer actually being reassigned. A pertinent point to me is that D uses pass-by-value so the chance of a reference being silently reassigned "somewhere in the program" is typically almost nil. About the only place where this is a real risk is with global variables, and since free functions support the property syntax, the programmer has a ready option to prevent such a reassignment from happening. Besides, free globals are bad programming style anyway.

I think "final" is mostly useful in class and struct members.
 As for the other two, it seems like #3 is a storage class

No, because the value may embed pointers that you want to pass to functions. Sorry for sounding dismissive, but we've been all of these dead alleys for so long, it's all green codes: I can see the damn Matrix now. Blonde, brunette, const, final...

That's fine. You may have been down all these roads before, but I haven't. I'm mostly asking to gain a better understanding of some of the background.
 while #2 is an attribute on the variable itself.  So #3 implies #2.

No. #3 implies #2. If a value is super const, then it can be seen as const. Essentially "const" loses a little of the precision that "super const" has.

I'm confused. I said "so #3 imples #2" and you replied "no, #3 implies #2." What does the "no" signify?

Sorry, I misread your sentence as #2 implies #3.
 I haven't given this much thought, but is it truly necessary to have 
 two separate keywords for #2 and #3?

It's not! That's what I tried to do all along...

(see below)
 Is this to aid the compiler in optimizing, or is there a perceived 
 value here from a user perspective?

It will be absolutely essential for the well-definedness of functional-style threads.

Okay, that's what I figured, though this seems to contradict what you said above. So in essence, "super const" is actually necessary in some situations to indicate to the programmer that the data he is manipulating is truly immutable. I almost feel like this feature should exist separately from the type mechanism, so there was a way to test for this instead.

Then you can't prove correctness statically.
  And is it necessary to distinguish all three by separate keywords?

It's not necessary :o).

So it sounds like it truly isn't necessary to separately distinguish #2 and #3 via type qualifiers. Is this correct? I can appreciate the need for both, but I very much do not want all of this to become a confusing mess when applied to actual code.

Much anything else would become a mess when applied to actual code. I think we discussed about two dozens of schemes. They all looked great on paper, at least at first blush. They all did miserably during a test drive. Andrei
Mar 16 2007
parent Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 There are 3 distinct, and very different, flavors of constant:

 1) rebinding of a value to a name
 2) a read-only view of a data structure
 3) a value that never changes

It seems like #1 is only useful for reference types, which currently means pointers and class handles.

#1 is essential for expressing numeric constants.

Could you please provide an example?

final int x = 42;

I must be missing something here. I expected this to be the point of 'const'. In C++ parlance: int const * const val = 0; ^ ^ | | #2 #1
 I think "final" is mostly useful in class and struct members.

Seems reasonable I suppose. Will there be any change to "final" as it exists now in D, or is this simply an extension of its meaning?
 Okay, that's what I figured, though this seems to contradict what you 
 said above.  So in essence, "super const" is actually necessary in 
 some situations to indicate to the programmer that the data he is 
 manipulating is truly immutable.  I almost feel like this feature 
 should exist separately from the type mechanism, so there was a way to 
 test for this instead.

Then you can't prove correctness statically.

Argh. Good point.
 So it sounds like it truly isn't necessary to separately distinguish 
 #2 and #3 via type qualifiers.  Is this correct?  I can appreciate the 
 need for both, but I very much do not want all of this to become a 
 confusing mess when applied to actual code.

Much anything else would become a mess when applied to actual code. I think we discussed about two dozens of schemes. They all looked great on paper, at least at first blush. They all did miserably during a test drive.

Well... darnit :-) Sean
Mar 16 2007
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 For the purposes of discussion, and to avoid confusing ourselves, we 
 adopted working names of:
 1) final
 2) const
 3) super const

How about taking Andrei's observation about extern(C) and scope(exit) and letting const have a modifier? 1) final 2) const 3) const(strong) or something like that? I guess the problem is that const(something) already has a meaning if it's something like const(int*). --bb
Mar 16 2007
prev sibling parent Geoff Carlton <gcarlton iinet.net.au> writes:
Walter Bright wrote:
 
 There are 3 distinct, and very different, flavors of constant:
 
 1) rebinding of a value to a name
 2) a read-only view of a data structure
 3) a value that never changes
 
 C++ tries to do all three with one keyword, and makes a confusing hash 
 of it.
 
 For the purposes of discussion, and to avoid confusing ourselves, we 
 adopted working names of:
 1) final
 2) const
 3) super const
 

Well, I'm a C++ programmer and I think the last two are much more confusing than "readonly" and a different "const". If they keywords are simple and intuitive then the learning curve will be quick. Can't get much better than "readonly" (I can't write to this data, but it may change) and "const" (the data will not change). Consider these two statements: The problem with C++ "const" is that it isn't really constant. D fixes this by having "readonly" for an immutable view, and "const" actually meaning that the data is guaranteed not going to change. The problem with C++ "const" is that it isn't really constant. D's "const" is also not really constant, but "super const" has been added for data that is guaranteed not going to change. Also, I am slightly confused why examples of final have been given with value types: final int x = 3; But x can never be rebound as it isn't a reference - what the user really means is the value never changes, therefore keyword (3) rather than keyword (1) seems appropriate here.
 The hard part is to figure out how these 3 interact, how they look & 
 feel, how they work with type deduction, type inference, type combining, 
 how do we sidestep the clumsiness in C++'s handling of it, etc.
 

Regardless of syntax, I would love to hear the advantages of the proposed system. It seems the really big change is the addition of (3), but I can't quite see how you could create complex data sets that are guaranteed constant apart from using "trust me" casts. For example, an function may take a data set as super const, therefore making optimisations. But the creation of such a data set (say a map of ints to an array of strings, or a graph of nodes) may well be done at runtime, such as loading it from a file. How do you "pin" runtime data to get a super constant type? Can you even create super const data using custom classes? Must everything be done in the constructor? What about pointers to non-owned non-constant data, such as back to an owner object? I'm intrigued. Any chance of posting some "behind the scenes" with the discussion and resolution to the issues you guys have grappled with?
Mar 17 2007
prev sibling next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 We've shoved keywords like "readonly" and "view" for a while, and they 
 just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be fine 
 to just encode it as the mildly verbose "super const". In fact, it's 
 shorter than "keepyourgrubbyhandsoff". :o)

 Andrei

Really? I'd think super const would be used all the time. Anywhere a class has some invariant field, it'll probably be expressed as super const (if I'm understanding correctly that super const is the equivalent of #define constants in C++ or static final constants in Java).

No. super const deals with pointers and transitivity. Final deals with non-rebindable symbols. I'd be hard pressed to think of many examples where class members are transitively immutable.
 Let me join the ranks of those who hate both versions of the new syntax. 
  const! looks either like a template or like "not const". You guys may 
 think you're saving a keyword, and that might be technically correct 
 from the perspective of the parser.

The parser has zilch to do with it.
 But, for the perspective of the D 
 developer, it still looks like you've introduced a new keyword, but it's 
 a keyword that's A) nearly identical to another keyword, and B) contains 
 a symbol. Ugh. I really. Really. Really. Hate it.

Can't help. For any given feature, there will be people who hate it. Usually they are also the most vocal :o).
 Using "super const" is also annoying because all of the other storage 
 classes use a single keyword. Now we've got this weird case that uses 
 two keywords. And the word "super" is now overloaded in two completely 
 unrelated concepts.

So what? Don't we use words in natural language that are polysemous too? As long as the context makes it unambiguous, great.
 (By the way, I don't like "static if" either. I think the right choice 
 would have been #if, #foreach, etc. And I think C++ made a mistake by 
 defining "long long" and "double double" types as well. Presumably, that 
 saved some keywords, but we'd all have been better off if the types were 
 defined as int8, int16, int32, int64, float32, float64, etc).

You're making my point exactly. I love static if, and don't like #if. Again: for each feature, there will be people who like it and hate it :o).
 I vote for "readonly" and "const". Anything else seems like a mistake.

Which is which? How would you have people distinguish them without running to the manual? How would you explain C++ immigrants that const is actually readonly, and there is a const, but that const means something else than their const? Andrei
Mar 16 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I vote for "readonly" and "const". Anything else seems like a mistake.

Which is which? How would you have people distinguish them without running to the manual? How would you explain C++ immigrants that const is actually readonly, and there is a const, but that const means something else than their const?

How would you have people distinguish char/wchar/dchar without running to the Unicode standard to see which characters they can hold in a single unit? Or short/int/long or float/double/real without running to the standard to look up the ranges and precision of those data types? How would you explain to the average person from the Netherlands/Estonia/Finland/Germany/Slovakia[1] 'auto' doesn't have anything to do with transportation? How would you explain to Dutchmen that 'long' doesn't have anything to do with breathing air? (Hint for all you foreigners out here: the Dutch word 'long' translates to 'lung' :P ) Some things are somewhat arbitrary conventions. When you learn a new language, some words will mean different things, with other words that have the meaning you're used to. It happens ;). [1]: See http://dictionary.reference.com/search?q=car , specifically the first set of translations, roughly halfway down the page Seriously though: "readonly" would mean that you (meaning any code that can access the symbol so declared) can only read data referenced through it, but someone else _may_ be able to write to it[2]. "const" would mean it's just that: constant. *Nobody* can write to it. Seems pretty easy to explain to me. The only caveat I see is that "readonly" would mean the object itself can still be modified, as opposed to final. I don't see how calling it "const" would fix that, though. [2]: Or of course that same code may be able to write to it, if for whatever reason it also has access to a mutable reference to the same data.
Mar 16 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Frits van Bommel wrote:
 Seriously though:
 "readonly" would mean that you (meaning any code that can access the 
 symbol so declared) can only read data referenced through it, but 
 someone else _may_ be able to write to it[2].

My perception of that is different. I used to do embedded systems, and 'readonly' means the data went into ROM. Also, marking a page in a virtual memory system as 'readonly' means that nobody can modify it. 'super const' at least has the connotation of being "constant, this time we mean it".
Mar 16 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Walter Bright wrote:
 Frits van Bommel wrote:
 Seriously though:
 "readonly" would mean that you (meaning any code that can access the 
 symbol so declared) can only read data referenced through it, but 
 someone else _may_ be able to write to it[2].

My perception of that is different. I used to do embedded systems, and 'readonly' means the data went into ROM. Also, marking a page in a virtual memory system as 'readonly' means that nobody can modify it.

Perhaps, but my perception of 'const' is different ;). Particularly, it seems to be short for "constant", which is therefore what it should mean. Not "*you* can't change this, but someone else might anyway". Oh, and on x86 (and amd64) processors the operating system is allowed to write to 'readonly'[1] pages unless it sets the WP (Write-Protect) bit in system register CR0. Note: as this bit is *off* by default, it needs to be explicitly turned on by the OS. [1] Well, technically they're not really readonly, it's more like non-writable (since the appropriate bit must be 1 on all levels of the paging data structures to get a writable page) but that's heading /even faster/ into the realm of nitpicking :).
 'super const' at least has the connotation of being "constant, this time 
 we mean it".

Wouldn't it be better if 'const' just _meant what it said_ in the first place? :P
Mar 16 2007
parent reply Sean Kelly <sean f4.ca> writes:
Frits van Bommel wrote:
 Walter Bright wrote:
 Frits van Bommel wrote:
 Seriously though:
 "readonly" would mean that you (meaning any code that can access the 
 symbol so declared) can only read data referenced through it, but 
 someone else _may_ be able to write to it[2].

My perception of that is different. I used to do embedded systems, and 'readonly' means the data went into ROM. Also, marking a page in a virtual memory system as 'readonly' means that nobody can modify it.

Perhaps, but my perception of 'const' is different ;). Particularly, it seems to be short for "constant", which is therefore what it should mean. Not "*you* can't change this, but someone else might anyway". Oh, and on x86 (and amd64) processors the operating system is allowed to write to 'readonly'[1] pages unless it sets the WP (Write-Protect) bit in system register CR0. Note: as this bit is *off* by default, it needs to be explicitly turned on by the OS.

So *that's* why Win32 doesn't complain when constant data is modified and pretty much every other OS does. Good to know. By the way... I feel the same about the meaning of 'const'. On some level I still even think that const-by-default is the correct choice, but now that D has hit 1.0 that will be a much harder sell. Sean
Mar 16 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 Frits van Bommel wrote:
 Walter Bright wrote:


 'readonly' means the data went into ROM. Also, marking a page in a 
 virtual memory system as 'readonly' means that nobody can modify it.



 Oh, and on x86 (and amd64) processors the operating system is allowed 
 to write to 'readonly'[1] pages unless it sets the WP (Write-Protect) 
 bit in system register CR0. Note: as this bit is *off* by default, it 
 needs to be explicitly turned on by the OS.

So *that's* why Win32 doesn't complain when constant data is modified and pretty much every other OS does. Good to know.

No I don't think so. The 'WP' bit only applies to kernel-level (ring 0-2) code. Ring 3 code (i.e. normal user-level code) can never write into "readonly" pages. The only explanation I can think of for Win32 allowing writes into a programs read-only data is that the pages aren't marked "readonly" at all for some reason. Unless of course user-level code is ran somewhere in ring 0-2, but I don't think even Windows would do something so stupidly insecure: that would allow user-level code to read and write anywhere in kernel space, barring segment limits. (I didn't say Microsoft instead of Windows above, since Microsoft's research division is experimenting with a .NET based OS that *does* run everything in ring 0. However, that uses a different mechanism to ensure safety: programs are checked for type-safety before being allowed to run. That's not really feasible for traditional machine-code based OSs though...)
Mar 16 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I vote for "readonly" and "const". Anything else seems like a mistake.

Which is which? How would you have people distinguish them without running to the manual? How would you explain C++ immigrants that const is actually readonly, and there is a const, but that const means something else than their const?

Hospitality to C++ immigrants is definitely important. But since C++ const is broken, there's inevitably going to be some culture shock. I think we're OK as long as D const is more restrictive than C++ const -- so that writing C++ code in D will either work the same as in C++, or fail to compile. And I don't think that the confusion exists for anyone other than C++ programmers. Don
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Don Clugston wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 I vote for "readonly" and "const". Anything else seems like a mistake.

Which is which? How would you have people distinguish them without running to the manual? How would you explain C++ immigrants that const is actually readonly, and there is a const, but that const means something else than their const?

Hospitality to C++ immigrants is definitely important. But since C++ const is broken, there's inevitably going to be some culture shock.

Let me say it again: we know why it's broken. We will provide a fixed equivalent, not something entirely different. It's only natural to call the fixed equivalent the same.
 I think we're OK as long as D const is more restrictive than C++ const
  -- so that writing C++ code in D will either work the same as in C++, 
 or fail to compile.
 And I don't think that the confusion exists for anyone other than C++ 
 programmers.

Which are in the millions :o). Andrei
Mar 16 2007
prev sibling next sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Really? I'd think super const would be used all the time. Anywhere a 
 class has some invariant field, it'll probably be expressed as super 
 const (if I'm understanding correctly that super const is the 
 equivalent of #define constants in C++ or static final constants in 
 Java).

No. super const deals with pointers and transitivity. Final deals with non-rebindable symbols. I'd be hard pressed to think of many examples where class members are transitively immutable.

Aha. In that case, what would you think of the declaration: super const int MY_CONSTANT = 6; Since a value type doesn't have any pointers, it wouldn't make any sense to apply super-constness to it, right? Should that be a compiler error? --benji
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Really? I'd think super const would be used all the time. Anywhere a 
 class has some invariant field, it'll probably be expressed as super 
 const (if I'm understanding correctly that super const is the 
 equivalent of #define constants in C++ or static final constants in 
 Java).

No. super const deals with pointers and transitivity. Final deals with non-rebindable symbols. I'd be hard pressed to think of many examples where class members are transitively immutable.

Aha. In that case, what would you think of the declaration: super const int MY_CONSTANT = 6; Since a value type doesn't have any pointers, it wouldn't make any sense to apply super-constness to it, right? Should that be a compiler error?

This should be a compiler error. Andrei
Mar 16 2007
parent Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 Aha. In that case, what would you think of the declaration:

   super const int MY_CONSTANT = 6;

 Since a value type doesn't have any pointers, it wouldn't make any 
 sense to apply super-constness to it, right? Should that be a compiler 
 error?

This should be a compiler error. Andrei

Cool. That's what I'd expect. Maybe rather than "super const", you could use "ref const" or "const*" or something that directly indicates that this form of constantness applies to the pointers rather than the values. If you use "ref" as a replacement for "inout", then the "ref" keyword would get some re-use. Actually, though, I kind of like the idea of "const*" // Create a constant pointer from a mutable pointer const* int* myPointer = pMutableInt; // Or maybe like this: *const int* myPointer = pMutableInt; What do you think of that? At least the asterisk provides a better clue to the functionality of the construct than an exclamation point would. --benji
Mar 16 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Benji Smith wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 We've shoved keywords like "readonly" and "view" for a while, and 
 they just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be 
 fine to just encode it as the mildly verbose "super const". In fact, 
 it's shorter than "keepyourgrubbyhandsoff". :o)

Really? I'd think super const would be used all the time. Anywhere a class has some invariant field, it'll probably be expressed as super const (if I'm understanding correctly that super const is the equivalent of #define constants in C++ or static final constants in Java).

No. super const deals with pointers and transitivity. Final deals with non-rebindable symbols. I'd be hard pressed to think of many examples where class members are transitively immutable.

Well this explains a lot. I'd misunderstood as well. Sean
Mar 16 2007
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 09:55:15 -0700, Benji Smith wrote:

 Andrei Alexandrescu (See Website For Email) wrote:
 We've shoved keywords like "readonly" and "view" for a while, and they 
 just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be fine 
 to just encode it as the mildly verbose "super const". In fact, it's 
 shorter than "keepyourgrubbyhandsoff". :o)
 
 Andrei

Really? I'd think super const would be used all the time. Anywhere a class has some invariant field, it'll probably be expressed as super const (if I'm understanding correctly that super const is the equivalent of #define constants in C++ or static final constants in Java). Let me join the ranks of those who hate both versions of the new syntax. const! looks either like a template or like "not const". You guys may think you're saving a keyword, and that might be technically correct from the perspective of the parser. But, for the perspective of the D developer, it still looks like you've introduced a new keyword, but it's a keyword that's A) nearly identical to another keyword, and B) contains a symbol. Ugh. I really. Really. Really. Hate it. Using "super const" is also annoying because all of the other storage classes use a single keyword. Now we've got this weird case that uses two keywords. And the word "super" is now overloaded in two completely unrelated concepts. (By the way, I don't like "static if" either. I think the right choice would have been #if, #foreach, etc. And I think C++ made a mistake by defining "long long" and "double double" types as well. Presumably, that saved some keywords, but we'd all have been better off if the types were defined as int8, int16, int32, int64, float32, float64, etc). I vote for "readonly" and "const". Anything else seems like a mistake. --benji

Well expressed. My think is almost 100% aligned with these thoughts above. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
prev sibling parent reply "Joel C. Salomon" <JoelCSalomon Gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 We've shoved keywords like "readonly" and "view" for a while, and they 
 just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be fine 
 to just encode it as the mildly verbose "super const". In fact, it's 
 shorter than "keepyourgrubbyhandsoff". :o)

How about “const const”? No new keywords, no reuse of keywords in disparate contexts, and equally noticeable to the eye. --Joel
Mar 16 2007
next sibling parent Dan <murpsoft hotmail.com> writes:
 How about “const const”?  No new keywords, no reuse of keywords in 
 disparate contexts, and equally noticeable to the eye.
 
 --Joel

Noticeable alright! I can't even type those characters. : D
Mar 16 2007
prev sibling next sibling parent reply kris <foo bar.com> writes:
Joel C. Salomon wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 
 We've shoved keywords like "readonly" and "view" for a while, and they 
 just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be fine 
 to just encode it as the mildly verbose "super const". In fact, it's 
 shorter than "keepyourgrubbyhandsoff". :o)

How about “const const”? No new keywords, no reuse of keywords in disparate contexts, and equally noticeable to the eye. --Joel

The whole things about adding *more* constantness to const screams one thing very loudly: the applicability of the word "const", in this context, is borked :)
Mar 16 2007
parent reply Benji Smith <dlanguage benjismith.net> writes:
kris wrote:
 Joel C. Salomon wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 We've shoved keywords like "readonly" and "view" for a while, and 
 they just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be 
 fine to just encode it as the mildly verbose "super const". In fact, 
 it's shorter than "keepyourgrubbyhandsoff". :o)

How about “const const”? No new keywords, no reuse of keywords in disparate contexts, and equally noticeable to the eye. --Joel

The whole things about adding *more* constantness to const screams one thing very loudly: the applicability of the word "const", in this context, is borked :)

Bingo. It's like trying to describe something as "impossible impossible". If the first usage of the word didn't really convey impossibility, then you were using the wrong word to begin with. --b
Mar 16 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 kris wrote:
 Joel C. Salomon wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 We've shoved keywords like "readonly" and "view" for a while, and 
 they just added more confusion than cleared, at a high cost. If (as 
 I suspect) super const appears relatively infrequently, it might be 
 fine to just encode it as the mildly verbose "super const". In fact, 
 it's shorter than "keepyourgrubbyhandsoff". :o)

How about “const const”? No new keywords, no reuse of keywords in disparate contexts, and equally noticeable to the eye. --Joel

The whole things about adding *more* constantness to const screams one thing very loudly: the applicability of the word "const", in this context, is borked :)

Bingo. It's like trying to describe something as "impossible impossible". If the first usage of the word didn't really convey impossibility, then you were using the wrong word to begin with.

You're "dead dead" right! :o) Andrei
Mar 16 2007
parent Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 14:25:36 -0700, Andrei Alexandrescu (See Website For
Email) wrote:


 Bingo. It's like trying to describe something as "impossible 
 impossible". If the first usage of the word didn't really convey 
 impossibility, then you were using the wrong word to begin with.

You're "dead dead" right! :o)

Reminds me a bit of the joke ... Teacher: Two negatives imply a positive ... eg. 'not bad' is 'good'. But two positives never imply a negative. Student: Yeah ... right, whatever. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Benji Smith wrote:
 Bingo. It's like trying to describe something as "impossible 
 impossible". If the first usage of the word didn't really convey 
 impossibility, then you were using the wrong word to begin with.

Reminds me of the old assembler program: foo: MOV EAX, 3 CALL BAR HALT ; stop the program HALT ; if skidding
Mar 16 2007
next sibling parent reply kris <foo bar.com> writes:
Walter Bright wrote:
 Benji Smith wrote:
 
 Bingo. It's like trying to describe something as "impossible 
 impossible". If the first usage of the word didn't really convey 
 impossibility, then you were using the wrong word to begin with.

Reminds me of the old assembler program: foo: MOV EAX, 3 CALL BAR HALT ; stop the program HALT ; if skidding

LOL
Mar 16 2007
parent Walter Bright <newshound digitalmars.com> writes:
kris wrote:
 Walter Bright wrote:
 Benji Smith wrote:

 Bingo. It's like trying to describe something as "impossible 
 impossible". If the first usage of the word didn't really convey 
 impossibility, then you were using the wrong word to begin with.

Reminds me of the old assembler program: foo: MOV EAX, 3 CALL BAR HALT ; stop the program HALT ; if skidding

LOL

You know those teenagers with the overclocked CPUs...
Mar 16 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Benji Smith wrote:
 Bingo. It's like trying to describe something as "impossible 
 impossible". If the first usage of the word didn't really convey 
 impossibility, then you were using the wrong word to begin with.

Reminds me of the old assembler program: foo: MOV EAX, 3 CALL BAR HALT ; stop the program HALT ; if skidding

LOL
Mar 16 2007
prev sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Joel C. Salomon wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 We've shoved keywords like "readonly" and "view" for a while, and they 
 just added more confusion than cleared, at a high cost. If (as I 
 suspect) super const appears relatively infrequently, it might be fine 
 to just encode it as the mildly verbose "super const". In fact, it's 
 shorter than "keepyourgrubbyhandsoff". :o)

How about “const const”? No new keywords, no reuse of keywords in disparate contexts, and equally noticeable to the eye.

It was on the plate briefly. Probably it would be an uphill battle to convince people to enjoy it. Andrei
Mar 16 2007
parent "Joel C. Salomon" <JoelCSalomon Gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 How about “const const”?  No new keywords, no reuse of keywords in 
 disparate contexts, and equally noticeable to the eye.

It was on the plate briefly. Probably it would be an uphill battle to convince people to enjoy it.

I’m still kinda shaky on the question of why the various extra specifiers are needed. If a variable is being declared “const” ought to mean nobody can change it, if it’s a function parameter it’s a promise that the function won’t change it. How vital is it that the language express “put this in read-only memory” as opposed to “don’t put this in read-only memory, but complain if I try to change it”? Why should a function care if it got a pointer to read-only memory or if it just promised not to alter the value? For numeric constants that don’t need addresses, I’d suggest “define” rather than “final”, as in: define float PI = 3.14159; If the “#if” syntax for “static if” is adopted, I might even suggest “#define”, just to make it clear that something is happening at compile-time rather than run-time. --Joel
Mar 17 2007
prev sibling next sibling parent reply janderson <askme me.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage 
 classes" manipulation that Andrei and others where thinking of for D. 
 The last I heard from it was Andrei's max suggestion from his max 
 design challenge, however, I think that suggestion may suffer from 
 some problems in regards to the "maxtype" requirement, plus it is 
 wholly incomplete in regards to how storage classes interact between 
 each other. Like Andrei said, what is a "const inout lazy const 
 char[]", if valid at all? Is there any news here? Is there a 
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable. There is talk about deprecating lazy if it's best implemented via other mechanisms. There is also talk about deprecating "inout" in favor of "ref" on grounds that the often-useful "inout const" is likely to become #1 reason for bashing D. To read a declaration like "const inout lazy const char[]", you can first parenthesize it appropriately: const(inout(lazy(const(char[])))) The lazy thing is really a delegate that returns a const char[]. The inout around it passes that delegate by reference, and the const at the top makes the delegate immutable. Andrei

Thanks for the update: BTW: const!(char[]) Looks to much like a template to me, I'd imagine some syntax phasers would have a harder time then necessary. -Joel
Mar 16 2007
next sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
janderson wrote:
 BTW:
 
 const!(char[])
 
 Looks to much like a template to me, I'd imagine some syntax phasers 
 would have a harder time then necessary.

If you mean parsers, I don't think that'll be much of a problem. 'const' is a keyword, template names are not. If you use a syntax-highlighting reader you'll also see the difference between const!(char[]) and foo!(char[]) immediately, for the same reason. (This is not to be construed as support for the "const!" syntax. I just dislike for different reasons ;) )
Mar 16 2007
prev sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
janderson wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage 
 classes" manipulation that Andrei and others where thinking of for D. 
 The last I heard from it was Andrei's max suggestion from his max 
 design challenge, however, I think that suggestion may suffer from 
 some problems in regards to the "maxtype" requirement, plus it is 
 wholly incomplete in regards to how storage classes interact between 
 each other. Like Andrei said, what is a "const inout lazy const 
 char[]", if valid at all? Is there any news here? Is there a 
 working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable. There is talk about deprecating lazy if it's best implemented via other mechanisms. There is also talk about deprecating "inout" in favor of "ref" on grounds that the often-useful "inout const" is likely to become #1 reason for bashing D. To read a declaration like "const inout lazy const char[]", you can first parenthesize it appropriately: const(inout(lazy(const(char[])))) The lazy thing is really a delegate that returns a const char[]. The inout around it passes that delegate by reference, and the const at the top makes the delegate immutable. Andrei

Thanks for the update: BTW: const!(char[]) Looks to much like a template to me, I'd imagine some syntax phasers would have a harder time then necessary.

The damndest thing is that const! (as well as const) _is_ a template: it's a type constructor that takes another type as an argument and returns a type. Andrei
Mar 16 2007
prev sibling next sibling parent reply eao197 <eao197 intervale.ru> writes:
On Fri, 16 Mar 2007 00:16:48 +0300, Andrei Alexandrescu (See Website For=
  =

Email) <SeeWebsiteForEmail erdani.org> wrote:

 a) final - a simple storage class controlling the immutability of the =

 bits allocated for the symbol per se;

 b) const - type qualifier meaning an immutable view of an otherwise  =

 modifiable data. const does not control the bits of the object, only t=

 storage addressed indirectly by it (transitively);

 c) "superconst" - denoted as "const!" or "super const": type qualifier=

 meaning that the data is genuinely unmodifiable.

Great! What about the influence of this storage classes on class method's = signatures? Is this will be in D: class Data { ... } class Container { // non-const access to data. Data data() { ... } // const access to data. const Data data() const { ... } } void useContainer( const Container c ) { auto d =3D c.data(); // the type of d is 'const Data'. ... } ? -- = Regards, Yauheni Akhotnikau
Mar 16 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
eao197 wrote:
 On Fri, 16 Mar 2007 00:16:48 +0300, Andrei Alexandrescu (See Website For 
 Email) <SeeWebsiteForEmail erdani.org> wrote:
 
 a) final - a simple storage class controlling the immutability of the 
 bits allocated for the symbol per se;

 b) const - type qualifier meaning an immutable view of an otherwise 
 modifiable data. const does not control the bits of the object, only 
 the storage addressed indirectly by it (transitively);

 c) "superconst" - denoted as "const!" or "super const": type qualifier 
 meaning that the data is genuinely unmodifiable.

Great! What about the influence of this storage classes on class method's signatures? Is this will be in D: class Data { ... } class Container { // non-const access to data. Data data() { ... } // const access to data. const Data data() const { ... } } void useContainer( const Container c ) { auto d = c.data(); // the type of d is 'const Data'. ... } ?

Yes, that's part of the plan. Moreover, you'll likely have the ability to define const-transporting methods (methods that look identical but only need to pass the const of the object out to the return value) only once. This has been a source of annoyance in certain C++ idioms. Andrei
Mar 16 2007
parent reply eao197 <eao197 intervale.ru> writes:
On Fri, 16 Mar 2007 12:43:19 +0300, Andrei Alexandrescu (See Website For  
Email) <SeeWebsiteForEmail erdani.org> wrote:

 Yes, that's part of the plan. Moreover, you'll likely have the ability  
 to define const-transporting methods (methods that look identical but  
 only need to pass the const of the object out to the return value) only  
 once. This has been a source of annoyance in certain C++ idioms.

Thanks, I'm glad to hear that! It seems that D with this storage classes and const-transporting methods will be almost different D, may be D 2.0 :) -- Regards, Yauheni Akhotnikau
Mar 16 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
eao197 wrote:
 On Fri, 16 Mar 2007 12:43:19 +0300, Andrei Alexandrescu (See Website For 
 Email) <SeeWebsiteForEmail erdani.org> wrote:
 
 Yes, that's part of the plan. Moreover, you'll likely have the ability 
 to define const-transporting methods (methods that look identical but 
 only need to pass the const of the object out to the return value) 
 only once. This has been a source of annoyance in certain C++ idioms.

Thanks, I'm glad to hear that! It seems that D with this storage classes and const-transporting methods will be almost different D, may be D 2.0 :)

Actually, this is exactly true. D 2.0. Andrei
Mar 16 2007
prev sibling parent reply =?UTF-8?B?QW5kZXJzIEYgQmrDtnJrbHVuZA==?= <afb algonet.se> writes:
eao197 wrote:

 It seems that D with this storage classes and const-transporting 
 methods  will be almost different D, may be D 2.0 :)

Or perhaps it should be called D++ ? --anders
Mar 16 2007
parent reply eao197 <eao197 intervale.ru> writes:
On Fri, 16 Mar 2007 15:12:09 +0300, Anders F Björklund <afb algonet.se>  
wrote:

 It seems that D with this storage classes and const-transporting  
 methods  will be almost different D, may be D 2.0 :)

Or perhaps it should be called D++ ?

No, at first there must be 'D with macros' (like 'C with classes') and only then -- D++ :)) -- Regards, Yauheni Akhotnikau
Mar 16 2007
next sibling parent reply Dan <murpsoft hotmail.com> writes:
 No, at first there must be 'D with macros' (like 'C with classes') and  
 only then -- D++ :))

*shudder* D already has templates. it shouldn't have a separate macro language. Having one indicates that the language itself somehow fails. Templates *should* be sufficient. Using the ++ notation for the language is dangerous. It allows you to only upgrade once. You can't have a D, D++, D 2.0 etc, it just doesn't fit, 'causing the language to completely stagnate. Then someone else has to come along and invent a language E. I think if we properly use 'const' to define something as 'not moving', and 'final' to define something as being 'unchangeable' then you can declare final const x and be done with it. The simpler the language is, the better. Sincerely, Dan
Mar 16 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Dan wrote:
 I think if we properly use 'const' to define something as 'not
 moving', and 'final' to define something as being 'unchangeable' then
 you can declare final const x and be done with it.
 
 The simpler the language is, the better.

Simplistic does not mean simpler. Your system would be unable to express the simplest realities: void print(const char[] message); // not modifying message void main() { char[] myMessage = readln(); print(myMessage); // error! myMessage is changeable! } I truly think we've distilled the simplest language within our requirements. Andrei
Mar 16 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 16 Mar 2007 11:02:39 -0700, Andrei Alexandrescu (See Website For
Email) wrote:


 void print(const char[] message); // not modifying message
 
 void main()
 {
    char[] myMessage = readln();
    print(myMessage); // error! myMessage is changeable!
 }
 
 I truly think we've distilled the simplest language within our requirements.

Maybe the concepts but I'm not so sure about the usage. Given a function signature that has an array argument, there are two independant things we might want to remain unchangeable - the reference and the data referred to. To me, the signature ... void print(const char[] message) looks like 'const' is qualifying 'char[]' because that's what it is next to, and the 'message' looks like it is not qualified. So the signature looks like it is saying, the data is 'const' but the reference is not. Thus this below signature looks more like what you are trying to express ... void print(char[] const message) And to make both unchangeable then this looks better to me ... void print(const char[] const message) -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Fri, 16 Mar 2007 11:02:39 -0700, Andrei Alexandrescu (See Website For
 Email) wrote:
 
 
 void print(const char[] message); // not modifying message

 void main()
 {
    char[] myMessage = readln();
    print(myMessage); // error! myMessage is changeable!
 }

 I truly think we've distilled the simplest language within our requirements.

Maybe the concepts but I'm not so sure about the usage. Given a function signature that has an array argument, there are two independant things we might want to remain unchangeable - the reference and the data referred to. To me, the signature ... void print(const char[] message) looks like 'const' is qualifying 'char[]' because that's what it is next to, and the 'message' looks like it is not qualified. So the signature looks like it is saying, the data is 'const' but the reference is not.

Exactly. All I'm saying is that if you go through the steps of your system, you will see that it's unable to implement the notion of modular mutability.
 Thus this below signature looks more like what you are trying to express
 ...
 
   void print(char[] const message)
 
 And to make both unchangeable then this looks better to me ...
 
   void print(const char[] const message)

We decided to use final to express symbol non-rebinding. Andrei
Mar 16 2007
prev sibling parent eao197 <eao197 intervale.ru> writes:
On Fri, 16 Mar 2007 17:56:57 +0300, Dan <murpsoft hotmail.com> wrote:

 No, at first there must be 'D with macros' (like 'C with classes') and
 only then -- D++ :))

*shudder*

It was a joke.
 D already has templates.  it shouldn't have a separate macro language.   
 Having one indicates that the language itself somehow fails.  Templates  
 *should* be sufficient.

My expirience in C++ and Ruby says that a language must have no macro system at all (like in Ruby) or must have a very powerful one (much more powerful than in C/C++, Nemerle is a fresh example). If D have started movement to support macros it must go as far as possible. I think.
 Using the ++ notation for the language is dangerous.  It allows you to  
 only upgrade once.  You can't have a D, D++, D 2.0 etc, it just doesn't  
 fit, 'causing the language to completely stagnate.  Then someone else  
 has to come along and invent a language E.

E programming language already exists: http://www.erights.org/ ;) And it grows from the other side of BCPL-C-C++ family -- from Java's branch. So the name of D can't be changed to E, only to D++ or D 2.0 :)
 The simpler the language is, the better.

Oberon was a very simple language, but where is it now? C++ have never been simple. Java isn't a simple language now. Ruby isn't simple. And this language are very successful in real world. And D will be, I hope. -- Regards, Yauheni Akhotnikau
Mar 16 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
eao197 wrote:
 No, at first there must be 'D with macros' (like 'C with classes') and 
 only then -- D++ :))

D will get macros - but they won't be text processing macros, they'll be abstract syntax tree (AST) processing macros. C++ programmers often try to use text macros to do syntax tree manipulation, but the result usually looks like one tried to use a belt buckle to drive screws.
Mar 16 2007
parent reply eao197 <eao197 intervale.ru> writes:
On Sat, 17 Mar 2007 01:41:54 +0300, Walter Bright  
<newshound digitalmars.com> wrote:

 eao197 wrote:
 No, at first there must be 'D with macros' (like 'C with classes') and  
 only then -- D++ :))

D will get macros - but they won't be text processing macros, they'll be abstract syntax tree (AST) processing macros.

It is very interesting to hear that. Could you say more about the future macro system in D? Or it is a secret now?
 C++ programmers often try to use text macros to do syntax tree  
 manipulation, but the result usually looks like one tried to use a belt  
 buckle to drive screws.

Yes, I know. I have used C++ macros a lot in some cases. -- Regards, Yauheni Akhotnikau
Mar 17 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
eao197 wrote:
 On Sat, 17 Mar 2007 01:41:54 +0300, Walter Bright 
 <newshound digitalmars.com> wrote:
 
 eao197 wrote:
 No, at first there must be 'D with macros' (like 'C with classes') 
 and only then -- D++ :))

D will get macros - but they won't be text processing macros, they'll be abstract syntax tree (AST) processing macros.

It is very interesting to hear that. Could you say more about the future macro system in D? Or it is a secret now?

It's pretty simple: macro foo(args) { ...syntax that gets inserted... } and the args and syntax is evaluated in the context of the invocation of the macro, not the definition of the macro. You'll be able to do things like: macro print(arg) { writefln(__FILE__, __LINE__, arg); } which get the file and line in the right context. There's no way to do that right now.
Mar 17 2007
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Funny, this is beginning to read like dialogue from Sam & Max: Episode 1...

Daniel Keep wrote:
 Walter Bright wrote:
 eao197 wrote:
 On Sat, 17 Mar 2007 01:41:54 +0300, Walter Bright
 <newshound digitalmars.com> wrote:
 D will get macros - but they won't be text processing macros, they'll
 be abstract syntax tree (AST) processing macros.




Bosco: And I might have another item of interest behind the counter...
 It is very interesting to hear that. Could you say more about the
 future macro system in D? Or it is a secret now?



Sam: I accept your thinly veiled invitation to ask about the item behind the counter.
 It's pretty simple:
 
     macro foo(args)
     {
         ...syntax that gets inserted...
     }


Bosco: Oh, it's a little something I like to call a Tear Gas Grenade Launcher.
 Oh, VERY slick.

Max: "Tear Gas Grenade Launcher?" Oh, I *really* want that!
 and the args and syntax is evaluated in the context of the invocation of
 the macro, not the definition of the macro. You'll be able to do things
 like:
 
     macro print(arg)
     {
         writefln(__FILE__, __LINE__, arg);
     }
 
 which get the file and line in the right context. There's no way to do
 that right now.


Bosco: It'll clear out a room full of militant college students in no time; guaranteed!
 Wow.  <3

Max: I feel really close to you right now. :P Question: in terms of usage, will these macros be like functions? Or will we able to define, say, a new kind of control structure with keywords? Or perhaps even a construct with a trailing curly brace block? You spoil us Walter, you really do. -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Mar 17 2007
parent reply Walter Bright <newshound digitalmars.com> writes:
Daniel Keep wrote:
 Question: in terms of usage, will these macros be like functions?

Yes. No !( ) syntax.
 Or
 will we able to define, say, a new kind of control structure with
 keywords?

No. Abner Hale thwacks such things with "abomination!"
  Or perhaps even a construct with a trailing curly brace block?

Yes.
 You spoil us Walter, you really do.

macros should be the final piece needed to make DSL's work nicely, and they are actually rather simple conceptually and implementation-wise. I tried for a long time to figure out how to make templates do the work, until I finally realized that AST macros are *fundamentally different* from templates. A nice side benefit to macros is we should be able to deprecate lazy.
Mar 17 2007
parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 
 macros should be the final piece needed to make DSL's work nicely, and 
 they are actually rather simple conceptually and implementation-wise. I 
 tried for a long time to figure out how to make templates do the work, 
 until I finally realized that AST macros are *fundamentally different* 
 from templates.

Yup. It sounds like the implementation will really be quite like the new mixin feature wrapped in some syntactic sugar. Oddly, I'm actually looking forward to this. There have been a few instances where I actually needed macros... some being cases where I wanted to define a local template function (which is illegal), and others for more bizarre uses.
 A nice side benefit to macros is we should be able to deprecate lazy.

I actually like 'lazy' for a narrow range of uses. It makes creating an optimal debug logger exceedingly simple, for example. Compared to all the evil tricks Matthew needed for Pantheios, 'lazy' has a clear and measurable benefit. But if the same thing could be accomplished using macros then so much the better. Sean
Mar 17 2007
prev sibling next sibling parent reply Marcin Kuszczak <aarti interia.pl> writes:
Walter Bright wrote:

 It's pretty simple:
 
 macro foo(args)
 {
 ...syntax that gets inserted...
 }
 
 and the args and syntax is evaluated in the context of the invocation of
 the macro, not the definition of the macro. You'll be able to do things
 like:
 
 macro print(arg)
 {
 writefln(__FILE__, __LINE__, arg);
 }
 
 which get the file and line in the right context. There's no way to do
 that right now.

And what about mixin templates? Couldn't they just be fixed to do this work? From docs: " Unlike a template instantiation, a template mixin's body is evaluated within the scope where the mixin appears, not where the template declaration is defined. It is analogous to cutting and pasting the body of the template into the location of the mixin. It is useful for injecting parameterized 'boilerplate' code, as well as for creating templated nested functions, which is not possible with template instantiations. " For me it looks exactly like this what you want to do with macro. What would be a difference? For me it seems that the whole mixin thing is rather buggy and unusfull at the moment - for reference see: http://www.digitalmars.com/d/archives/digitalmars/D/learn/3412.html#N3416 -- Regards Marcin Kuszczak (Aarti_pl) ------------------------------------- Ask me why I believe in Jesus - http://zapytaj.dlajezusa.pl (en/pl) Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/ -------------------------------------
Mar 17 2007
parent Walter Bright <newshound digitalmars.com> writes:
See new thread for reply.
Mar 17 2007
prev sibling next sibling parent janderson <askme me.com> writes:
Walter Bright wrote:
 eao197 wrote:
 On Sat, 17 Mar 2007 01:41:54 +0300, Walter Bright 
 <newshound digitalmars.com> wrote:

 eao197 wrote:
 No, at first there must be 'D with macros' (like 'C with classes') 
 and only then -- D++ :))

D will get macros - but they won't be text processing macros, they'll be abstract syntax tree (AST) processing macros.

It is very interesting to hear that. Could you say more about the future macro system in D? Or it is a secret now?

It's pretty simple: macro foo(args) { ...syntax that gets inserted... } and the args and syntax is evaluated in the context of the invocation of the macro, not the definition of the macro. You'll be able to do things like: macro print(arg) { writefln(__FILE__, __LINE__, arg); } which get the file and line in the right context. There's no way to do that right now.

On naming why not use mixin, since they are so similar? mixin print(arg) { } Then make them typesafe (using alias to get out of type-safty)? What if you want the file in some other context? It would be nice to have a complete solution to that, which allows some sort of stack traversal. Although I'm not sure its possible, due to not wanting to keep this sort of information around at release time. ie: macro print(arg) { writefln(__FILE__[stack_level], __LINE__[stack_level], arg); } Or even better: Stack[0].Line; //Current line Stack[1].Line; //Line one level up Stack[0].File; Stack[0].Module; Stack[0].Function; Stack[0].NumbArgs; //Some form of reflection Stack[0].Arg[N]; //Access to the value of the argument (ie Turple of values) Stack[0].ArgIdentifier[N] //String name of the identifier Stack[0].FuncType //The type of function we are in (is it a macro, a compile time function a member, a regular function) ect... macro print(arg) { with(Stack[0]) { writefln(File, Line, arg); } } I guess that would have problems for larger depths. Perhaps it could be limited to two levels (current and parent) or only work for ones that can be evaluated at compile time (you would get a compile time error if the line number couldn't be evaluated.) There could be a debug version that would work at run time. -Joel
Mar 17 2007
prev sibling parent eao197 <eao197 intervale.ru> writes:
On Sat, 17 Mar 2007 11:49:59 +0300, Walter Bright  
<newshound digitalmars.com> wrote:

 It's pretty simple:

 	macro foo(args)
 	{
 		...syntax that gets inserted...
 	}

 and the args and syntax is evaluated in the context of the invocation of  
 the macro, not the definition of the macro. You'll be able to do things  
 like:

 	macro print(arg)
 	{
 		writefln(__FILE__, __LINE__, arg);
 	}

 which get the file and line in the right context. There's no way to do  
 that right now.

As I can see the content of macro body will be inserted into AST in the place of invocation. But there is not much differences with existing C/C++ macro handling (insertion of right __FILE__, __LINE__ is good thing anyway). Is there allowed any access to previous parsed entity? For example, can I define macro: macro some_class_extender(class_name) { ...modification of 'class_name' class structure... } class MyCoolClass { ... } some_class_extender( MyCoolClass ); and get the modified version of MyCoolClass after some_class_externder invocation? -- Regards, Yauheni Akhotnikau
Mar 17 2007
prev sibling next sibling parent reply Reiner Pope <no spam.com> writes:
Andrei Alexandrescu (See Website For Email) Wrote:

 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage classes" 
 manipulation that Andrei and others where thinking of for D. The last I 
 heard from it was Andrei's max suggestion from his max design challenge, 
 however, I think that suggestion may suffer from some problems in 
 regards to the "maxtype" requirement, plus it is wholly incomplete in 
 regards to how storage classes interact between each other. Like Andrei 
 said, what is a "const inout lazy const char[]", if valid at all? Is 
 there any news here? Is there a working(aka complete) design?

We have talked about a design. In short, the intent is to define three flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable. There is talk about deprecating lazy if it's best implemented via other mechanisms. There is also talk about deprecating "inout" in favor of "ref" on grounds that the often-useful "inout const" is likely to become #1 reason for bashing D. To read a declaration like "const inout lazy const char[]", you can first parenthesize it appropriately: const(inout(lazy(const(char[])))) The lazy thing is really a delegate that returns a const char[]. The inout around it passes that delegate by reference, and the const at the top makes the delegate immutable. Andrei

So, would this allow you to express the associative array requirement, that the key must never be changed after it is set? char[] myKey = "foo"; myAA[myKey] = "bar"; myKey = "something else"; // ERROR: this stuffs up the AA would you write the getter's signature as superconst? T set(super const K key) {...} Is super const allowed as a parameter type?
Mar 16 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Reiner Pope wrote:
 Andrei Alexandrescu (See Website For Email) Wrote:
 
 Bruno Medeiros wrote:
 What is the status of the experimental designs for the "storage classes" 
 manipulation that Andrei and others where thinking of for D. The last I 
 heard from it was Andrei's max suggestion from his max design challenge, 
 however, I think that suggestion may suffer from some problems in 
 regards to the "maxtype" requirement, plus it is wholly incomplete in 
 regards to how storage classes interact between each other. Like Andrei 
 said, what is a "const inout lazy const char[]", if valid at all? Is 
 there any news here? Is there a working(aka complete) design?

flavors of immutability: a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se; b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively); c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable. There is talk about deprecating lazy if it's best implemented via other mechanisms. There is also talk about deprecating "inout" in favor of "ref" on grounds that the often-useful "inout const" is likely to become #1 reason for bashing D. To read a declaration like "const inout lazy const char[]", you can first parenthesize it appropriately: const(inout(lazy(const(char[])))) The lazy thing is really a delegate that returns a const char[]. The inout around it passes that delegate by reference, and the const at the top makes the delegate immutable. Andrei

So, would this allow you to express the associative array requirement, that the key must never be changed after it is set? char[] myKey = "foo"; myAA[myKey] = "bar"; myKey = "something else"; // ERROR: this stuffs up the AA would you write the getter's signature as superconst? T set(super const K key) {...} Is super const allowed as a parameter type?

Yes. Andrei
Mar 16 2007
prev sibling parent Dave <Dave_member pathlink.com> writes:
Andrei Alexandrescu (See Website For Email) Wrote:
 We have talked about a design. In short, the intent is to define three 
 flavors of immutability:
 
 a) final - a simple storage class controlling the immutability of the 
 bits allocated for the symbol per se;
 
 b) const - type qualifier meaning an immutable view of an otherwise 
 modifiable data. const does not control the bits of the object, only the 
 storage addressed indirectly by it (transitively);
 
 c) "superconst" - denoted as "const!" or "super const": type qualifier 
 meaning that the data is genuinely unmodifiable.
 

'invariant' seems to be the consensus (instead of super const or const!)? If so, I'd agree that's the best of the three.
 There is talk about deprecating lazy if it's best implemented via other 
 mechanisms. There is also talk about deprecating "inout" in favor of 
 "ref" on grounds that the often-useful "inout const" is likely to become 
 #1 reason for bashing D.

I haven't noticed 'ref' come-up on subsequent posts, but I would strongly favor this change as well. - Dave
Mar 23 2007
prev sibling next sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bruno Medeiros wrote:
 What is the status of the experimental designs ...

I asked this because yesterday, out of nowhere, I started thinking about this problem, and as a kind of a mental exercise I came to a working design. It seems kinda pointless, since you already made your design, but I'll show this one nevertheless. Consider it a late entry to the max challenge :P . Still, there are some aspects presented here, that I don't how they would work on your planned design (like those keyed by the questions). Most of the terms here are tentative, and so is the syntax. Please consider the syntax separately from the semantics and conceptualization. Errors may be present in the text. This design is presented as is, without any warranty of any kind. :P CONCEPTUALIZATION There are 3 major kinds of D entities: Values (expressions), types, and templates. (and labels too I guess...) Values are characterized by properties that define what one can do with the value. The most important of these properties is the type. D offers a very rich mechanism to query and manipulate types (typeof(..), is(..) expression, auto, type parameters, etc.), much better than any other statically typed language I know. But the type is not the only "property" of an expression. There are others, such as whether an expression is an lvalue or not, if it can be assigned, etc.. The problem so far is that D does not offer a good mechanism to query and manipulate such "type properties". Let's consider the following type properties: R: is the value readable. C: is the value readable at compile time (compile time constant). &: is the value referenciable. W: is the value writable. (explained later) I can't find a good term for these "extended type properties" or "extended value properties" so I'll just call it QUX for now. Silly, I know, but whatever. And I will call "core type" to the current notion of type, which is what typeof(..) returns. So, with these QUX, what are the valid combinations of them? They are: R CR R& RW& W& (I'm skipping the explanation of why, since I think that's clear, see examples below). Furthermore, these property combinations are related in the following hierarchy of conversion: R / \ CR R& W& \ / RW& Examples of the QUX for various values in current D: 42 // CR - A literal is a constant. var // RW& - As in: int var; fvar // R& - As in: const fvar; fvar = 2; It's like 'final' func() // R - the value of a function is readonly and not referenciable // W - No example in current D for W Let's give some tentative keywords for the possible QUX combinations: CR - const RW& - ref W& - wronly ref R& - rdonly ref R - rdonly Now, recall the following: QUX describe the properties of a value. The first thing you may think now is that one can use QUX to declare variables of the same QUX type. That's not entirely accurate. For example you can't declare a variable of QUX R, because a var is always either referenciable, or a (compile-time) constant. There are no R variables. Specifying a var as rdonly will create a R&. Specifying no QUX will create a RW&. Specifying ref in a var declaration will alse create a RW& var, but the identity will be the same as the var in the given initializer. ref will preserve the "reference" (memory location) of the initializer. This is mentioned to clear the declaration semantics. Examples: int varA = varX; // var is RW& const int varB = 1; // var is CR rdonly int varC = 2; // var is R& rdonly ref int varD = &varX; // var is RW& too wronly ref int varE = &varX; // var is W& varA will be a copy of varX, while varD will be the same as varX (same identity). After definition, varA and varD will have the same QUX. And what about the definitions of function parameters, and function return types? There are some minor differences. In function return types, if no QUX is specified, then the QUX is rdonly (as it is currently in D). In function parameters, if no QUX is specified the QUX is rdonly too (this is different from current D, but is considered a nice improvement. Function arguments are almost never modified anyway). What about composing types? That is, when one has a composite type (array, pointers, etc.), how does one specify QUX for each of the type components? Well for this var: int[]* var; then QUX are specified like this: rdonly int[]* var; // var (the pointer) is rdonly (rdonly int[])* var; // the pointer target (the array) is rdonly (rdonly int)[]* var; // the members of the array are rdonly rdonly (rdonly (rdonly int)[])* var; // All are rdonly Note that some QUX don't make sense in certain declarations, like declaring an array member as ref, like this: (ref int)[] var; because the members of arrays are refs already. This could be an error or simply ignored. What about auto? auto does not in any way capture the QUX, just the core type (as in typeof(..) ). rdonly var; auto foo = var; // foo is not rdonly How do we templatize and parameterize QUX? Let's see by example, looking at previous design challenges: The id function: T id(expr T) (T a) { return a; } So, "expr T" denotes that T is not a normal type parameter, but an "extended type parameter". Besides the core type, it will also hold information about the QUX. id can be instanced manually or with IFTI. The max function (challenge #3) will show more advanced scenarios of QUX manipulation, but let's first recall what max does. Consider these vars: a = 3; b = 9; const fvar; // fvar is 'final' fvar = 1; And now some examples of max usage: max(1, 2) 2 of QUX CR max(a, 2) a of QUX R max(a, b) b of QUX RW& max(a, func()) a of QUX R max(a, fvar) a of QUX R& As requested, max preservers the greatest common QUX information. Here's how we define max: maxExtType!(A,B) max(expr A :: rdonly, expr B :: rdonly) (A a, B b) { if(a >= b) return a; else return b; } Of note: The 'T :: U' syntax means specialize the template if T can be converted to U. This is a variant of the current 'T : U' syntax which means specialize if T is the *same* as U. In both these constructs, U can be a QUX, but only if T is an "extended type parameter" (a parameter declared with expr). maxExtType is the key to complete the challenge. It defines the maximum common extended type of A and B. This is defined as: template maxExtType(expr A, expr B) { static if( !is(typeof(A) == typeof(B)) ) { alias maxCoreType(A, B) maxType; // Type cannot be ref } else { static if( is(A == ref) && is(B == ref)) alias (ref typeof(A)) maxType; else static if( is(A :: rdonly ref) && is(B :: rdonly ref)) alias (rdonly ref typeof(A)) maxType; else static if( is(A :: wronly ref) && is(B :: wronly ref) ) alias (wronly typeof(A)) maxType; else static if( is(A :: rdonly) && is(B :: rdonly) ) alias (rdonly typeof(A)) maxType; else static assert(false, "No common extended type for:"+A+" and "+B); } } So, like mentioned in the original challenge thread, if the core type of A and B are not the same, then maxExtType cannot be a ref. That's what the first static if checks for (note: an exception can be made for object types). The subsequent static ifs check for increasingly less restrictive common QUXs. It's possible that a common QUX does not exist if one is R& and the other is W& for example. What about lazy? In this design, lazy simply isn't considered as a QUX, as it simply is not a property of an expression. There are no lazy expressions. After a lazy FOO variable is created (which must be initialized), the variable becomes for *all effects* indistinguishable from a FOO delegate() , that is, a delegate returning type FOO. Thus, lazy can't also be parameterized/templatized. IMMUTABILITY Immutability, as in, "transitive immutability" is achieved as a type modifier with the keyword "immut". An immut value means that any other member obtained from the original value cannot be modified, and so on. The members of immut values are rdonly and immut. immut is not a QUX, it is a type modifier that modifies (and is part of) the core type. This means that immut appears in "typeof(..)", and consequentely is also captured by auto. This is the only sensible behavior, since immut describes a property of the referenced data of that expression, and must be preserved upon assignments (and thus part of the core type). This is unlike QUX, since QUX only describe properties of the immediate-value of an expression, which is copied in assignments. I.e., you can assign a rdonly value to a non-rdonly var, but you can't assign an immut value to a non-immut (normal) var. This shows how immut and QUX are somewhat different in nature. Also immut vars and not automatically rdonly, they are rdonly only if 'rdonly' is also specified. An example: immut Foo[] fooar; then: typeof(fooar[0]) == rdonly immut Foo; TODO *Syntax to specify the "this" of a method as immut. Maybe do it like C++? *A way to conveniently specify/templatize methods that are identical and only vary in the mutability of it's types (like 'romaybe' in Javari). The following describes a particular use case for rdonly and wronly: VARIANT COMPOSITE TYPES. Consider this hierarchy: FooBar extends Foo extends Object Xpto extends Object Suppose we have an array of Foo: Foo[] fooarr; The classic contravariance problem is, is fooarr safely castable to Object[] ? On first sight one might think yes, since Foo is an Object, then Foo[] is an Object[]. But that is not the case since an Object[] array is an array that one can put an Xpto object into: (cast(Object[]) fooarr)[0] = new Xpto(); which would break type safety, since we would have a Xpto in an array of Foo's. What happens is that we have some array operations (like readers) that remain safe , but others do not (like writers). Java allows that cast, but has runtime checks on array member assignments, and throws an exception if the type safety is violated like in the example above. Can a language provide (compile time) support for safe casting? With rdonly and wronly it can. We have that Foo[] cannot be cast to Object[], but it can be safely cast to (rdonly Object)[]. And then: fooarr2 = cast((rdonly Object)[]) fooarr; fooarr2[0] = new Xpto(); // Not allowed fooarr2[0].doSomething(); // Allowed Assignments won't be allowed, but reading is allowed. Conversely, the array type parameter can be contravariantly cast, from Foo[] to (wronly Foobar)[]. // Ok because fooarr[] is of type wronly FooBar fooarr[0] = new FooBar(); // Not allowed because you can't read fooarr[0]; fooarr[0].doSomething(); This was the main motivation I saw for the use of wronly, however, this mechanism is quite simple (as in, simplistic) and limited. It's not as powerful as Java's generics, which allow a greater degree of functionality with lower-bounded types. As such, it may not be worth having wronly just because of this. Still, I guess wronly could also be used in place of 'out' parameters. SYNTAX AND TERMS SUBJECT TO CHANGE: QUX - EVT (Extended Value Properties) or ETP (Extended Type Properties) ? Or 'attributes' instead of 'properties' ? But definitely not "storage class", that term sucks. :o immut - perhaps 'immutable'? rdonly - perhaps 'readonly' or 'final'? wronly - perhaps 'writeonly'? expr "::" - Ideally, it would be better that the ":" of template specialization would behave the same as the ":" of the is(..) expression. Comments are welcome. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 17 2007
prev sibling parent reply Tyler Knott <tywebmail mailcity.com> writes:
I think I've got this.  Is the following right?

*final variables can be assigned only once in their lifetime, but are otherwise
normal variables (i.e. they *do* have a 
memory address), except where constant folded by the compiler (this should be
completely transparent to the programmer).

*const and invariant only make sense when applied to references, otherwise they
are ignored or (preferably) errors.

*const references cannot be used to mutate the data they reference, but the
data they reference may be mutated directly 
or through other non-const, non-invariant references.

*invariant references only reference data that will never be mutated (e.g.
final variables).

*Both const and invariant references can be reassigned unless they are also
final references.

*Conversely, when final is applied to the declaration of a reference, only that
reference is protected from mutation, 
not the data it references, unless that reference is also const or invariant.

*Neither const nor invariant references can be cast to non-const or
non-invariant references directly.

*The address of any variable or the value of any reference (of compatible type)
can be assigned to const references.

*Only the addresses of final variables (I'm sure of this), the direct result of
"new" statements (not sure on this one), 
and the values of other invariant references may be assigned to invariant
references.

*const and invariant are viral (i.e. all references in the data referenced by a
const or invariant reference is also 
const or invariant, respectively, when access through the const or invariant
reference).

*final, when applied directly to POD structures (structs and array references),
means no members of that structure can 
be modified after assignment, but is non-viral to references within the POD
structure. (Not sure on this.)

*Function declarations cannot overloaded based on const or invariant. (Pretty
sure on this.)
---------------------
Here are a whole bunch of contrived examples of most of these rules:

final int w = 5; 	//legal
final int x = 55;	//ditto
int y = 6; 		//legal
int z;			//ditto
const int i;		//either (preferably) illegal or ignored
invariant int i;	//ditto

x = 6; 			//illegal
y = 7; 			//legal

int* mut_pointer;
mut_pointer = &x; 	//illegal: this would allow x to be mutated
mut_pointer = &y; 	//obviously legal
*mut_pointer = 10;	//obviously legal

const int* const_pointer;
const_pointer = &x;		//legal
const_pointer = &y; 		//Also legal: const_pointer is mutable (non-final)
z = *const_pointer;		//Legal: you can read through const references
*const_pointer = 10;		//illegal: you can't mutate through const references

invariant int* inv_pointer = &x; 	//Legal: x is final and never changes
inv_pointer = &y 			//Illegal: y is non-final may change
inv_pointer = &w;			//Legal: inv_pointer is mutable (non-final)
y = *inv_pointer;			//Legal: data can be read through inv_pointer
*inv_pointer = y;			//Illegal: you can't mutate through invariant references

final int* final_pointer = &y;		//obviously legal
z = *final_pointer;			//obviously legal
*final_pointer = 2; 			//legal: y can be changed through final_pointer
final_pointer = &z; 			//illegal: final_pointer already assigned

final int* final_pointer2 = &x;		//illegal: final_pointer2 allows mutation

final const int* final_const_pointer = &x; //Legal: const pointer does not
allow mutation
final_const_pointer = &w;		//Illegal: final_const_pointer is final and cannot
be mutated
y = *final_const_pointer;		//Obviously legal
*final_const_pointer = y;		//Obviously illegal

final const int* final_const_pointer2 = &y; //Legal: neither y nor const care
about underlying mutability

final invariant int* final_inv_pointer = &x; //Legal
final invariant int* final_inv_pointer2 = &y; //Illegal: y is mutable

struct S
{
	int a;
	char[] b;
	static S* opCall()
	{
		S* ret = new S;
		s.b = "test";
		return ret;
	}
}

final S final_s;	//final_s exists on the stack or in the global scope

final_s.a = 6;		//Illegal: s is final
final_s.b = "hello"; 	//ditto

final S final_s2 = *S();	//Legal
final_s2.b[2] = 'd';		//Legal: final is non-viral

final S* s_pointer = new S;	//Legal, *(s_pointer) exists on the heap
s_pointer.a = 6;		//Legal, *(s_pointer) can be mutated through s_pointer
s_pionter.b = "hello"; 		//ditto
s_pointer = new S;		//Illegal: s_pointer is already assigned

final S* s_pointer2 = S();	//Obviously legal

const S* const_s_pointer = new S;	//Legal
y = const_s_pointer.a;			//Legal
const_s_pointer.a = 5;			//Illegal
const_s_pointer = S();			//Legal: const_s_pionter can be reassigned

invariant S* inv_s_pointer = new S;	//Legal
y = inv_s_pionter.a;			//Legal
inv_s_pointer.a = 5;			//Illegal
inv_s_pointer = S();			//Not sure, but probably illegal: S* points to mutable
data,
					//even though there are no other references to it.  How do
					//invariant pointers work with the static opCall() workaround for
					//missing struct constructors? Does NRVO deal with this?

class C
{
	int a;
	final S* s_in_c;
	this() { s_in_c = new S; } 	//Is it legal to assign final values in
constructors?
					//What about in a static opCall() for structs?
	void increment_a() { this.a++; }
}

final C final_c = new C();
final_c = new C();		//Illegal: final_c already assigned.
y = final_c.a;			//Legal
final_c.a = x;			//Also legal
final_c.increment_a();		//Ditto

const C const_c = new C();
const_c = new C();		//Legal: reference const_c can be reassigned
const_c.a = y;			//Illegal: cannot mutate through const reference
const_c.increment_a();		//Legal??? Is the object's "this" reference non-const?

invariant C inv_c = new C();
inv_c = new C();		//legal: reference inv_c can be reassigned
ivn_c.a = y;			//Illegal: cannot mutate through invariant reference
inv_c.increment_a();		//Illegal???  The object shouldn't be able to be modified
at all, but
				//how could the compiler catch this?
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Tyler Knott wrote:
 I think I've got this.  Is the following right?
 
 *final variables can be assigned only once in their lifetime, but are 
 otherwise normal variables (i.e. they *do* have a memory address), 
 except where constant folded by the compiler (this should be completely 
 transparent to the programmer).

Correct.
 *const and invariant only make sense when applied to references, 
 otherwise they are ignored or (preferably) errors.

Correct.
 *const references cannot be used to mutate the data they reference, but 
 the data they reference may be mutated directly or through other 
 non-const, non-invariant references.

Correct.
 *invariant references only reference data that will never be mutated 
 (e.g. final variables).

Ambiguously expressed. But I think I see what you mean, e.g. you can take the address of a final int and bind it to an invariant int*, like so: final int x = 42; immutable int* p = &x; // correct
 *Both const and invariant references can be reassigned unless they are 
 also final references.

Correct.
 *Conversely, when final is applied to the declaration of a reference, 
 only that reference is protected from mutation, not the data it 
 references, unless that reference is also const or invariant.

Correct.
 *Neither const nor invariant references can be cast to non-const or 
 non-invariant references directly.

Correct.
 *The address of any variable or the value of any reference (of 
 compatible type) can be assigned to const references.

Correct. (Just like in C++.)
 *Only the addresses of final variables (I'm sure of this), the direct 
 result of "new" statements (not sure on this one), and the values of 
 other invariant references may be assigned to invariant references.

Yes, yes (and more), and yes. New statements can be solved easily by just requiring people to qualify the type created: invariant int* p = new invariant int(42); User-defined types will have a chance to define constructors for invariant objects. Those constructors will have the chance of modifying the object being constructed, but won't be able to alias 'this' and member addresses to modifiable pointers. The deeper problem is with functions that return pointers that could be assigned to either mutable or invariant symbols. For example, array.dup. Take a look: int[] array = [1, 2, 3]; invariant int[] brray = array.dup; // should work invariant int[] crray = array.dup; // should work, too! I am working on a solution for this issue. The problem is rather general. Consider a function char[] readln() that reads a line from the standard input, creating a fresh string each call. That string should be assigned to char[] or invariant char[].
 *const and invariant are viral (i.e. all references in the data 
 referenced by a const or invariant reference is also const or invariant, 
 respectively, when access through the const or invariant reference).

"Transitive." Yes.
 *final, when applied directly to POD structures (structs and array 
 references), means no members of that structure can be modified after 
 assignment, but is non-viral to references within the POD structure. 
 (Not sure on this.)

Correct. "final x" freezes all of x's bits.
 *Function declarations cannot overloaded based on const or invariant. 
 (Pretty sure on this.)

Walter is mildly unconvinced about such overloads' utility, I think they should be allowed without being required. I'll snip your examples, which are correct and also raise a couple of interesting questions that are being worked on. Andrei
Mar 19 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
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? f.s[0] = 'a'; // okay??? f.s = "xyz"; // okay??? f.s = "def".dup; // okay ??? f.s.length = 1; // okay? f.s ~= 'a'; // okay?? f.c = 'a'; // okay??? const Foo somefunc(); f = somefunc; // okay?? Foo someotherfunc(); f = someotherfunc; // okay?? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 20/03/2007 4:45:27 PM
Mar 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
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.


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
Mar 19 2007
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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.


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
Mar 20 2007
parent reply Tyler Knott <tywebmail mailcity.com> writes:
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
Mar 20 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Tyler Knott wrote:
 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; }

Duh, of course. I misread and was thinking Foo was a class and not a struct. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 21 2007