www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - any news on const/invariant?

reply Denton Cockburn <diboss hotmail.com> writes:
Any update on the status of the const/invariant changes?

I'm using D 2.0 in some things, and would hate to learn to do it X way,
only to have it changed on me.
Nov 26 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?

Yes, it's about done.
 I'm using D 2.0 in some things, and would hate to learn to do it X way,
 only to have it changed on me.

While the semantic changes are extensive, I've found to my surprise that little has to change in source code that uses const.
Nov 26 2007
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?

Yes, it's about done.

How about documentation of how const will work. Is this essentially what was originally posted in the "const is broken" thread? Or are there new surprises in store for us?
 
 I'm using D 2.0 in some things, and would hate to learn to do it X way,
 only to have it changed on me.

While the semantic changes are extensive, I've found to my surprise that little has to change in source code that uses const.

Nov 26 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 Walter Bright Wrote:
 
 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?


How about documentation of how const will work. Is this essentially what was originally posted in the "const is broken" thread? Or are there new surprises in store for us?

I don't remember the details of that, but the nutshell version is: 1) no more final for variables 2) no more 'head const' or 'tail const', it's all just 'const' 3) ditto (2) for invariant It should be working as one would intuitively expect it to.
Nov 26 2007
next sibling parent Jason House <jason.james.house gmail.com> writes:
Walter Bright Wrote:

 Jason House wrote:
 How about documentation of how const will work.  Is this essentially
 what was originally posted in the "const is broken" thread?  Or are
 there new surprises in store for us?

I don't remember the details of that, but the nutshell version is: 1) no more final for variables 2) no more 'head const' or 'tail const', it's all just 'const' 3) ditto (2) for invariant It should be working as one would intuitively expect it to.

Sounds about the same... I remember it as "transitive const". Looking at changeset 512, I can see only a few small changes: * const after functions for const functions * "static const" instead of just "const" for a few global constants Is the 2nd one just code cleanup or does it have a deeper reason? PS: It really is interesting to see just how little you've changed over the previous code. If I remember right, the original framework caused grumblings about how hard it was to use string constants. Since very little of that code has changed (I'm guessing), has this become any easier for developers? PSS: I haven't used D 2.0 yet, so my questions may be ignorant. It wasn't anything specific to D 2.0 except that Tango didn't work on it and gdc didn't support it. Mac support was more important to me than cost support.
Nov 26 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 26 Nov 2007 14:11:25 -0800, Walter Bright wrote:

 Jason House wrote:
 Walter Bright Wrote:
 
 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?


How about documentation of how const will work. Is this essentially what was originally posted in the "const is broken" thread? Or are there new surprises in store for us?

I don't remember the details of that, but the nutshell version is: 1) no more final for variables

Is the keyword in this context now a syntax error?
 2) no more 'head const' or 'tail const', it's all just 'const'

Does that mean that if X is const then neither the bits in X and the bits referenced by X can be modified (though the X symbol)?
 3) ditto (2) for invariant

Does that mean that if X is invariant then neither the bits in X and the bits referenced by X can be modified by anything?
 It should be working as one would intuitively expect it to.

If the answer is 'yes' to the couple of questions above ... (a) is there a form of the syntax that allows 'X' to be modified but not what it references? (b) is there a form that prevents 'X' from being modified but allows what it references to be modified? -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 9:27:28 AM
Nov 26 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Mon, 26 Nov 2007 14:11:25 -0800, Walter Bright wrote:
 
 Jason House wrote:
 Walter Bright Wrote:

 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?


what was originally posted in the "const is broken" thread? Or are there new surprises in store for us?

1) no more final for variables

Is the keyword in this context now a syntax error?

It's just ignored.
 2) no more 'head const' or 'tail const', it's all just 'const'

Does that mean that if X is const then neither the bits in X and the bits referenced by X can be modified (though the X symbol)?

Const and invariant are transitive. C++ const is not transitive, leading to programmers establishing a *convention* for transitive const. But it being an unenforced convention means it is not reliable.
 3) ditto (2) for invariant

Does that mean that if X is invariant then neither the bits in X and the bits referenced by X can be modified by anything?

Right.
 It should be working as one would intuitively expect it to.

If the answer is 'yes' to the couple of questions above ... (a) is there a form of the syntax that allows 'X' to be modified but not what it references?

Yes, transitive const applies downwards, not upwards.
 (b) is there a form that prevents 'X' from being modified but allows what
 it references to be modified?

No. There is no language typing support for 'logical const' (i.e. const structures with mutable members). Many C++ people have argued passionately for logical const, but I have become strongly convinced that the notion is fatally flawed.
Nov 26 2007
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Derek Parnell wrote:
 On Mon, 26 Nov 2007 14:11:25 -0800, Walter Bright wrote:
 2) no more 'head const' or 'tail const', it's all just 'const'

Does that mean that if X is const then neither the bits in X and the bits referenced by X can be modified (though the X symbol)?

Const and invariant are transitive. C++ const is not transitive, leading to programmers establishing a *convention* for transitive const. But it being an unenforced convention means it is not reliable.

Before, I could declare an array of const characters. Essentially, the array structure could be modified, but the characters pointed to by the array could not. i.e. const(char)[] myString = "hello"; However, in this case, I could slice my string like so myString = myString[0..4] // set to "hell" The way you are talking this is no longer possible? Because now I can only have a fully const array which means you cannot change the pointer in the array? Just trying to clarify :) -Steve
Nov 26 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 "Walter Bright" wrote
 Derek Parnell wrote:
 On Mon, 26 Nov 2007 14:11:25 -0800, Walter Bright wrote:
 2) no more 'head const' or 'tail const', it's all just 'const'

referenced by X can be modified (though the X symbol)?

to programmers establishing a *convention* for transitive const. But it being an unenforced convention means it is not reliable.

Before, I could declare an array of const characters. Essentially, the array structure could be modified, but the characters pointed to by the array could not. i.e. const(char)[] myString = "hello"; However, in this case, I could slice my string like so myString = myString[0..4] // set to "hell" The way you are talking this is no longer possible?

It still works.
 Because now I can only 
 have a fully const array which means you cannot change the pointer in the 
 array?

A "fully const array" would be declared as: const(char[]) myString; or just: const char[] myString;
Nov 26 2007
prev sibling next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 26 Nov 2007 15:02:57 -0800, Walter Bright wrote:

 Derek Parnell wrote:

 2) no more 'head const' or 'tail const', it's all just 'const'

Does that mean that if X is const then neither the bits in X and the bits referenced by X can be modified (though the X symbol)?

Const and invariant are transitive. C++ const is not transitive, leading to programmers establishing a *convention* for transitive const. But it being an unenforced convention means it is not reliable.

And I assume that 'transitive' in this context means that if X is const then it's members are const, and in turn their members are const, ... all the way down. Does this mean that the syntax 'const char[] X' and 'const (char)[] X' are now the same thing?
 3) ditto (2) for invariant

Does that mean that if X is invariant then neither the bits in X and the bits referenced by X can be modified by anything?

Right.
 It should be working as one would intuitively expect it to.

If the answer is 'yes' to the couple of questions above ... (a) is there a form of the syntax that allows 'X' to be modified but not what it references?

Yes, transitive const applies downwards, not upwards.

What would that syntax be? Can you give examples for arrays, AA, classes and structs?
 (b) is there a form that prevents 'X' from being modified but allows what
 it references to be modified?

No. There is no language typing support for 'logical const' (i.e. const structures with mutable members). Many C++ people have argued passionately for logical const, but I have become strongly convinced that the notion is fatally flawed.

How would I specify that I have a buffer of bytes and I can't change its location but I can change its contents? -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 10:20:49 AM
Nov 26 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Mon, 26 Nov 2007 15:02:57 -0800, Walter Bright wrote:
 
 Derek Parnell wrote:

 2) no more 'head const' or 'tail const', it's all just 'const'

referenced by X can be modified (though the X symbol)?

to programmers establishing a *convention* for transitive const. But it being an unenforced convention means it is not reliable.

And I assume that 'transitive' in this context means that if X is const then it's members are const, and in turn their members are const, ... all the way down.

Yes.
 Does this mean that the syntax 'const char[] X' and 'const (char)[] X' are
 now the same thing?

No. "const char[] X;" and "const(char[]) X;" mean the same thing.
 3) ditto (2) for invariant

bits referenced by X can be modified by anything?

 It should be working as one would intuitively expect it to.

(a) is there a form of the syntax that allows 'X' to be modified but not what it references?


What would that syntax be? Can you give examples for arrays, AA, classes and structs?

Just put () around the part of the type you wish to be const. See above examples.
 (b) is there a form that prevents 'X' from being modified but allows what
 it references to be modified?

structures with mutable members). Many C++ people have argued passionately for logical const, but I have become strongly convinced that the notion is fatally flawed.

How would I specify that I have a buffer of bytes and I can't change its location but I can change its contents?

You can't. A good question would be what would be the purpose of such?
Nov 26 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 26 Nov 2007 16:36:17 -0800, Walter Bright wrote:

 Derek Parnell wrote:
 How would I specify that I have a buffer of bytes and I can't change its
 location but I can change its contents?

You can't. A good question would be what would be the purpose of such?

I am under the impression that this is a fairly common way of doing things. byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); ---- dchar[] Text; Text = ReadTextFile("collectedshakespeare.txt"); ConvertToLowercase(Text); ---- In other words, there maybe valid reasons to modify data in-place rather than create a modified copy of the data. -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 11:53:31 AM
Nov 26 2007
parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Derek Parnell wrote:
 I am under the impression that this is a fairly common way of doing things.
 
    byte[] bitmap;
 
    bitmap = LoadBitMapFromFile("worldroadmap.bmp");
    GammaAdjust(bitmap, 0.20);

void GammaAdjust(ref byte[] bitmap,float gamma) ? regards, frank
Nov 26 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
0ffh wrote:
 Derek Parnell wrote:
 I am under the impression that this is a fairly common way of doing 
 things.

    byte[] bitmap;

    bitmap = LoadBitMapFromFile("worldroadmap.bmp");
    GammaAdjust(bitmap, 0.20);

void GammaAdjust(ref byte[] bitmap,float gamma) ?

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); byte[] same_bitmap = bitmap; GammaAdjust(bitmap, 0.20); assert(bitmap.ptr is same_bitmap.ptr, "Error GammaAdjust not supposed to change the bitmap pointer"); Sometimes it's convenient to have more than one reference to the same buffer around. --bb
Nov 26 2007
next sibling parent 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Dr. Egon Spengler: Don't cross the streams. =)
Nov 26 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 0ffh wrote:
 Derek Parnell wrote:
 I am under the impression that this is a fairly common way of doing 
 things.

    byte[] bitmap;

    bitmap = LoadBitMapFromFile("worldroadmap.bmp");
    GammaAdjust(bitmap, 0.20);

void GammaAdjust(ref byte[] bitmap,float gamma) ?

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); byte[] same_bitmap = bitmap; GammaAdjust(bitmap, 0.20); assert(bitmap.ptr is same_bitmap.ptr, "Error GammaAdjust not supposed to change the bitmap pointer"); Sometimes it's convenient to have more than one reference to the same buffer around.

I'm not sure why one would need protection against changing the bitmap pointer. GammaAdjust should just take a byte[], not a ref byte[].
Nov 26 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 26 Nov 2007 21:24:13 -0800, Walter Bright wrote:

 I'm not sure why one would need protection against changing the bitmap 
 pointer. GammaAdjust should just take a byte[], not a ref byte[].

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); Render(device, bitmap); If the 'GammaAdjust' routine changed the pointer then 'Render' routine would not display the adjusted bitmap. So I would like to tell the compiler that 'GammaAdjust' is not allowed to change the pointer and thus if (at compile time) it does so the compiler should tell me of the error. -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 5:28:39 PM
Nov 26 2007
next sibling parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Janice Caron wrote:
 I guess it could change its own local copy of the pointer, but that
 would just be a bug local to the function.

One which, I would think, should be caught by the type system. However, if there's no 'final', I guess this can't be done. On the other hand, maybe the compiler should just emit a warning when a passed-by-value parameter is written to. If you really do want to change your local copy of a parameter you can always copy it to a variable. Thanks, Nathan Reed
Nov 26 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 26 Nov 2007 23:23:41 -0800, Nathan Reed wrote:

 Janice Caron wrote:
 I guess it could change its own local copy of the pointer, but that
 would just be a bug local to the function.

One which, I would think, should be caught by the type system. However, if there's no 'final', I guess this can't be done. On the other hand, maybe the compiler should just emit a warning when a passed-by-value parameter is written to. If you really do want to change your local copy of a parameter you can always copy it to a variable.

I'm talking about having the compiler help catch coding mistakes at compile time. In this case, the compiler can only know that it is a mistake to (attempt to) modify the pointer if the coder tells it that the design forbids that behaviour. Therefore we need to have form of syntax to help the compiler help the coder. -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 6:29:07 PM
Nov 26 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Derek Parnell wrote:
 On Mon, 26 Nov 2007 23:23:41 -0800, Nathan Reed wrote:
 
 Janice Caron wrote:
 I guess it could change its own local copy of the pointer, but that
 would just be a bug local to the function.

if there's no 'final', I guess this can't be done. On the other hand, maybe the compiler should just emit a warning when a passed-by-value parameter is written to. If you really do want to change your local copy of a parameter you can always copy it to a variable.

I'm talking about having the compiler help catch coding mistakes at compile time. In this case, the compiler can only know that it is a mistake to (attempt to) modify the pointer if the coder tells it that the design forbids that behaviour. Therefore we need to have form of syntax to help the compiler help the coder.

If you're worried about the implementer doing the wrong thing then you could argue that the same applies to all arguments. void foo(int x) { x = 10; /* oops! did I mean to do that? Or was I hoping the user would see it? */ } But there is a difference in that if you're really worried about that possibility then you can make it foo(const int), but without head const you can't do the same thing for just the pointer. But is it important enough to justify the extra complication? We get by in D1.0 without const at all, after all. Java gets by without it too. I'm ready to accept a const that can't express every nuance if it enables greater simplicity. --bb
Nov 27 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 But is it important enough to justify the extra complication?  We get by 
 in D1.0 without const at all, after all.  Java gets by without it too. 
 I'm ready to accept a const that can't express every nuance if it 
 enables greater simplicity.

It is clear that the previous implementation of const, while it allowed for very nuanced control, was worthless because it was too complicated. Going back and rethinking things, it is apparent that things like final only serve a local purpose, not an API one, and so have limited utility for their complexity.
Nov 27 2007
prev sibling next sibling parent Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 27 Nov 2007 06:54:40 +0000, Janice Caron wrote:

 On 11/27/07, Derek Parnell <derek nomail.afraid.org> wrote:
 On Mon, 26 Nov 2007 21:24:13 -0800, Walter Bright wrote:

 I'm not sure why one would need protection against changing the bitmap
 pointer. GammaAdjust should just take a byte[], not a ref byte[].

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); Render(device, bitmap); If the 'GammaAdjust' routine changed the pointer then 'Render' routine would not display the adjusted bitmap.

The GammaAdjust routine /cannot/ change the pointer, because it is passed in by value, not by reference. I guess it could change its own local copy of the pointer, but that would just be a bug local to the function.

I'm sorry I'm not getting my request out clear enough. I *know* that at run time the GammaAdjust routine cannot effectively change the pointer. But that is not what I'm saying. I would like the compiler to tell me at /compile/ time that I've incorrectly attempted to change a pointer that has been designed to remain unchanged. I would like to avoid waiting for this to only become apparent at /run/ time. -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 6:24:56 PM
Nov 26 2007
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Mon, 26 Nov 2007 21:24:13 -0800, Walter Bright wrote:
 
 I'm not sure why one would need protection against changing the bitmap 
 pointer. GammaAdjust should just take a byte[], not a ref byte[].

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); Render(device, bitmap); If the 'GammaAdjust' routine changed the pointer then 'Render' routine would not display the adjusted bitmap. So I would like to tell the compiler that 'GammaAdjust' is not allowed to change the pointer and thus if (at compile time) it does so the compiler should tell me of the error.

Change GammaAdjust from (ref byte[] bitmap) to (byte[] bitmap), and it cannot change the caller's copy.
Nov 27 2007
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Derek Parnell" wrote
 On Mon, 26 Nov 2007 21:24:13 -0800, Walter Bright wrote:

 I'm not sure why one would need protection against changing the bitmap
 pointer. GammaAdjust should just take a byte[], not a ref byte[].

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); Render(device, bitmap); If the 'GammaAdjust' routine changed the pointer then 'Render' routine would not display the adjusted bitmap. So I would like to tell the compiler that 'GammaAdjust' is not allowed to change the pointer and thus if (at compile time) it does so the compiler should tell me of the error.

I think you are missing something here. Let's view this as it really is, a pointer with a length: void GammaAdjust(byte *bitmap, int length, double adjustment) { // want to realloc bitmap? byte *tmp = bitmap; bitmap = malloc(length + 1); // don't really know the right way to do this :) memcpy(bitmap, tmp, length); bitmap[length] = 5; // work on bitmap... } Now does any reasonable C coder expect this to work correctly? no. It seems a bit fuzzy because in D we think of arrays as pointers, but they are really structures, passed by value. Let's assume your wish was granted and headconst was available. Now the code becomes: void GammaAdjust(headconst byte *bitmap, int length, double adjustment) { // want to realloc bitmap? oops! can't do it, I'll just use a different variable byte *tmp = malloc(length + 1); memcpy(tmp, bitmap, length); tmp[length] = 5; // work on tmp... } our coder has now realized that he cannot change the bitmap pointer, so he just allocates a new one. This is the same as the previous example, except now we just use a different variable. You have not solved the problem, and that is: any coder who does not understand the semantics of variable passing is not going to be saved by anything the compiler can enforce. I believe the second example is more obvious than the first, but both are still obvious to me, and even the example where byte[] is used is obvious to me :) I think what we need is a better explanation to new coders on how arrays work and are passed to functions. I agree with Walter's position regarding head const. -Steve
Nov 27 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 27 Nov 2007 09:41:03 -0500, Steven Schveighoffer wrote:

 "Derek Parnell" wrote
 On Mon, 26 Nov 2007 21:24:13 -0800, Walter Bright wrote:

 I'm not sure why one would need protection against changing the bitmap
 pointer. GammaAdjust should just take a byte[], not a ref byte[].

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); Render(device, bitmap); If the 'GammaAdjust' routine changed the pointer then 'Render' routine would not display the adjusted bitmap. So I would like to tell the compiler that 'GammaAdjust' is not allowed to change the pointer and thus if (at compile time) it does so the compiler should tell me of the error.

I think you are missing something here. Let's view this as it really is, a pointer with a length: void GammaAdjust(byte *bitmap, int length, double adjustment) { // want to realloc bitmap? byte *tmp = bitmap; bitmap = malloc(length + 1); // don't really know the right way to do this :) memcpy(bitmap, tmp, length); bitmap[length] = 5; // work on bitmap... } Now does any reasonable C coder expect this to work correctly? no.

Arrrgh! I'm talking about accidently or inadvertant attempts to change a pointer's value. I'm talking about allowing the compiler to help the coder. void GammaAdjust(byte* const(bitmap), int length, double adjustment) { // want to realloc bitmap? byte *tmp = bitmap; bitmap = malloc(length + 1); // BANG! Compiler shouldn't allow this. memcpy(bitmap, tmp, length); bitmap[length] = 5; // But this is okay. // work on bitmap... } -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Nov 27 2007
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Derek Parnell" wrote
 On Tue, 27 Nov 2007 09:41:03 -0500, Steven Schveighoffer wrote:

 "Derek Parnell" wrote
 On Mon, 26 Nov 2007 21:24:13 -0800, Walter Bright wrote:

 I'm not sure why one would need protection against changing the bitmap
 pointer. GammaAdjust should just take a byte[], not a ref byte[].

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); Render(device, bitmap); If the 'GammaAdjust' routine changed the pointer then 'Render' routine would not display the adjusted bitmap. So I would like to tell the compiler that 'GammaAdjust' is not allowed to change the pointer and thus if (at compile time) it does so the compiler should tell me of the error.

I think you are missing something here. Let's view this as it really is, a pointer with a length: void GammaAdjust(byte *bitmap, int length, double adjustment) { // want to realloc bitmap? byte *tmp = bitmap; bitmap = malloc(length + 1); // don't really know the right way to do this :) memcpy(bitmap, tmp, length); bitmap[length] = 5; // work on bitmap... } Now does any reasonable C coder expect this to work correctly? no.

Arrrgh! I'm talking about accidently or inadvertant attempts to change a pointer's value. I'm talking about allowing the compiler to help the coder.

I understood what you meant. My point is that the compiler cannot prevent *all* types of mistakes that you are talking about. So in my opinion, the benefit of including such a feature is very minimal compared to the complexity required to be added to the compiler. -Steve
Nov 27 2007
parent Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 27 Nov 2007 16:57:36 -0500, Steven Schveighoffer wrote:

 I understood what you meant.  My point is that the compiler cannot prevent 
 *all* types of mistakes that you are talking about.  So in my opinion, the 
 benefit of including such a feature is very minimal compared to the 
 complexity required to be added to the compiler.

Thank you. -- Derek (skype: derek.j.parnell) Melbourne, Australia 28/11/2007 10:46:15 AM
Nov 27 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 27 Nov 2007 10:48:42 +0900, Bill Baxter wrote:

 0ffh wrote:
 Derek Parnell wrote:
 I am under the impression that this is a fairly common way of doing 
 things.

    byte[] bitmap;

    bitmap = LoadBitMapFromFile("worldroadmap.bmp");
    GammaAdjust(bitmap, 0.20);

void GammaAdjust(ref byte[] bitmap,float gamma) ?

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); byte[] same_bitmap = bitmap; GammaAdjust(bitmap, 0.20); assert(bitmap.ptr is same_bitmap.ptr, "Error GammaAdjust not supposed to change the bitmap pointer");

Okay, I get it. We don't actually need const/invariant at all. Instead we just sprinkle our code with asserts to catch these compile-time errors at run-time. Neat. -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 5:37:49 PM
Nov 26 2007
parent reply Tomas Lindquist Olsen <tomas famolsen.dk> writes:
Derek Parnell wrote:
 
 Okay, I get it. We don't actually need const/invariant at all. Instead we
 just sprinkle our code with asserts to catch these compile-time errors at
 run-time. Neat.
 

I think this new const sounds like a reasonable compromise to get simple yet still useful const semantics. People will never agree 100% on how this should work, and comments like this one doesn't help anything. Walter already said there will be no head/tail const. Just const. Why bother asking how to do head const? (Or have I completely misunderstood things?)
Nov 26 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 27 Nov 2007 07:47:45 +0100, Tomas Lindquist Olsen wrote:

 Derek Parnell wrote:
 
 Okay, I get it. We don't actually need const/invariant at all. Instead we
 just sprinkle our code with asserts to catch these compile-time errors at
 run-time. Neat.
 

I think this new const sounds like a reasonable compromise to get simple yet still useful const semantics. People will never agree 100% on how this should work, and comments like this one doesn't help anything.

I agree that the const as now proposed is simple and still useful. I'm sorry you see my comment as unhelpful.
 Walter already said there will be no head/tail const. Just const. Why bother
asking how to do 
 head const?
 
 (Or have I completely misunderstood things?)

You have not misunderstood. As I'm sure Walter will be the first to agree, he is not all-knowing. I believe that there has been times when Walter has said "X", defended "X" vigorously against "Y", but then something happens and we get "Y" implemented instead. I think that Walter is saying that he (currently), after much thought and deliberation, has decided that there is no benefit to any coder in implementing the head const paradigm in D. Therefore those unfortunates such as myself, that can't yet grasp why there is no benefit, may feel obliged to continue asking for an explanation or assistance about how we could implement head const without the compiler helping us. -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/11/2007 5:55:45 PM
Nov 26 2007
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 I think that Walter is saying that he (currently), after much thought and
 deliberation, has decided that there is no benefit to any coder in
 implementing the head const paradigm in D. Therefore those unfortunates
 such as myself, that can't yet grasp why there is no benefit, may feel
 obliged to continue asking for an explanation or assistance about how we
 could implement head const without the compiler helping us.

Head const turned out to be an unexplainable, incomprehensible feature. A feature that is incomprehensible is unusable, unworkable, and a complete waste of time. It's also become apparent that a lot of people stick with D 1.0, and refuse to move to 2.0, because of the incomprehensibility of head/tail const. Head const (aka 'final'), had another fatal flaw. If you took the address of it, you had a const that was not transitive.
Nov 27 2007
prev sibling parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Derek Parnell wrote:
 On Tue, 27 Nov 2007 07:47:45 +0100, Tomas Lindquist Olsen wrote: [...] I
 think that Walter is saying that he (currently), after much thought and 
 deliberation, has decided that there is no benefit to any coder in 
 implementing the head const paradigm in D. Therefore those unfortunates 
 such as myself, that can't yet grasp why there is no benefit, may feel 
 obliged to continue asking for an explanation or assistance about how we
  could implement head const without the compiler helping us.

Well, I don't quite see your problem with assert. Earlier you said:
 Okay, I get it. We don't actually need const/invariant at all. Instead
 we just sprinkle our code with asserts to catch these compile-time
 errors at run-time. Neat.

I think you forget (or ignore) something elemental here, which is that runtime not equals runtime, and that you'll use you asserts only to make sure at "test/debug"-runtime that you (and all of your co-workers) didn't fup it. I postulate that even with head const available, you'll still have to test your software. Or do you go "Ah, finally it compiles! Call in the delivery boy!"? I think the difference is not so grand as you make it. regards, frank
Nov 27 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 27 Nov 2007 12:12:51 +0100, 0ffh wrote:

 Derek Parnell wrote:
 On Tue, 27 Nov 2007 07:47:45 +0100, Tomas Lindquist Olsen wrote: [...] I
 think that Walter is saying that he (currently), after much thought and 
 deliberation, has decided that there is no benefit to any coder in 
 implementing the head const paradigm in D. Therefore those unfortunates 
 such as myself, that can't yet grasp why there is no benefit, may feel 
 obliged to continue asking for an explanation or assistance about how we
  could implement head const without the compiler helping us.

Well, I don't quite see your problem with assert. Earlier you said:
 Okay, I get it. We don't actually need const/invariant at all. Instead
 we just sprinkle our code with asserts to catch these compile-time
 errors at run-time. Neat.

I think you forget (or ignore) something elemental here, which is that runtime not equals runtime, and that you'll use you asserts only to make sure at "test/debug"-runtime that you (and all of your co-workers) didn't fup it.

Neither did I forget nor ignore. My 'assert' comment was a (poor) example of sarcasm. I didn't really mean it. I just said it as an extreme statement to highlight that I thought it was a idea not worthy of further consideration.
 I postulate that even with head const available, you'll still have
 to test your software. Or do you go "Ah, finally it compiles! Call in the
 delivery boy!"?

And why do you assume that I think otherwise?
  I think the difference is not so grand as you make it.

I do not think it as "so grand", whatever that means. My point is that any help that a compiler can give to the coder in preventing coding errors is a worthwhile pursuit. Sure, there are diminishing returns and maybe this is one of them. I'm not sure because I have not analyzed any empirical statistics. I am simply saying, and this seems to be very hard to get across, that if there is some syntax to express the design concept of "the pointer may not be modified but the items pointed to can be", then the compiler can detect at compile time when the coder attempts to change the pointer, and issue the appropriate warning. A compiler exists to make life easier for the coder. If this truely is a case where "Head const turned out to be an unexplainable, incomprehensible feature." and/or is too hard to implement, then I'll go with the flow. I haven't seen evidence of that being the case yet, regardless of Walter's statements so far. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Nov 27 2007
next sibling parent 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Derek Parnell wrote:
 On Tue, 27 Nov 2007 12:12:51 +0100, 0ffh wrote:
  I think the difference is not so grand as you make it.

I do not think it as "so grand", whatever that means. My point is that any help that a compiler can give to the coder in preventing coding errors is a worthwhile pursuit. Sure, there are diminishing returns and maybe this is one of them. I'm not sure because I have not analyzed any empirical statistics.

Neither am I. But I've lived a reasonably long life coding without "const" of any kind, so I really wonder what all the fuzz is about, when a simple, straightforward implementation (probably better than none) is not enough.
 I am simply saying, and this seems to be very hard to get across, that if
 there is some syntax to express the design concept of "the pointer may not
 be modified but the items pointed to can be", then the compiler can detect
 at compile time when the coder attempts to change the pointer, and issue
 the appropriate warning.

Well, the point is not so hard to get across as you think. I agree that, ideally, any coding error should be flagged by the compiler. I suppose we agree that this gets hard as the potential errors get nontrivial. Give me some room to hyperbole, and I'll tell you this: We can make a super coding-error resistant language by just requiring that every function must be coded in triplicate, in different languages. The compiler will check if all three implementations are identical, otherwise it'll fail with an error. This is where you're heading, albeit from a long way away. =)
 A compiler exists to make life easier for the coder.
 If this truely is a case where "Head const turned out to be an
 unexplainable, incomprehensible feature." and/or is too hard to implement,
 then I'll go with the flow. I haven't seen evidence of that being the case
 yet, regardless of Walter's statements so far.

Actually, I'll now change hats and tell you my opinion: Simple features should be easily reachable, advanced features should exist. So I think that simple const as is now planned should be reachable using a simple syntax, and advanced concepts of const should ideally be possible, but in case of doubt by using a more complicated syntax. I don't think that the last const-proposal by Walter was really much too complicated. The problem is more that most people here were immediately trying to grok it in all it's aspects, and some were a bit turned off that some of those tries have not met instant gratification. And apart from all that - from a /very/ personal POV - I don't need it. =) regards, frank
Nov 27 2007
prev sibling next sibling parent "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
"Derek Parnell" <derek psych.ward> kirjoitti viestissä 
news:1vst9y1mnh6kz.tkg1phefemws$.dlg 40tude.net...

 My point is that any help that a compiler can give to the coder in
 preventing coding errors is a worthwhile pursuit. Sure, there are
 diminishing returns and maybe this is one of them. I'm not sure because I
 have not analyzed any empirical statistics.

 I am simply saying, and this seems to be very hard to get across, that if
 there is some syntax to express the design concept of "the pointer may not
 be modified but the items pointed to can be", then the compiler can detect
 at compile time when the coder attempts to change the pointer, and issue
 the appropriate warning.

 A compiler exists to make life easier for the coder.

 If this truely is a case where "Head const turned out to be an
 unexplainable, incomprehensible feature." and/or is too hard to implement,
 then I'll go with the flow. I haven't seen evidence of that being the case
 yet, regardless of Walter's statements so far.

I believe head const is a feature that should exist. I'm just a bit unsure of how it should be integrated with the new version of const, since the main thing, IMO, is that it should be as simple as possible. We can, I think, basically separate constness into four categories (I'm not talking about invariants, just head and tail const): 1: no constness 2: tail constness 3: head constness 4: head and tail constness The syntax for cases 1 and 2 are now getting quite clear (I'll just use "ref" here in these examples): 1: void foo(ref byte[] bar) 2: void foo(ref const(byte[]) bar) - at least I think it would be this way... :) The cases for 3 and 4 could just basically be an extension of this, with the "ref" keyword basically required to mark head constness as follows: 3: void foo(const(ref) byte[] bar) 4: void foo(const(ref) const(byte[]) bar) I admit these cases looks ugly, but at least they tell exactly what is going on: the reference itself is constant, and in case four the type, as well. (As a side-note, I was at first considering the following syntax: 3: void foo(const(ref byte[]) bar) 4: void foo(const(ref const(byte[])) bar) but I believe the last case to be even more uglier than in the variant above and case 3 to be ambivalent: is the intent to make the reference constant, or the reference and the byte[] itself?)
Nov 28 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 If this truely is a case where "Head const turned out to be an
 unexplainable, incomprehensible feature." and/or is too hard to implement,
 then I'll go with the flow. I haven't seen evidence of that being the case
 yet, regardless of Walter's statements so far.

The only person I know of who understood it was Andrei. This is despite many hours spent trying to explain it with pencil and paper, on whiteboards (at the D conference), and on the n.g. Now, I'll be the first to say I stink at explaining concepts to people, but I watched Andrei try and fail at this repeatedly, and Andrei is very good at explaining things. I do not know why it is so hard to explain, but it is, and it's clear I was bashing my head on a rock with it, and that rock was going to sink D 2.0 if it wasn't changed.
Nov 28 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 28 Nov 2007 17:56:37 -0800, Walter Bright wrote:

 Derek Parnell wrote:
  ... Head const ...


 The only person I know of who understood it was Andrei.

 I do not know why it is so hard to explain, but it is ...

I though it was simply "the pointer can't change but what it points to can". Am I missing the true definition of Head Const here? If so, can someone please have a go at bringing me up to speed. If not, is that really so hard to explain? Also, what is the syntax now for declaring a const function that returns a const value? -- Derek (skype: derek.j.parnell) Melbourne, Australia 29/11/2007 2:29:41 PM
Nov 28 2007
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Derek Parnell" wrote
 Also, what is the syntax now for declaring a const function that returns a
 const value?

const const(int)* f(){ ... } or const(int)* f() const { ... } This REALLY REALLY needs an example in the docs. Walter, please do something about this. The const void f() example doesn't cut it, especially for C++ defectors. I had to ask this question a while back in the learn NG after carefully examining the docs :) Also, will the following compile? const int* f1() const {...} invariant int* f2() const {...} And if they do, what do they mean? -Steve
Nov 28 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 "Derek Parnell" wrote
 Also, what is the syntax now for declaring a const function that returns a
 const value?

const const(int)* f(){ ... } or const(int)* f() const { ... } This REALLY REALLY needs an example in the docs. Walter, please do something about this. The const void f() example doesn't cut it, especially for C++ defectors. I had to ask this question a while back in the learn NG after carefully examining the docs :) Also, will the following compile?

Yes.
 const int* f1() const {...}

A const function returning an int*. The const here is just redundant.
 invariant int* f2() const {...}

An invariant function returning an int*. The invariant overrides the const.
 And if they do, what do they mean?

Nov 28 2007
parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Thu, 29 Nov 2007 05:02:29 -0000, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Steven Schveighoffer wrote:

 invariant int* f2() const {...}

An invariant function returning an int*. The invariant overrides the const.

to change any contents of the object. An invariant method makes the same promise. So there is no difference to the caller of such a method. I can't see that it make any difference inside the method either. Regards, Bruce.
Nov 29 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Steven Schveighoffer wrote:
 "Derek Parnell" wrote
 Also, what is the syntax now for declaring a const function that returns a
 const value?

const const(int)* f(){ ... } or const(int)* f() const { ... } This REALLY REALLY needs an example in the docs. Walter, please do something about this. The const void f() example doesn't cut it, especially for C++ defectors. I had to ask this question a while back in the learn NG after carefully examining the docs :) Also, will the following compile? const int* f1() const {...}

Currently: Yes if f1 returns the address of a global or 'null'. No if it tries to return pointer to member of the class. Should probably be: No always. (Since "const const int* f1" is a "repeated storage class error", putting the redundant const at the end should be an error too, I think)
 invariant int* f2() const {...}

Same thing. Yes if you return a pointer that's not part of the class scope. No if you return something that is class scope. Should also be no on both counts I think.
 And if they do, what do they mean?

They mean the compiler is too leniant. :-) --bb
Nov 28 2007
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 If this truely is a case where "Head const turned out to be an
 unexplainable, incomprehensible feature." and/or is too hard to 
 implement,
 then I'll go with the flow. I haven't seen evidence of that being the 
 case
 yet, regardless of Walter's statements so far.

The only person I know of who understood it was Andrei. This is despite many hours spent trying to explain it with pencil and paper, on whiteboards (at the D conference), and on the n.g. Now, I'll be the first to say I stink at explaining concepts to people, but I watched Andrei try and fail at this repeatedly, and Andrei is very good at explaining things. I do not know why it is so hard to explain, but it is, and it's clear I was bashing my head on a rock with it, and that rock was going to sink D 2.0 if it wasn't changed.

My issue with the design wasn't so much with the conceptual complexity (though this was still a bit high) as with the complexity it promised to add to code. The const features in C++ are simpler from a semantic standpoint than the proposed const features in D, and yet they can complicate code tremendously. With D I would prefer to sacrifice a bit of functionality in exchange for simpler code. Sean
Nov 29 2007
prev sibling parent Georg Wrede <georg nospam.org> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 
 If this truely is a case where "Head const turned out to be an 
 unexplainable, incomprehensible feature." and/or is too hard to 
 implement, then I'll go with the flow. I haven't seen evidence of
 that being the case yet, regardless of Walter's statements so far.

The only person I know of who understood it was Andrei. This is despite many hours spent trying to explain it with pencil and paper, on whiteboards (at the D conference), and on the n.g. Now, I'll be the first to say I stink at explaining concepts to people, but I watched Andrei try and fail at this repeatedly, and Andrei is very good at explaining things. I do not know why it is so hard to explain, but it is, and it's clear I was bashing my head on a rock with it, and that rock was going to sink D 2.0 if it wasn't changed.

I do. (Appologies, but:) "Poorly understood is poorly explained." Even if it was cosmology, black holes, or the behavior of your neighbor; once thoroughly understood, you should be able to convey it in understandable terms to your audience. So, either Andrei has to rethink the whole thing, or we have to assume that the state of the art is, er, diffuse. NO offense.
Dec 17 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:

 How would I specify that I have a buffer of bytes and I can't change its
 location but I can change its contents?

You can't. A good question would be what would be the purpose of such?

I pass you a buffer that I've carefully pre-allocated for you to fill in. You shouldn't reallocate it but you should modify its contents. Having the function signature say "takes const-pointer-to-mutable-data" makes it clear that the function won't be allocating the buffer for you or reallocating it out from under you. Just passing the pointer by value is ok from the caller's perspective (you can't change what *my* pointer points to) but from the maintenance programmer's perspective there's nothing to prevent accidentally re-assigning that mutable pointer to some other buffer inside the function and incorrectly making changes there. --bb (who was just in the process of writing such a modify-this-buffer function in D1, sans const)
Nov 26 2007
next sibling parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 
 How would I specify that I have a buffer of bytes and I can't change its
 location but I can change its contents?

You can't. A good question would be what would be the purpose of such?

I pass you a buffer that I've carefully pre-allocated for you to fill in. You shouldn't reallocate it but you should modify its contents. Having the function signature say "takes const-pointer-to-mutable-data" makes it clear that the function won't be allocating the buffer for you or reallocating it out from under you. [...] --bb (who was just in the process of writing such a modify-this-buffer function in D1, sans const)

Okay, that's a feature. But wouldn't an assertion do the same for you, just a bit more wordy? regards, frank
Nov 26 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
0ffh wrote:
 Bill Baxter wrote:
 Walter Bright wrote:

 How would I specify that I have a buffer of bytes and I can't change 
 its
 location but I can change its contents?

You can't. A good question would be what would be the purpose of such?

I pass you a buffer that I've carefully pre-allocated for you to fill in. You shouldn't reallocate it but you should modify its contents. Having the function signature say "takes const-pointer-to-mutable-data" makes it clear that the function won't be allocating the buffer for you or reallocating it out from under you. [...] --bb (who was just in the process of writing such a modify-this-buffer function in D1, sans const)

Okay, that's a feature. But wouldn't an assertion do the same for you, just a bit more wordy?

More wordy and not checked at compile time. But it may be that it's not important enough to warrant language support. --bb
Nov 26 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 
 How would I specify that I have a buffer of bytes and I can't change its
 location but I can change its contents?

You can't. A good question would be what would be the purpose of such?

I pass you a buffer that I've carefully pre-allocated for you to fill in. You shouldn't reallocate it but you should modify its contents.

Then just pass in the buffer[]. No need to pass *buffer* by reference.
Nov 26 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 Walter Bright wrote:

 How would I specify that I have a buffer of bytes and I can't change 
 its
 location but I can change its contents?

You can't. A good question would be what would be the purpose of such?

I pass you a buffer that I've carefully pre-allocated for you to fill in. You shouldn't reallocate it but you should modify its contents.

Then just pass in the buffer[]. No need to pass *buffer* by reference.

void foo(char[] pbuffer) { //assume this causes reallocation //assume there is no more space in place pbuffer ~= "a"; } void main() { char[] buffer = "test".dup; foo(buffer); buffer[0] = 'a' //crash } Right? Regan
Nov 27 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }
 
 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }
 
 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.
Nov 27 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

Yes, but the underlying memory is reallocated so buffer no longer points to valid memory, right? Regan
Nov 27 2007
next sibling parent reply Frank Benoit <keinfarbton googlemail.com> writes:
Regan Heath schrieb:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

Yes, but the underlying memory is reallocated so buffer no longer points to valid memory, right? Regan

You missed a semicolon, so it does not compile :) Realloc does not destroy the original buffer. So your foo just make a modified copy, that is not visible for the caller. buffer will stay unchanged.
Nov 27 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Frank Benoit wrote:
 Regan Heath schrieb:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?


to valid memory, right? Regan

You missed a semicolon, so it does not compile :) Realloc does not destroy the original buffer. So your foo just make a modified copy, that is not visible for the caller. buffer will stay unchanged.

'realloc' (the ANSI C version) free's the original buffer. Are you saying that when D extends an array buffer, and there is no space to extend in place, it always creates a 2nd copy of the data and leaves the original copy untouched? Because, I was under the impression that completely new copies were only made on concatenation in this form: char[] a = b ~ c; I'll try and code up a test case that exhibits the behaviour I'm worried about, but it's hard when I don't really have control of the array memory allocation/re-allocation itself.. or do I.. I wonder if this would fix the problem: void foo(const(char[]) pbuffer) { ... } would reallocation of the data pointer in this array be prevented? If so, I reckon we should be passing either: const(char[]) array ref char[] array; and never: char[] array Regan
Nov 27 2007
parent Regan Heath <regan netmail.co.nz> writes:
Regan Heath wrote:
 Are you saying that when D extends an array buffer, and there is no 
 space to extend in place, it always creates a 2nd copy of the data and 
 leaves the original copy untouched?

Seems like you were right about this. This seems to be the code in internal\gc\gc.d if (newlength > p.length) { size_t size = p.length * sizeelem; size_t cap = _gc.capacity(p.data); if (cap <= newsize) { if (cap >= 4096) { // Try to extend in-place auto u = _gc.extend(p.data, (newsize + 1) - cap, (newsize + 1) - cap); if (u) { goto L1; } } newdata = cast(byte *)_gc.malloc(newsize + 1); newdata[0 .. size] = p.data[0 .. size]; if (!(ti.next.flags() & 1)) _gc.hasNoPointers(newdata); } L1: newdata[size .. newsize] = 0; } Regan
Nov 27 2007
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Frank Benoit, el 27 de noviembre a las 11:01 me escribiste:
 Regan Heath schrieb:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

Yes, but the underlying memory is reallocated so buffer no longer points to valid memory, right? Regan

You missed a semicolon, so it does not compile :) Realloc does not destroy the original buffer. So your foo just make a modified copy, that is not visible for the caller. buffer will stay unchanged.

I think you are missing an important point here: string concatenation is a fairly common case where is really obscure to figure out if the undelying pointer gets changed or not. When a user writes pbuffer ~= "a"; it's not very intuitive to expect a realocation and pbuffer.ptr change. This could lead to some hard to find bugs, because of the lack of explicitness and non-determinism (so to speak) where pbuffer ~= "a"; could lead or not to reallocation (or the fact that pbuffer[0] = 'a' will survive the local function and concatenation *may* survive or may not). -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Hey you, would you help me to carry the stone? Open your heart, I'm coming home.
Nov 27 2007
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Leandro Lucarella wrote:
 Frank Benoit, el 27 de noviembre a las 11:01 me escribiste:
 Regan Heath schrieb:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?


to valid memory, right? Regan

Realloc does not destroy the original buffer. So your foo just make a modified copy, that is not visible for the caller. buffer will stay unchanged.

I think you are missing an important point here: string concatenation is a fairly common case where is really obscure to figure out if the undelying pointer gets changed or not. When a user writes pbuffer ~= "a"; it's not very intuitive to expect a realocation and pbuffer.ptr change.

Though in the calling method, the buffer length wouldn't change, so if there isn't a realloc, the change is still lost.
 This could lead to some hard to find bugs, because of the lack of
 explicitness and non-determinism (so to speak) where pbuffer ~= "a"; could
 lead or not to reallocation (or the fact that pbuffer[0] = 'a' will
 survive the local function and concatenation *may* survive or may not).
 
 

Nov 27 2007
prev sibling next sibling parent Regan Heath <regan netmail.co.nz> writes:
Leandro Lucarella wrote:
 Frank Benoit, el 27 de noviembre a las 11:01 me escribiste:
 Regan Heath schrieb:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?


to valid memory, right? Regan

Realloc does not destroy the original buffer. So your foo just make a modified copy, that is not visible for the caller. buffer will stay unchanged.

I think you are missing an important point here:

In fact I missed it too! I was actually more concerned that buffer would not longer point to valid memory. I eventually figured out that D does not reallocate the original pointer unless it can reallocate in place. If it cannot reallocate in place it creates a new copy of the data.
 This could lead to some hard to find bugs, because of the lack of
 explicitness and non-determinism (so to speak) where pbuffer ~= "a"; could
 lead or not to reallocation (or the fact that pbuffer[0] = 'a' will
 survive the local function and concatenation *may* survive or may not).

Actually concatenation wont survive either because buffer.length is not changed by the concatenation. eg. void foo(char[] pbuffer) { pbuffer ~= "a"; } void main() { char[] buffer = "test".dup; foo(buffer); writefln(buffer); } This will always output "test". It would only become non-deterministic if you coded: void main() { char[] buffer = "test".dup; foo(buffer); buffer = buffer.ptr[0..buffer.length+1] writefln(buffer); } as 99 times out of 100 you would get "testa" now, but the one time it cannot do an in place reallocation in foo there will be no "a" at the memory address following "test". As for: void foo(char[] pbuffer) { pbuffer[0] = 'a'; } this modification survives, but can be prevented with: void foo(invariant(char)[] pbuffer) { pbuffer[0] = 'a'; //error } and "string" will continue to be an alias for invariant(char)[] I imagine. In short, ignore me I was throwing crimson coloured fish in the air. Regan
Nov 27 2007
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Leandro Lucarella" wrote
 Frank Benoit, el 27 de noviembre a las 11:01 me escribiste:
 Regan Heath schrieb:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

Yes, but the underlying memory is reallocated so buffer no longer points to valid memory, right? Regan

You missed a semicolon, so it does not compile :) Realloc does not destroy the original buffer. So your foo just make a modified copy, that is not visible for the caller. buffer will stay unchanged.

I think you are missing an important point here: string concatenation is a fairly common case where is really obscure to figure out if the undelying pointer gets changed or not. When a user writes pbuffer ~= "a"; it's not very intuitive to expect a realocation and pbuffer.ptr change. This could lead to some hard to find bugs, because of the lack of explicitness and non-determinism (so to speak) where pbuffer ~= "a"; could lead or not to reallocation (or the fact that pbuffer[0] = 'a' will survive the local function and concatenation *may* survive or may not).

It is very deterministic actually. From http://www.digitalmars.com/d/arrays.html "Concatenation always creates a copy of its operands, even if one of the operands is a 0 length array" -Steve
Nov 27 2007
parent Regan Heath <regan netmail.co.nz> writes:
Steven Schveighoffer wrote:
 "Leandro Lucarella" wrote
 Frank Benoit, el 27 de noviembre a las 11:01 me escribiste:
 Regan Heath schrieb:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

buffer.

points to valid memory, right? Regan

Realloc does not destroy the original buffer. So your foo just make a modified copy, that is not visible for the caller. buffer will stay unchanged.

fairly common case where is really obscure to figure out if the undelying pointer gets changed or not. When a user writes pbuffer ~= "a"; it's not very intuitive to expect a realocation and pbuffer.ptr change. This could lead to some hard to find bugs, because of the lack of explicitness and non-determinism (so to speak) where pbuffer ~= "a"; could lead or not to reallocation (or the fact that pbuffer[0] = 'a' will survive the local function and concatenation *may* survive or may not).

It is very deterministic actually. From http://www.digitalmars.com/d/arrays.html "Concatenation always creates a copy of its operands, even if one of the operands is a 0 length array"

True, but the example after that statement is: a = b ~ c[0..0]; whereas foo is doing: a ~= b; which does not necessarily have to make a copy, it could reallocate 'a'. An in fact it does. It either reallocates if it can do so in place or creates a new copy. That plus the fact that the callers array length never changes means you're right, it's deterministic. Regan
Nov 27 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Regan Heath wrote:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

Yes, but the underlying memory is reallocated so buffer no longer points to valid memory, right?

No, a new copy is made. The old one is left alone.
Nov 27 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

So we'll have pass by value for arrays? Nifty. Sean
Nov 27 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

So we'll have pass by value for arrays? Nifty.

Arrgh! There are two things here, the length/ptr, and the array contents. The former is passed by value.
Nov 27 2007
parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 Regan Heath wrote:
 void foo(char[] pbuffer)
 {
   //assume this causes reallocation
   //assume there is no more space in place
   pbuffer ~= "a";
 }

 void main()
 {
   char[] buffer = "test".dup;
   foo(buffer);
   buffer[0] = 'a' //crash
 }

 Right?

No. foo() modifies its copy of pbuffer, which is not the same as buffer.

So we'll have pass by value for arrays? Nifty.

Arrgh! There are two things here, the length/ptr, and the array contents. The former is passed by value.

Oops! I totally misunderstood. This is yet another reminder of why I try to avoid posting so early in the morning. One of these days the lesson will sink in. Sean
Nov 27 2007
prev sibling parent 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 [...]
 How would I specify that I have a buffer of bytes and I can't change its
 location but I can change its contents?

You can't. A good question would be what would be the purpose of such?

Well, it might make sense with a moving GC, I suppose; which we won't have anyway, as long as we have void[] or malloc, and I think I wouldn't want to miss those. regards, frank
Nov 26 2007
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 On Mon, 26 Nov 2007 14:11:25 -0800, Walter Bright wrote:

 Jason House wrote:
 Walter Bright Wrote:

 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?


what was originally posted in the "const is broken" thread? Or are there new surprises in store for us?

1) no more final for variables

Is the keyword in this context now a syntax error?

It's just ignored.
 2) no more 'head const' or 'tail const', it's all just 'const'

Does that mean that if X is const then neither the bits in X and the bits referenced by X can be modified (though the X symbol)?

Const and invariant are transitive. C++ const is not transitive, leading to programmers establishing a *convention* for transitive const. But it being an unenforced convention means it is not reliable.
 3) ditto (2) for invariant

Does that mean that if X is invariant then neither the bits in X and the bits referenced by X can be modified by anything?

Right.
 It should be working as one would intuitively expect it to.

If the answer is 'yes' to the couple of questions above ... (a) is there a form of the syntax that allows 'X' to be modified but not what it references?

Yes, transitive const applies downwards, not upwards.
 (b) is there a form that prevents 'X' from being modified but allows what
 it references to be modified?

No. There is no language typing support for 'logical const' (i.e. const structures with mutable members). Many C++ people have argued passionately for logical const, but I have become strongly convinced that the notion is fatally flawed.

I like it! Simple, yet it does what it needs to do.
Nov 26 2007
prev sibling parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Many C++ people have argued
 passionately for logical const, but I have become strongly convinced
 that the notion is fatally flawed.

How about a constant/immutable object that refers to and uses (but does not contain) a mutable object? That's rather a common need, and it would be a shame not to be able to declare some object as being unchanging simply because it changes other objects' states, or to have to pass references to those other objects in to functions instead of holding them in member variables? -- James
Dec 17 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
James Dennett wrote:
 Walter Bright wrote:
 Many C++ people have argued
 passionately for logical const, but I have become strongly convinced
 that the notion is fatally flawed.

How about a constant/immutable object that refers to and uses (but does not contain) a mutable object? That's rather a common need, and it would be a shame not to be able to declare some object as being unchanging simply because it changes other objects' states, or to have to pass references to those other objects in to functions instead of holding them in member variables?

But the compiler needs to assume that every reference could be an owned reference since it doesn't know who you consider to be the "owner". --bb
Dec 17 2007
parent reply Jason House <jason.james.house gmail.com> writes:
Bill Baxter Wrote:

 James Dennett wrote:
 Walter Bright wrote:
 Many C++ people have argued
 passionately for logical const, but I have become strongly convinced
 that the notion is fatally flawed.

How about a constant/immutable object that refers to and uses (but does not contain) a mutable object? That's rather a common need, and it would be a shame not to be able to declare some object as being unchanging simply because it changes other objects' states, or to have to pass references to those other objects in to functions instead of holding them in member variables?

But the compiler needs to assume that every reference could be an owned reference since it doesn't know who you consider to be the "owner".

Is it really about ownership? That tends to imply to me that this would affect the garbage collector. I think most posts on the topic look for exceptions to the transitive const rule. i.e. where a pointer/class reference can point to some non-const data. I don't fully appreciate the trouble having something like this would cause the compiler. For parallelization of invariant objects, it seems about as bad as using a global variable.
Dec 17 2007
parent James Dennett <jdennett acm.org> writes:
Jason House wrote:
 Bill Baxter Wrote:
 
 James Dennett wrote:
 Walter Bright wrote:
 Many C++ people have argued
 passionately for logical const, but I have become strongly convinced
 that the notion is fatally flawed.

uses (but does not contain) a mutable object? That's rather a common need, and it would be a shame not to be able to declare some object as being unchanging simply because it changes other objects' states, or to have to pass references to those other objects in to functions instead of holding them in member variables?

reference since it doesn't know who you consider to be the "owner".

Is it really about ownership?

More about "containment", but that implies ownership, and D does not directly model containment as it uses reference semantics for class types.
 That tends to imply to me that this would affect the garbage collector.

I don't think so.
 I think most posts on the topic look for exceptions to the transitive
 const rule.  i.e. where a pointer/class reference can point to some
 non-const data. 

No "looking for" such exceptions: merely pointing out that they arise naturally in *designs* which take account of mutability, and hoping for tools which allow us to express natural designs to the compiler in a way that allows it to help to verify that the code does what our design calls for.
 I don't fully appreciate the trouble having something like this
 would cause the compiler.  For parallelization of invariant
 objects, it seems about as bad as using a global variable.

Not so different. There really are different forces at work here: from an implementor's perspective, "transitive const" is a much more convenient beast. From a software designer's view, it's a rather blunt tool compared to C++'s const. It might or might not be a pragmatic trade-off, but I think the battle for a const which is as designer-friendly as C++'s has been lost already. -- James
Dec 17 2007
prev sibling next sibling parent reply Victor Tyurin <eaglux gmail.com> writes:
Walter, please, explain us, why you don't post any thoughts on important
     language features like const?
Why you don't tell us about concepts you want to change in constness?
I think it will be very useful to discuss important features with
community, before those will be implemented.
As you can see, consts and invariants was not successful, i think,
because it has not been discussed on conceptual stage...

 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?

Yes, it's about done.
 I'm using D 2.0 in some things, and would hate to learn to do it X way,
 only to have it changed on me.

While the semantic changes are extensive, I've found to my surprise that little has to change in source code that uses const.

Nov 26 2007
parent Denton Cockburn <diboss hotmail.com> writes:
On Mon, 26 Nov 2007 23:43:21 +0300, Victor Tyurin wrote:

Well, we did discuss const/final/invariant quite a bit before it was
actually implemented.

I do agree though that we should be able to talk about it, even if we do
not play a role in the decision making.  Discussing these things helps
gain us more in-depth understanding, such that we are better prepared to
adjust to the changes.

 Walter, please, explain us, why you don't post any thoughts on important
      language features like const?
 Why you don't tell us about concepts you want to change in constness? I
 think it will be very useful to discuss important features with
 community, before those will be implemented. As you can see, consts and
 invariants was not successful, i think, because it has not been
 discussed on conceptual stage...
 
 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?

Yes, it's about done.
 I'm using D 2.0 in some things, and would hate to learn to do it X
 way, only to have it changed on me.

While the semantic changes are extensive, I've found to my surprise that little has to change in source code that uses const.


Nov 26 2007
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 26 Nov 2007 11:32:44 -0800, Walter Bright wrote:

 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?


 While the semantic changes are extensive, I've found to my surprise that 
 little has to change in source code that uses const.

Prior to releasing the new version, will you tell us what the semantic changes are? It would be useful to know the paradigm, and to discuss it, in order to be fully 'aware' before we start making any source code changes for it. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Nov 26 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?
 
 I'm using D 2.0 in some things, and would hate to learn to do it X way,
 only to have it changed on me.

You can get a preview of the new way things are going to look by browsing the latest source of Phobos from dsource. I didn't spend too much looking through the changes, but it looked like D is going to be switching to the C++ style of flagging 'this' as const by putting 'const' at the end of the function signature rather than the beginning. --bb
Nov 26 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?

 I'm using D 2.0 in some things, and would hate to learn to do it X way,
 only to have it changed on me.

You can get a preview of the new way things are going to look by browsing the latest source of Phobos from dsource. I didn't spend too much looking through the changes, but it looked like D is going to be switching to the C++ style of flagging 'this' as const by putting 'const' at the end of the function signature rather than the beginning.

At the beginning will still work for function types. The at the end option is there for those who are building complex type declarations.
Nov 26 2007
next sibling parent Extrawurst <spam extrawurst.org> writes:
Walter Bright schrieb:
 Bill Baxter wrote:
 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?

 I'm using D 2.0 in some things, and would hate to learn to do it X way,
 only to have it changed on me.

You can get a preview of the new way things are going to look by browsing the latest source of Phobos from dsource. I didn't spend too much looking through the changes, but it looked like D is going to be switching to the C++ style of flagging 'this' as const by putting 'const' at the end of the function signature rather than the beginning.

At the beginning will still work for function types. The at the end option is there for those who are building complex type declarations.

Nov 26 2007
prev sibling next sibling parent reply Extrawurst <spam extrawurst.org> writes:
Walter Bright schrieb:
 Bill Baxter wrote:
 Denton Cockburn wrote:
 Any update on the status of the const/invariant changes?

 I'm using D 2.0 in some things, and would hate to learn to do it X way,
 only to have it changed on me.

You can get a preview of the new way things are going to look by browsing the latest source of Phobos from dsource. I didn't spend too much looking through the changes, but it looked like D is going to be switching to the C++ style of flagging 'this' as const by putting 'const' at the end of the function signature rather than the beginning.

At the beginning will still work for function types. The at the end option is there for those who are building complex type declarations.

honestly i would prefer just one single way of writing things. allowing multiple ways makes it less simple to understand external code for beginners. this is one thing i like about java that there hardly exist multiple ways to formulate the same piece of code.
Nov 26 2007
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-26 18:20:18 -0500, Extrawurst <spam extrawurst.org> said:

 I didn't spend too much looking through the changes, but it looked like 
 D is going to be switching to the C++ style of flagging 'this' as const 
 by putting 'const' at the end of the function signature rather than the 
 beginning.

At the beginning will still work for function types. The at the end option is there for those who are building complex type declarations.

honestly i would prefer just one single way of writing things. allowing multiple ways makes it less simple to understand external code for beginners. this is one thing i like about java that there hardly exist multiple ways to formulate the same piece of code.

I second that: the language should help make code consistent. I don't really care which syntax is kept in the language, but in my opinion Walter should decide on one, not two. Allowing both seems like a compromise to make C++ switchers happy by permitting const at the end of a member function (as in C++), but I think it just makes the differences about const even more subtle and thus harder to learn to that group. Or is there another rationale I'm missing for having the two syntaxes? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 27 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Michel Fortin wrote:
 On 2007-11-26 18:20:18 -0500, Extrawurst <spam extrawurst.org> said:
 
 I didn't spend too much looking through the changes, but it looked 
 like D is going to be switching to the C++ style of flagging 'this' 
 as const by putting 'const' at the end of the function signature 
 rather than the beginning.

At the beginning will still work for function types. The at the end option is there for those who are building complex type declarations.

honestly i would prefer just one single way of writing things. allowing multiple ways makes it less simple to understand external code for beginners. this is one thing i like about java that there hardly exist multiple ways to formulate the same piece of code.

I second that: the language should help make code consistent. I don't really care which syntax is kept in the language, but in my opinion Walter should decide on one, not two. Allowing both seems like a compromise to make C++ switchers happy by permitting const at the end of a member function (as in C++), but I think it just makes the differences about const even more subtle and thus harder to learn to that group. Or is there another rationale I'm missing for having the two syntaxes?

I agree. I don't get why we need two ways to express it. What's the benefit of writing const out in front? --bb
Nov 27 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 I agree.  I don't get why we need two ways to express it.  What's the 
 benefit of writing const out in front?

1. It's semantically consistent. 2. It enables you to 'group' const member functions: const { int foo(); int bar(); } this cleans up things nicely when you have a number of member functions. Contrast this with how the same code looks in C++.
Nov 27 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Chris Miller wrote:
 When grouping with : or { } it applies to the actual named declarations 
 it encloses; but if const is applied before the return type, precedence 
 (the juxtaposition) dictates association with the return type.
 
 This is what I would assume.

Think about const as applying to the item being declared. Here, foo is being declared, so const applies to the foo. Not its return type. It would also completely break the consistency of storage classes if: const: const { } const meant different things.
Nov 27 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Chris Miller wrote:
 When grouping with : or { } it applies to the actual named 
 declarations it encloses; but if const is applied before the return 
 type, precedence (the juxtaposition) dictates association with the 
 return type.

 This is what I would assume.

Think about const as applying to the item being declared. Here, foo is being declared, so const applies to the foo. Not its return type. It would also completely break the consistency of storage classes if: const: const { } const meant different things.

But const on a function isn't really a storage class. It's a type specifier on an implicit parameter. Although being able to wrap a bunch of functions in const{ } may be neat, there's no particular reason to expect that you should be able to do that based on what it actually is. Personally I'm not too wild about big storage class blocks in a class. I think it quickly makes the code hard to read. Consider: public: const: private: // am I still const here? how do I turn off const now? I just don't see the ability to say const{ ... } as a big win at all. The real annoyance with C++ const is the redundant methods in both const and non-const flavors. That's the thing to target for elimination. Here's another thought about syntax for specifying const this: in methods, make it optional to put a 'this' as a first argument. So int foo(int x) {...} becomes just a shorthand for the explicit version: int foo(this, int x) {...} Then it's obvoious that you'd write the const version as int foo(const this, int x) {...} It's sort of a halfway between explicit 'this' like in Python, and fully implicit 'this' like in C++. Doesn't do anything for the const{ ... } syntax, but I don't really see that syntax as a big win, anyway. It's really never bothered me in C++. Usually in fact I think const and non-const functions end up being more or less interleaved if you write a bunch of typical getThis(); setThis(); getThat(); setThat(); methods. Having a run of functions that are all const isn't all that common. Also const{...} does nothing to eliminate repeated consts used on other parameters besides 'this'. Consider your typical big-value-type struct where you have bunches of functions taking const references like opAdd(ref const typeof(this) other_one) {...} you still have to type all those 'const's. --bb
Nov 27 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Chris Miller wrote:
 When grouping with : or { } it applies to the actual named
 declarations it encloses; but if const is applied before the return
 type, precedence (the juxtaposition) dictates association with the
 return type.

 This is what I would assume.

Think about const as applying to the item being declared. Here, foo is being declared, so const applies to the foo. Not its return type. It would also completely break the consistency of storage classes if: const: const { } const meant different things.

But const on a function isn't really a storage class. It's a type specifier on an implicit parameter.

Only because you say it is. Pretend we don't have the current C-style function declaration syntax for a minute. The example would become: const int function() foo = () { ... }; const int function() bar = () { ... }; So one would naturally expect this to work: const { int function() foo = () { ... }; int function() bar = () { ... }; } const really *is* a storage class, even for functions. Really, I could say that const is delicious when barbecued if I decided to redefine it to mean "chicken;" you can say anything you want when you change the question. ;)
 Although being able to wrap a bunch
 of functions in const{ } may be neat, there's no particular reason to
 expect that you should be able to do that based on what it actually is.

Well, if you extend that logic, there's no reason to expect this to work: writefln("Hi!"); After all, we don't really have letters in a computer! All we have are numbers. And even those are really just fixed-length sequences of logic values. So clearly, we shouldn't have int, we should have logic[32]. Except we don't have arrays, either, which makes this somewhat inconvient... You've got to draw the abstraction line *somewhere.* You're correct in saying that it's not obvious that you should be able to group by const when you think of a member function in terms of its implementation. But if you think of functions as values just like ints or floats (or indeed as some special kind of function that magically knows what "this" means,) then it makes perfect sense.
 Personally I'm not too wild about big storage class blocks in a class. I
 think it quickly makes the code hard to read.  Consider:
 
    public:
 
    const:
 
    private:
 
    // am I still const here? how do I turn off const now?

That's a separate issue, and doesn't really factor into this discussion. Again, this is a personal style issue. I love this because it lets me just go "and the rest is private." All of my classes are strictly divided into the public interface, followed by "private:" and the private interface. I could tag every single private thing as being "private," but then it all turns into spaghetti, and I lose the ability to see clear delineations in the source. Six of one, half-dozen of the other. :)
 I just don't see the ability to say const{ ... } as a big win at all.
 The real annoyance with C++ const is the redundant methods in both const
 and non-const flavors.  That's the thing to target for elimination.

I really have to agree with you here. The presence of the 'return' storage class is interesting... perhaps something similar could be done to factor out the common parts of const/non-const members?
 Here's another thought about syntax for specifying const this:
 in methods, make it optional to put a 'this' as a first argument.  So
 
     int foo(int x) {...}
 
 becomes just a shorthand for the explicit version:
 
     int foo(this, int x) {...}
 
 Then it's obvoious that you'd write the const version as
 
     int foo(const this, int x) {...}
 
 It's sort of a halfway between explicit 'this' like in Python, and fully
 implicit 'this' like in C++.

Personally, I'm not too hot on this one. It looks too much like type extensions. I mean, from the "one way to do things" POV, if you can do that, why do we even have methods in class bodies at all? You yourself said earlier: "I don't get why we need two ways to express it." So by your own reasoning, we should abandon the existing member function syntax entirely, since it would become redundant. I know a lot of people who hold very... emphatic... opinions on Python's use of 'self'. Heck, AFAIK, Guido's removing it from Python 3000. Again, this is also inconsistent with everything else you can apply const to. I don't get why we need different ways of talking about functions and everything else.
 Doesn't do anything for the const{ ... } syntax, but I don't really see
 that syntax as a big win, anyway.  It's really never bothered me in C++.
  Usually in fact I think const and non-const functions end up being more
 or less interleaved if you write a bunch of typical  getThis();
 setThis(); getThat(); setThat(); methods.  Having a run of functions
 that are all const isn't all that common.

Can't really comment since I'm waiting for the D const stuff to come out so I can play with it. I can see it being useful for anything that has clearly delineated const and non-const interfaces. Again, I think this is an issue of how you personally like to organise things. As a side note, one of the truly terrible things about code is that we have to choose exactly one way of structuring things. We can either interleave definitions, or do them in blocks. Deeply nest modules, or shallowly nest them. Pity we can't just click a header and re-sort our code. :)
 Also const{...} does nothing to eliminate repeated consts used on other
 parameters besides 'this'.  Consider your typical big-value-type struct
 where you have bunches of functions taking const references like
     opAdd(ref const typeof(this) other_one) {...}
 you still have to type all those 'const's.

True, but then this is the only storage class I can think of that *can* apply to function arguments at all. Not surprising that no simple, general way of solving this has presented itself, considering the problem didn't exist until now.
 --bb

-- Daniel
Nov 27 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Daniel Keep wrote:
 
 Bill Baxter wrote:
 Walter Bright wrote:
 Chris Miller wrote:
 When grouping with : or { } it applies to the actual named
 declarations it encloses; but if const is applied before the return
 type, precedence (the juxtaposition) dictates association with the
 return type.

 This is what I would assume.

being declared, so const applies to the foo. Not its return type. It would also completely break the consistency of storage classes if: const: const { } const meant different things.

specifier on an implicit parameter.

Only because you say it is. Pretend we don't have the current C-style function declaration syntax for a minute. The example would become: const int function() foo = () { ... }; const int function() bar = () { ... }; So one would naturally expect this to work: const { int function() foo = () { ... }; int function() bar = () { ... }; } const really *is* a storage class, even for functions.

Maybe I'm missing something here, but what exactly are you thinking you're making const there? My understanding is that const applied to a method of a class means 'this' is const. Const applied to a bare function or a static method has no useful interpretation AFAIK. If I'm wrong please correct me. I haven't been using D2.0 so I don't know the nuances of how const works there.
 Really, I could say that const is delicious when barbecued if I decided
 to redefine it to mean "chicken;" you can say anything you want when you
 change the question. ;)

I wasn't intending to change the question. Your response sounds to me like you're the one changing the question ... to something about tasty chicken. So please explain again.
 Although being able to wrap a bunch
 of functions in const{ } may be neat, there's no particular reason to
 expect that you should be able to do that based on what it actually is.

Well, if you extend that logic, there's no reason to expect this to work: writefln("Hi!");

My point was that there is no precedent in the language for something like const applying blockwise. It's a really different kind of beast from private/public/protected or from version(){}. That's not to say we can't define it to behave the same way just that there's no entity in the language that's exactly like it that *compels* us to say const{...} must be allowed in the name of consistency. If you want to say the usage looks kinda like "private" so lets let "const" syntax work similarly, then fine, it's a valid argument. But it's also a valid argument to say that const is different enough that it shouldn't work the same way as private/public. In fact there is no 'unconst' so as far as I know you won't be able to do 'const:' like you can do 'private:'. Unless Walter declares some syntax for it like '!const'.
 After all, we don't really have letters in a computer!  All we have are
 numbers.  And even those are really just fixed-length sequences of logic
 values.  So clearly, we shouldn't have int, we should have logic[32].
 Except we don't have arrays, either, which makes this somewhat inconvient...
 
 You've got to draw the abstraction line *somewhere.*  You're correct in
 saying that it's not obvious that you should be able to group by const
 when you think of a member function in terms of its implementation.  But
 if you think of functions as values just like ints or floats (or indeed
 as some special kind of function that magically knows what "this"
 means,) then it makes perfect sense.

How does it make sense to think of a function as being const without considering the actual thing you're promising won't change? A mere function has no 'state' of its own so there's nothing to make constant. Its the data referred to by the function that the function is promising not to change.
 ...


 Also const{...} does nothing to eliminate repeated consts used on other
 parameters besides 'this'.  Consider your typical big-value-type struct
 where you have bunches of functions taking const references like
     opAdd(ref const typeof(this) other_one) {...}
 you still have to type all those 'const's.

True, but then this is the only storage class I can think of that *can* apply to function arguments at all.

Ok there's part of my confusion right there -- I was under the impression that const on arguments was classified as a type constructor, not a storage class. But anyway the fact that it is unique in that respect just goes back to underscore my point that nothing compels us to go out of our way to treat it like every other storage class. Maybe even calling it a storage class is misleading to begin with. Maybe it is sufficiently different that it deserves a different moniker. --bb
Nov 27 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Bill Baxter wrote:
 Daniel Keep wrote:
 Bill Baxter wrote:
 ...

Only because you say it is. Pretend we don't have the current C-style function declaration syntax for a minute. The example would become: const int function() foo = () { ... }; const int function() bar = () { ... }; So one would naturally expect this to work: const { int function() foo = () { ... }; int function() bar = () { ... }; } const really *is* a storage class, even for functions.

Maybe I'm missing something here, but what exactly are you thinking you're making const there? My understanding is that const applied to a method of a class means 'this' is const. Const applied to a bare function or a static method has no useful interpretation AFAIK. If I'm wrong please correct me. I haven't been using D2.0 so I don't know the nuances of how const works there.

No, you're not missing anything. Personally, I blame brain-slippage. You're correct; the above only vaguely makes sense, and certainly doesn't mean what I implied it means.
 Really, I could say that const is delicious when barbecued if I decided
 to redefine it to mean "chicken;" you can say anything you want when you
 change the question. ;)

I wasn't intending to change the question. Your response sounds to me like you're the one changing the question ... to something about tasty chicken. So please explain again.

Again, I apologise for the snafu on my part. Honestly, I can't imagine where I got that from. Except for the chicken; I happen to rather like chicken, and it was the tastiest metaphor I could come up with.
 Although being able to wrap a bunch
 of functions in const{ } may be neat, there's no particular reason to
 expect that you should be able to do that based on what it actually is.

Well, if you extend that logic, there's no reason to expect this to work: writefln("Hi!");

My point was that there is no precedent in the language for something like const applying blockwise. It's a really different kind of beast from private/public/protected or from version(){}. That's not to say we can't define it to behave the same way just that there's no entity in the language that's exactly like it that *compels* us to say const{...} must be allowed in the name of consistency. If you want to say the usage looks kinda like "private" so lets let "const" syntax work similarly, then fine, it's a valid argument. But it's also a valid argument to say that const is different enough that it shouldn't work the same way as private/public. In fact there is no 'unconst' so as far as I know you won't be able to do 'const:' like you can do 'private:'. Unless Walter declares some syntax for it like '!const'.

Well, what about static? You can apply that to blocks, AFAIK. Same thing with extern(X) decorators. As for the uncost, I was under the impression that 'thing:' blocks did not stack. That is, private: static: int foo; // foo is public and static I would expect the same thing to apply in this case; it would be like static in that I can't think of any inverse for it.
 After all, we don't really have letters in a computer!  All we have are
 numbers.  And even those are really just fixed-length sequences of logic
 values.  So clearly, we shouldn't have int, we should have logic[32].
 Except we don't have arrays, either, which makes this somewhat
 inconvient...

 You've got to draw the abstraction line *somewhere.*  You're correct in
 saying that it's not obvious that you should be able to group by const
 when you think of a member function in terms of its implementation.  But
 if you think of functions as values just like ints or floats (or indeed
 as some special kind of function that magically knows what "this"
 means,) then it makes perfect sense.

How does it make sense to think of a function as being const without considering the actual thing you're promising won't change? A mere function has no 'state' of its own so there's nothing to make constant. Its the data referred to by the function that the function is promising not to change.

I think this is where I got my thoughts mixed up. Taking a member function, the only piece of state this has that you could conceivably 'const'ify is the 'this' pointer. Although, I admit this is a rather tenuous link.
 ...
 Also const{...} does nothing to eliminate repeated consts used on other
 parameters besides 'this'.  Consider your typical big-value-type struct
 where you have bunches of functions taking const references like
     opAdd(ref const typeof(this) other_one) {...}
 you still have to type all those 'const's.

True, but then this is the only storage class I can think of that *can* apply to function arguments at all.

Ok there's part of my confusion right there -- I was under the impression that const on arguments was classified as a type constructor, not a storage class. But anyway the fact that it is unique in that respect just goes back to underscore my point that nothing compels us to go out of our way to treat it like every other storage class. Maybe even calling it a storage class is misleading to begin with. Maybe it is sufficiently different that it deserves a different moniker. --bb

The way I've understood it is that const is both a type constructor *and* a storage class. When invoked with parenthesis, it's a type constructor. When invoked without, it's a storage class that happens to apply the type constructor to whatever the type of the declaration is. In any case, I retract my argument above since it's clear not even I am sure what I'm saying. :P I still *feel* that putting the 'const' out the front is OK, although I now cannot give you a rational argument for it. -- Daniel "this is why I don't post on the NG much..."
Nov 27 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Daniel Keep wrote:
 The way I've understood it is that const is both a type constructor
 *and* a storage class.

That's right. It's a floor wax *and* a dessert topping!
 When invoked with parenthesis, it's a type
 constructor.  When invoked without, it's a storage class that happens to
 apply the type constructor to whatever the type of the declaration is.

That's right, too.
Nov 27 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 If we ditch
 const-as-an-attribute altogether, it would require only minor changes
 to get code compiling again: e.g.
 
     const int[] table = [ 1, 2, 3, 4 ];
 
 becomes
 
     const(int[]) table = [ 1, 2, 3, 4 ];

I think it would be pretty hard to give up: const x = 3; Also, C++ has const as both a storage class and a type constructor, and yes, they have subtly different meanings. This doesn't seem to cause any major problems.
Nov 28 2007
next sibling parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright wrote:
 Janice Caron wrote:
 If we ditch
 const-as-an-attribute altogether, it would require only minor changes
 to get code compiling again: e.g.

     const int[] table = [ 1, 2, 3, 4 ];

 becomes

     const(int[]) table = [ 1, 2, 3, 4 ];

I think it would be pretty hard to give up: const x = 3; Also, C++ has const as both a storage class and a type constructor, and yes, they have subtly different meanings. This doesn't seem to cause any major problems.

Shouldn't const as a storage class mean really constant, so that typeof(&x) above would be invariant(int)* instead of const(int)*? And in that case, wouldn't it be more logical if the keword "const", as a type modifier meant constant (what invariant is today), and another keyword was used for the weaker access protection... Say "readonly". *ducks* ;) -- Oskar
Nov 28 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Janice Caron wrote:
 On Nov 28, 2007 10:10 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 I think it would be pretty hard to give up:

         const x = 3;

 Also, C++ has const as both a storage class and a type constructor, and
 yes, they have subtly different meanings. This doesn't seem to cause any
 major problems.

Actually, I may have been misunderstood, so I want to be clear. There is a difference between an ATTRIBUTE and a STORAGE CLASS. C++ has storage classes, but D does not. D has attributes, but C++. They are not the same thing. The terms are not interchangeable. For example, in C++, "typedef" is (syntactically) a storage class, but it's certainly not an attribute in D. If you want to make the statement const x = 3; legal in D

Um, "const x = 3" *is* legal in D. Has been for about a year now. Sean
Nov 28 2007
parent reply Sean Kelly <sean f4.ca> writes:
Janice Caron wrote:
 On 11/28/07, Sean Kelly <sean f4.ca> wrote:
 Um, "const x = 3" *is* legal in D.  Has been for about a year now.

 Sean

No need for the "Um". Of course I know that, and certainly I should have said, "if you want to /keep/ [it] legal", rather than "if you want to /make/ [it] legal". My apologies for the typographical faux pas, however it is entirely unimportant in the context of this discussion.

I disagree. But back to the main point. You claimed that D does not have storage classes. Could you explain this? Sean
Nov 28 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 11/28/07, Sean Kelly <sean f4.ca> wrote:
 You claimed that D does not
 have storage classes.  Could you explain this?

Certainly. In the grammar of C and C++, a rule is defined called "storage-class-specifier". The "storage-class-specifier" grammar rule forms part of the rule defining the syntax of a declaration. There is no corresponding grammar element in D. The storage class specifiers in C and C++ are auto, extern, mutable, register, static and typedef. See the grammar at http://www.kuzbass.ru:8086/docs/isocpp/gram.html The closest grammar element D has is "attribute". This is defined on the web page. http://www.digitalmars.com/d/attribute.html. D attributes are deprecated, static, final, override, abstract, const, auto, scope, and various other constructs as defined on that page. Though there is overlap between C's storage classes and D's attributes, they are not completely identical, and in consequence, some analogies don't work.

I think this is a distinction without a difference. D also has storage classes static, extern, etc., which work just like storage classes in C++. Syntactically (and the grammar rules for), attributes and storage classes are exactly the same.
Nov 28 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On Nov 28, 2007 10:10 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 I think it would be pretty hard to give up:

       const x = 3;

Why? It would just turn into something else, like const(int) x = 3; static x = 3; macro x = 3;

Because those alternatives all look terrible. And frankly, how could anyone be confused about what: const x = 3; means? I can't understand throwing that out to improve clarity?
 Also, C++ has const as both a storage class and a type constructor, and
 yes, they have subtly different meanings. This doesn't seem to cause any
 major problems.

That's because C++ behaves as expected. If I declare const int * f(); then I get a function which returns a pointer to const int, /not/ a const member function which returns a pointer to mutable int.

What is expected, or what you are *used* to? Let me put it another way, can you explain the difference between C++ const storage class and C++ const type constructors? There's a big semantic difference.
 I other
 words, in C++, the const adheres to the return type, not the
 function's "this" pointer. The D behaviour is drastically different
 and vastly unintuitive.

D's const storage class applies to the declaration. That makes intuitive sense, although it is different from C++.
 I don't know whether this is syntactically considered to be a storage
 class or a type constructor in C++, but really, it's irrelevant to me.
 What matters is the principle of least surprise. It should do what we
 expect it to do, and not something else.

Every other storage class applies to the declaration, why should const be completely different?
Nov 28 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Janice Caron wrote:
 On Nov 28, 2007 10:10 AM, Walter Bright <newshound1 digitalmars.com> 
 wrote:


 I other
 words, in C++, the const adheres to the return type, not the
 function's "this" pointer. The D behaviour is drastically different
 and vastly unintuitive.

D's const storage class applies to the declaration. That makes intuitive sense, although it is different from C++.
 I don't know whether this is syntactically considered to be a storage
 class or a type constructor in C++, but really, it's irrelevant to me.
 What matters is the principle of least surprise. It should do what we
 expect it to do, and not something else.

Every other storage class applies to the declaration, why should const be completely different?

We're talking here about methods, right? And you're saying if we can do this: static int[] func() { } then why shouldn't we be able to do this: const int[] func() { } ?? (Just trying to make sure I understand your point.) Acutally, when comparing side by side with "static", that starts to sound reasonable. Both are mucking with func's implicit 'this' paramter (static eliminates it, const makes it const). The big difference is just that 'static int[]' as a return value makes no sense, so there's only one way to read it, but "returns a const int[]" is a logical way to mis-read the const version, especially for C++ converts. One workaround for people who find that confusing would be to reiterate the protection: const public int[] func() {} Which is not much more verbose than const(this) int[] func() { } So I guess I can see people getting used to this. So the question again becomes why allow the at-the-end syntax too? Isn't one syntax enough? Is it just because you want to avoid const const int[] func() {} ? --bb
Nov 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Every other storage class applies to the declaration, why should const 
 be completely different?

We're talking here about methods, right? And you're saying if we can do this: static int[] func() { } then why shouldn't we be able to do this: const int[] func() { } ??

And you can do that.
 The big difference is just that 'static int[]' as a return value makes 
 no sense, so there's only one way to read it, but "returns a const 
 int[]" is a logical way to mis-read the const version, especially for 
 C++ converts.

Ok, let's assume C++ is intuitive, consistent, and logical. Given: const int *p; what makes more logical, intuitive sense? 1) p is a non-constant pointer to const int? 2) p is a const pointer to a non-const int? 3) p is a const pointer to a const int? I doubt you'll get a consistent answer from programmers who aren't already very used to the wacky C++ semantics.
 One workaround for people who find that confusing would be to reiterate 
 the protection:
 
     const public int[] func() {}
 
 Which is not much more verbose than
 
     const(this) int[] func() { }

I doubt people will be confused by it for more than 5 minutes, and then it will make sense. I find the D version of const much more intuitive, from a logical, consistency point of view, than the C++ one.
 So I guess I can see people getting used to this.  So the question again 
 becomes why allow the at-the-end syntax too?  Isn't one syntax enough? 

It's a good question. Note that D allows both C-style and D-style array declarations. It makes it easier to convert C code over to D code.
Nov 28 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 Walter Bright wrote:
 Every other storage class applies to the declaration, why should 
 const be completely different?

We're talking here about methods, right? And you're saying if we can do this: static int[] func() { } then why shouldn't we be able to do this: const int[] func() { } ??

And you can do that.
 The big difference is just that 'static int[]' as a return value makes 
 no sense, so there's only one way to read it, but "returns a const 
 int[]" is a logical way to mis-read the const version, especially for 
 C++ converts.

Ok, let's assume C++ is intuitive, consistent, and logical. Given: const int *p; what makes more logical, intuitive sense? 1) p is a non-constant pointer to const int? 2) p is a const pointer to a non-const int? 3) p is a const pointer to a const int? I doubt you'll get a consistent answer from programmers who aren't already very used to the wacky C++ semantics.
 One workaround for people who find that confusing would be to 
 reiterate the protection:

     const public int[] func() {}

 Which is not much more verbose than

     const(this) int[] func() { }

I doubt people will be confused by it for more than 5 minutes, and then it will make sense. I find the D version of const much more intuitive, from a logical, consistency point of view, than the C++ one.

Ok, but that seems to differ from your default position on other topics. Usually I see you saying we should do things to not confuse C++ coders. "If it looks like C++ it should behave like C++" is the impression I've gotten in the past. But maybe you're gaining enough confidence in D these days that you're not as concerned about that any more. :-) ... So can we talk about the fall-through default in switch statements again? :-)
 So I guess I can see people getting used to this.  So the question 
 again becomes why allow the at-the-end syntax too?  Isn't one syntax 
 enough? 

It's a good question. Note that D allows both C-style and D-style array declarations.

And the only commentary I've heard on that feature from D users has been negative. There could be a "silent majority" in favor of it, but I doubt it.
 It makes it easier to convert C code over to D code.

So you're making this at-the-end-const special case for no other reason than to make it easier to convert C++ code? As someone who converted a 15K-line C++ library, I don't think that this one thing will help much. At least not for someone translating manually. Perhaps it would help an automatic translator. But I doubt it. "Move const-after-parameters to const-before-declaration" would be a relatively straightforward conversion rule to implement. Much easier than the "convert-struct-with-inheritance-to-something-else" rule. :-) --bb
Nov 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Ok, but that seems to differ from your default position on other topics. 
  Usually I see you saying we should do things to not confuse C++ 
 coders.  "If it looks like C++ it should behave like C++" is the 
 impression I've gotten in the past.   But maybe you're gaining enough 
 confidence in D these days that you're not as concerned about that any 
 more. :-)
 
 ... So can we talk about the fall-through default in switch statements 
 again?  :-)

No, because to break things in a way that results in an error message is ok, but breaking them so things silently behave differently is not ok.
 And the only commentary I've heard on that feature from D users has been 
 negative.  There could be a "silent majority" in favor of it, but I 
 doubt it.

But I've seen converted code that used it.
Nov 28 2007
next sibling parent 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Walter Bright wrote:
 But I've seen converted code that used it.

Having second thoughts, I suppose that some people might be really thankful for this feature. Me being not one of them is probably not much of an argument, I agree! :) regards, frank
Nov 28 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 Ok, but that seems to differ from your default position on other 
 topics.  Usually I see you saying we should do things to not confuse 
 C++ coders.  "If it looks like C++ it should behave like C++" is the 
 impression I've gotten in the past.   But maybe you're gaining enough 
 confidence in D these days that you're not as concerned about that any 
 more. :-)

 ... So can we talk about the fall-through default in switch statements 
 again?  :-)

No, because to break things in a way that results in an error message is ok, but breaking them so things silently behave differently is not ok.

Doesn't const int func(); break things in a way that might not caught by a compiler?
 And the only commentary I've heard on that feature from D users has 
 been negative.  There could be a "silent majority" in favor of it, but 
 I doubt it.

But I've seen converted code that used it.

That doesn't mean they consciously used it, or that they would mind changing it if it were pointed out to them. Heck I might even be using it in OpenMesh/D. But that's just laziness. If someone points out to me where I am using it, I will change it. If the compiler pointed out to me that I was using it, I would change it. Don't know if I'm like everyone there. But there is also the issue with C style function pointer declarations. Wasn't that allowed for a while then eventually gotten rid of in favor of "just one way to do it"? --bb
Nov 28 2007
next sibling parent Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 
 But there is also the issue with C style function pointer declarations. 
  Wasn't that allowed for a while then eventually gotten rid of in favor 
 of "just one way to do it"?

It's still there. Check phobos/internal/object.d :-) Sean
Nov 28 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Doesn't
    const int func();
 
 break things in a way that might not caught by a compiler?

I can't think of a way.
 And the only commentary I've heard on that feature from D users has 
 been negative.  There could be a "silent majority" in favor of it, 
 but I doubt it.

But I've seen converted code that used it.

That doesn't mean they consciously used it, or that they would mind changing it if it were pointed out to them. Heck I might even be using it in OpenMesh/D. But that's just laziness. If someone points out to me where I am using it, I will change it. If the compiler pointed out to me that I was using it, I would change it. Don't know if I'm like everyone there. But there is also the issue with C style function pointer declarations. Wasn't that allowed for a while then eventually gotten rid of in favor of "just one way to do it"?

The C way is still supported.
Nov 28 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:filh8n$5ce$1 digitalmars.com...

 The C way is still supported.

As is uglily demonstrated by DDoc and all compiler error messages involving function pointers.
Nov 29 2007
prev sibling next sibling parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Walter Bright wrote:
 Ok, let's assume C++ is intuitive, consistent, and logical. Given:
 
 const int *p;
 
 what makes more logical, intuitive sense?
 
 1) p is a non-constant pointer to const int?
 2) p is a const pointer to a non-const int?
 3) p is a const pointer to a const int?
 
 I doubt you'll get a consistent answer from programmers who aren't 
 already very used to the wacky C++ semantics.

Okay, you can take me as one sample, I suck at C++! Hah! I haven't even followed the thread very closely... =) I'd expect... hmmm... option 1: I'd think *p is an int (p being the pointer), it's just not an int but a const int. But I really couldn't be sure, and would probably look it up... Btw. wouldn't it be "const int* p" in D? Just changing the spacing makes me re-think my answer! Argh!
 I doubt people will be confused by it for more than 5 minutes, and then 
 it will make sense. I find the D version of const much more intuitive, 
 from a logical, consistency point of view, than the C++ one.

I guess now I'll have to go and look up both!
 So I guess I can see people getting used to this.  So the question 
 again becomes why allow the at-the-end syntax too?  Isn't one syntax 
 enough? 

It's a good question. Note that D allows both C-style and D-style array declarations. It makes it easier to convert C code over to D code.

Actually, when it comes to array declaration style, I wouldn't care if the old style was dropped in D. The new one works fine (for me), and there are some good arguments for having just OWTDI. regards, frank
Nov 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
0ffh wrote:
 Actually, when it comes to array declaration style, I wouldn't care if the
 old style was dropped in D. The new one works fine (for me), and there are
 some good arguments for having just OWTDI.

For new code, there isn't much point to using the C style array declarations. But when you're translating a big chunk of C code, it's a time and bug saver. Converting the array declarations from C style to D style is surprisingly error prone and rather tedious. It cannot be automated with simple string editors.
Nov 28 2007
next sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
Walter Bright escribió:
 0ffh wrote:
 Actually, when it comes to array declaration style, I wouldn't care if 
 the
 old style was dropped in D. The new one works fine (for me), and there 
 are
 some good arguments for having just OWTDI.

For new code, there isn't much point to using the C style array declarations. But when you're translating a big chunk of C code, it's a time and bug saver. Converting the array declarations from C style to D style is surprisingly error prone and rather tedious. It cannot be automated with simple string editors.

But it could be automated with a smarter "editor". In fact, I'm willing to add an option in Descent to do it if there is interest. The outline view already show declarations that way, so it's just a matter of "writing down" the code with a simple visitor. Of course, it still can't be done with simple string editors, so if you don't have an IDE keeping the C syntax is still time saver.
Nov 28 2007
prev sibling parent Clay Smith <clayasaurus gmail.com> writes:
Walter Bright wrote:
 0ffh wrote:
 Actually, when it comes to array declaration style, I wouldn't care if 
 the
 old style was dropped in D. The new one works fine (for me), and there 
 are
 some good arguments for having just OWTDI.

For new code, there isn't much point to using the C style array declarations. But when you're translating a big chunk of C code, it's a time and bug saver. Converting the array declarations from C style to D style is surprisingly error prone and rather tedious. It cannot be automated with simple string editors.

I agree, it is useful to help port C/C++ code to D.
Nov 28 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 You are of course completely correct about that, Walter. However,
 nobody here is claiming that C++ got it right. Rather, we're saying
 that D /should/ get it right.
 
 Just because C++ is confusing, doesn't mean D has to be confusing too.

But much of the justifications used here are that C++ is intuitive. It isn't, it's just what you're used to. Also, there are many uses to which const is put. There's no way any useful definition of it will be 100% pure and consistent. For example, const x = 3; is inconsistent. But it is far too darned useful to sacrifice for some notion of perfection.
Nov 28 2007
parent James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Janice Caron wrote:
 You are of course completely correct about that, Walter. However,
 nobody here is claiming that C++ got it right. Rather, we're saying
 that D /should/ get it right.

 Just because C++ is confusing, doesn't mean D has to be confusing too.

But much of the justifications used here are that C++ is intuitive. It isn't, it's just what you're used to.

I don't think it's that C++ is intuitive: it's that it's not as hard for people to grok, in this area, as D. (And let's admit that C++'s declaration syntax is one of worst things about C++, so D should be able to avoid that criticism.) -- James
Dec 17 2007
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 11/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Because those alternatives all look terrible. And frankly, how could
 anyone be confused about what:
         const x = 3;
 means? I can't understand throwing that out to improve clarity?

You have misunderstood me. The suggestion is that "const" shall not be an attribute. This does not rule out allowing "const x = 3;" being a legal statement, it only means you have to change the grammar of a declaration in order to allow it.
 Let me put it another way,
 can you explain the difference between C++ const storage class and C++
 const type constructors? There's a big semantic difference.

I just checked the grammar of C++, and C++'s list of storage-class-specifiers doesn't actually include "const", so I think neither of us is using the term correctly. However, in C++, if a statement such as "const int x = 3;" occurs at global scope, then x is invariant. We can call that a storage class if you want. The similar looking, but drastically different statement "const int * p = whatever;" uses const as a type constructor, equivalent to D's const(int). In C++, this can also be written as "int const * p" (a style I prefer as it now reads from right to left as "p is a pointer to const int"). As far as member functions go, the C++ const storage class cannot be applied to them because it makes no sense (because a member function cannot be declared at global scope. By definition, it is always in the scope of the enclosing class). Thus, when I write (in C++) class A { int f() const; } The word "const" is /not/ acting as a "storage class". Instead, it tells us that "this" is const within f.
 D's const storage class applies to the declaration. That makes intuitive
 sense, although it is different from C++.

But what makes no sense is the very notion of a function (as opposed to its "this" pointer) being const. All functions are, by definition, invariant. That is, the bytes of machine code which are executed, may not be changed by any part of the program. If you take the address of those bytes of machine code, that's a pointer to invariant. The RAM utilitised during that function's execution (the stack, or the member variables, or both) are part of that function's context, and we care whether or not the function is allowed to modify those variables in the course of its execution. Thus, the kind of constness we are talking about is the constness of the function's context, not of the function itself. You can call that a "storage class" if you want, but it means something very different from a variable declared at global scope.

Janice, what are your thoughts about how the 'static' attribute fits into that. I was thinking largely the way you were, but 'static' seems to be another example of a "storage class" that doesn't change anything about the function per-se, but rather it's parameters. Just like the const "storage class", it mucks with the hidden "this" parameter. If you're going to allow "static" to modify the 'this' parameter by sticking it out in front of a function, then it seems you should allow const the same courtesy. --bb
Nov 28 2007
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 28 Nov 2007 20:33:20 +0000, Janice Caron wrote:

 My objection to the existing syntax is more or less solely that
     const int f();
 
 does not declare that f returns a const int, and I think that all
 newcomers to D will find that /hugely/ confusing.

The simple fact is that const type funcname(); looks ambiguous. It LOOKS like it could mean either that funcname() returns a const type OR that funcname() is const but returns a non-const type. Once you know the rules you can workout what is intended, but it is not as intuitive as you might like it to be. Will it really be so hard to provide syntax that is not so ambiguous? Also, what is the syntax for a const function that returns a const type? const (const type) funcname(); ???? const const type funcname(); ??? const (const type funcname)(); ??? const (const type funcname()); ??? Walter, your efforts to achieve simplicity are being thwarted by the results being simplistic. The D programming language has way too much keyword overloading to make it an easy language to learn and remember. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Nov 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 The simple fact is that 
 
    const type funcname();
 
 looks ambiguous. It LOOKS like it could mean either that funcname() returns
 a const type OR that funcname() is const but returns a non-const type.
 
 Once you know the rules you can workout what is intended, but it is not as
 intuitive as you might like it to be.
 
 Will it really be so hard to provide syntax that is not so ambiguous?
 
 Also, what is the syntax for a const function that returns a const type?

The rule for constructing a const type is to enclose that type in parentheses, so: const const(type) funcname(); or: const(type) funcname()const; or: const { const(type) funcname(); }
 Walter, your efforts to achieve simplicity are being thwarted by the
 results being simplistic.

I think it is thwarted by people believing it is far more complicated than it actually is. For example, in the above case, you have to use const twice, because there are two different things in the same declaration you want to be const. Forget about const as a storage class for the moment, and just think of const as a type constructor. To make a type const, const(T) and it will all follow from there.
Nov 28 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 The simple fact is that
    const type funcname();

 looks ambiguous. It LOOKS like it could mean either that funcname() 
 returns
 a const type OR that funcname() is const but returns a non-const type.

 Once you know the rules you can workout what is intended, but it is 
 not as
 intuitive as you might like it to be.

 Will it really be so hard to provide syntax that is not so ambiguous?

 Also, what is the syntax for a const function that returns a const type?

The rule for constructing a const type is to enclose that type in parentheses, so: const const(type) funcname(); or: const(type) funcname()const; or: const { const(type) funcname(); }
 Walter, your efforts to achieve simplicity are being thwarted by the
 results being simplistic.

I think it is thwarted by people believing it is far more complicated than it actually is. For example, in the above case, you have to use const twice, because there are two different things in the same declaration you want to be const. Forget about const as a storage class for the moment, and just think of const as a type constructor. To make a type const, const(T) and it will all follow from there.

That's a good way to remember it. If we get used to seeing and writing cosnt(T) everywhere C++ would have said "const T", then it should become natural to see const int foo() as a "const(this)" rather than returing const int. --bb
Nov 28 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 I just checked the grammar of C++, and C++'s list of
 storage-class-specifiers doesn't actually include "const", so I think
 neither of us is using the term correctly.

The C++ spec doesn't call const a storage class explicitly, but it is clear in saying what the "linkage" of a const declaration is, which is what the storage class is. It also fits in the grammar with storage classes, i.e. you can do things like: const static int x = 3;
 The similar looking, but drastically different statement "const int *

So I infer you agree that C++ is not consistent or intuitive <g>.
 p = whatever;" uses const as a type constructor, equivalent to D's
 const(int). In C++, this can also be written as "int const * p" (a
 style I prefer as it now reads from right to left as "p is a pointer
 to const int").

 As far as member functions go, the C++ const storage class cannot be
 applied to them because it makes no sense (because a member function
 cannot be declared at global scope. By definition, it is always in the
 scope of the enclosing class). Thus, when I write (in C++)
 
     class A
     {
         int f() const;
     }
 
 The word "const" is /not/ acting as a "storage class". Instead, it
 tells us that "this" is const within f.
 
 
 
 D's const storage class applies to the declaration. That makes intuitive
 sense, although it is different from C++.

But what makes no sense is the very notion of a function (as opposed to its "this" pointer) being const. All functions are, by definition, invariant. That is, the bytes of machine code which are executed, may not be changed by any part of the program. If you take the address of those bytes of machine code, that's a pointer to invariant. The RAM utilitised during that function's execution (the stack, or the member variables, or both) are part of that function's context, and we care whether or not the function is allowed to modify those variables in the course of its execution. Thus, the kind of constness we are talking about is the constness of the function's context, not of the function itself. You can call that a "storage class" if you want, but it means something very different from a variable declared at global scope. So what you're saying as that the meaning of the word "const" changes from place to place, depending on what it applies to. For instance class A { const { int x; int f(); } } This /in fact/ means that the class A has a member variable called x, which is a const(int), and a function f, which does not modify anything through this. Those are two totally different meanings of "const"
 Every other storage class applies to the declaration, why should const
 be completely different?

Functions can't have storage classes. The notion of a function being anything other than invariant ... well, it just doesn't happen in today's world (though I do remember the old days of self-modifying code). The storage class of a function's /context/ - that's a different thing, and needs a different syntax.

I think this over analyzes things, and it looks for consistency where there simply cannot be (and there aren't in C++, either).
 ...and that's all I'm suggesting really. Let the syntax be
 const(this), instead of merely const. (I can't claim credit for that
 idea. Someone else came up with that one. I just like it lots). That
 makes it clear that we're talking about the storage class of the
 function's context, and explicitly names that context as the
 function's "this".

I don't think it adds anything but extra typing.
 My objection to the existing syntax is more or less solely that
     const int f();
 
 does not declare that f returns a const int, and I think that all
 newcomers to D will find that /hugely/ confusing.

Const is different in D than in C++, and it will take some getting used to for refugees from C++. Anyone who expects it to behave exactly like C++ will have difficulty. But if they spend a little time looking at it and give it a chance, I think they'll find that it is more consistent, intuitive, and usable than C++ const. Just remember, the const storage class applies to the declaration, not the type, and it will make sense.
Nov 28 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Janice Caron wrote:

 My objection to the existing syntax is more or less solely that
     const int f();

 does not declare that f returns a const int, and I think that all
 newcomers to D will find that /hugely/ confusing.

Const is different in D than in C++, and it will take some getting used to for refugees from C++. Anyone who expects it to behave exactly like C++ will have difficulty. But if they spend a little time looking at it and give it a chance, I think they'll find that it is more consistent, intuitive, and usable than C++ const. Just remember, the const storage class applies to the declaration, not the type, and it will make sense.

Here's a class that compiles fine and will probably mostly work. class Klass { this() { array.length = 10; } const int elem(int i) { return array[i]; } int[] array; } But someday when someone tries to call elem() with a const Klass it will fail to compile. Also in case anyone else wasn't sure, this is how you declare the common idiom of const method returning a const reference in the latest D2.008: const const(int*) get_elem_ptr(int i) {...} Without the parens it gives "Redundant storage class". (However declaring it "public public public" still does not create an error...) Also const int* get_elem_ptr(int i) fails to compile because it tries to return a mutable pointer from a const method. So that case is ok, but the one in Klass above seems like trouble brewing to me. --bb
Nov 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Here's a class that compiles fine and will probably mostly work.
 
 class Klass {
 
     this() {
         array.length = 10;
     }
     const int elem(int i) {
         return array[i];
     }
 
     int[] array;
 
 }
 
 But someday when someone tries to call elem() with a const Klass it will 
 fail to compile.

Yes. So where's the problem? Also, why would one want to return a "const int"? It doesn't make much sense.
 Also in case anyone else wasn't sure, this is how you declare the common 
 idiom of const method returning a const reference in the latest D2.008:
 
    const const(int*) get_elem_ptr(int i) {...}
 
 Without the parens it gives "Redundant storage class".  (However 
 declaring it "public public public" still does not create an error...)
 
 Also const int* get_elem_ptr(int i) fails to compile because it tries to 
 return a mutable pointer from a const method.  So that case is ok, but 
 the one in Klass above seems like trouble brewing to me.
 
 --bb

Nov 28 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 Here's a class that compiles fine and will probably mostly work.

 class Klass {

     this() {
         array.length = 10;
     }
     const int elem(int i) {
         return array[i];
     }

     int[] array;
 }

 But someday when someone tries to call elem() with a const Klass it 
 will fail to compile.

Yes. So where's the problem? Also, why would one want to return a "const int"? It doesn't make much sense.

Doh. Good point. It doesn't make much sense returning a const int. I started with the function "const int*elem()", found out that didn't compile, and tweaked it so it would compile. But I forgot to double check that the resulting code still made sense. :-o --bb
Nov 28 2007
parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Thu, 29 Nov 2007 04:53:43 -0000, Bill Baxter  =

<dnewsgroup billbaxter.com> wrote:

 Walter Bright wrote:
 Bill Baxter wrote:
 Here's a class that compiles fine and will probably mostly work.

 class Klass {

     this() {
         array.length =3D 10;
     }
     const int elem(int i) {
         return array[i];
     }

     int[] array;
 }

 But someday when someone tries to call elem() with a const Klass it =



 will fail to compile.



 "const  int"? It doesn't make much sense.

Doh. Good point. It doesn't make much sense returning a const int. I started with the function "const int*elem()", found out that didn't =

 compile, and tweaked it so it would compile.  But I forgot to double  =

 check that the resulting code still made sense.  :-o

 --bb

I personally agree that const on value types in C++ is a stupid idea. Meyers recommends it (effective C++) for the following case however: T operator*(T,T) if ((a*b)=3Dc)) { //oops! instead of: if ((a*b)=3D=3Dc) { Personally I prefer to use lint to disallow assignment in boolean = expressions. There is a school that uses const value types even for builtins like int= . I am currently trying to convince people at work that it is pointless an= d = neither being const correct not just a style thing. By the way to the const(this) advocates. Though the idea looks nice on t= he = surface it isn't quite consistent. const works on type (declarations), this is a= = variable not a type. Regards, Bruce.
Nov 29 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Bruce Adams wrote:
 On Thu, 29 Nov 2007 04:53:43 -0000, Bill Baxter 
 <dnewsgroup billbaxter.com> wrote:
 
 Walter Bright wrote:
 Bill Baxter wrote:
 Here's a class that compiles fine and will probably mostly work.

 class Klass {

     this() {
         array.length = 10;
     }
     const int elem(int i) {
         return array[i];
     }

     int[] array;
 }

 But someday when someone tries to call elem() with a const Klass it 
 will fail to compile.

"const int"? It doesn't make much sense.

Doh. Good point. It doesn't make much sense returning a const int. I started with the function "const int*elem()", found out that didn't compile, and tweaked it so it would compile. But I forgot to double check that the resulting code still made sense. :-o --bb

I personally agree that const on value types in C++ is a stupid idea. Meyers recommends it (effective C++) for the following case however: T operator*(T,T) if ((a*b)=c)) { //oops! instead of: if ((a*b)==c) {

Oh, yeh, I do vaguely remember that one. Good point. Also for opIndex it can be used to prevent people thinking they're modifying the array when they're not: struct FakeArray { int opIndex(int i) { return i*i; } } FakeArray x; x[4]; //ok x[4]++; // oops, only increments a tmp! probably not what you meant! whereas if it returns a 'const int' the compiler will tell you you're an idiot.
 Personally I prefer to use lint to disallow assignment in boolean 
 expressions.
 There is a school that uses const value types even for builtins like int.
 I am currently trying to convince people at work that it is pointless 
 and neither
 being const correct not just a style thing.

I don't see much point in decorating all your value parameters with it. But I've seen code written in that style. All it does is promise to the caller that the function won't modify something that the caller can't see anyway. Why do that?
 By the way to the const(this) advocates. Though the idea looks nice on 
 the surface
 it isn't quite consistent. const works on type (declarations), this is a 
 variable not
 a type.

Ok. Let's make it "const(typeof(this))" then. :-) Actually that's why I kind of preferred my "const()" suggestion. It's applying const to the 'this' parameter which of course you can't see, because it's implicit. --bb
Nov 29 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Bill Baxter wrote:
 Bruce Adams wrote:
 On Thu, 29 Nov 2007 04:53:43 -0000, Bill Baxter 
 <dnewsgroup billbaxter.com> wrote:

 Walter Bright wrote:
 Bill Baxter wrote:
 Here's a class that compiles fine and will probably mostly work.

 class Klass {

     this() {
         array.length = 10;
     }
     const int elem(int i) {
         return array[i];
     }

     int[] array;
 }

 But someday when someone tries to call elem() with a const Klass it 
 will fail to compile.

"const int"? It doesn't make much sense.

Doh. Good point. It doesn't make much sense returning a const int. I started with the function "const int*elem()", found out that didn't compile, and tweaked it so it would compile. But I forgot to double check that the resulting code still made sense. :-o --bb

I personally agree that const on value types in C++ is a stupid idea. Meyers recommends it (effective C++) for the following case however: T operator*(T,T) if ((a*b)=c)) { //oops! instead of: if ((a*b)==c) {

Oh, yeh, I do vaguely remember that one. Good point. Also for opIndex it can be used to prevent people thinking they're modifying the array when they're not: struct FakeArray { int opIndex(int i) { return i*i; } } FakeArray x; x[4]; //ok x[4]++; // oops, only increments a tmp! probably not what you meant! whereas if it returns a 'const int' the compiler will tell you you're an idiot.

My bad, the compiler actually catches that. But it doesn't catch it if you make opIndex return a struct with an opPostInc. And ooh another D2 bug: actually making it return a const(Strukt) crashes the compiler. --bb
Nov 29 2007
prev sibling parent reply James Dennett <jdennett acm.org> writes:
Bruce Adams wrote:

[snip]

 I personally agree that const on value types in C++ is a stupid idea.
 Meyers recommends it (effective C++) for the following case however:
 
 T operator*(T,T)
 
 if ((a*b)=c)) { //oops!
 
 instead of:
 
 if ((a*b)==c) {
 
 Personally I prefer to use lint to disallow assignment in boolean
 expressions.

A good compiler can do that for you too (though using lint is a fine idea to supplement even a good compiler). (Scott's advice here doesn't suit me; "do as the ints do" is
 There is a school that uses const value types even for builtins like int.
 I am currently trying to convince people at work that it is pointless
 and neither
 being const correct not just a style thing.

It's not pointless, and it's not just a style thing. It avoids defects, and it makes code clearer for those who are used to both styles. Maybe instead of setting out to convince people of something, you could set out to discover which solution really works better in practice. Using const has known advantages in expressiveness and defect elimination; apart from saving typing, what's the benefit of *not* recording the design decision that some value will not change during execution? It's a very lightweight, compile-time enforced invariant check. -- James
Dec 17 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
James Dennett wrote:
 Bruce Adams wrote:
 
 [snip]
 
 I personally agree that const on value types in C++ is a stupid idea.
 Meyers recommends it (effective C++) for the following case however:

 T operator*(T,T)

 if ((a*b)=c)) { //oops!

 instead of:

 if ((a*b)==c) {

 Personally I prefer to use lint to disallow assignment in boolean
 expressions.

A good compiler can do that for you too (though using lint is a fine idea to supplement even a good compiler). (Scott's advice here doesn't suit me; "do as the ints do" is
 There is a school that uses const value types even for builtins like int.
 I am currently trying to convince people at work that it is pointless
 and neither
 being const correct not just a style thing.

It's not pointless, and it's not just a style thing. It avoids defects, and it makes code clearer for those who are used to both styles. Maybe instead of setting out to convince people of something, you could set out to discover which solution really works better in practice. Using const has known advantages in expressiveness and defect elimination; apart from saving typing, what's the benefit of *not* recording the design decision that some value will not change during execution? It's a very lightweight, compile-time enforced invariant check.

I can't speak for all the cases the original poster was talking about, but const on value parameters is useless excess verbiage in my opinion. float sin(const float) { ... } Here I'm recording my design decision that my implementation of sin will not change its copy of the float argument passed in. Great. Callers just don't care. Such uses of const clutter up the interface for no good reason in my opinion. Most other uses of const on simple value types I'm ok with. --bb
Dec 17 2007
next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Bill Baxter wrote:

 I can't speak for all the cases the original poster was talking about, 
 but const on value parameters is useless excess verbiage in my opinion.
 
 float sin(const float) { ... }
 
 Here I'm recording my design decision that my implementation of sin will 
 not change its copy of the float argument passed in.
 
 Great.  Callers just don't care.  Such uses of const clutter up the 
 interface for no good reason in my opinion.  Most other uses of const on 
 simple value types I'm ok with.

Isn't there a value in knowing the argument will never change when passing potentially large structs to functions by value? For example, calling void foo(Struct a); will in most cases mean that a copy of the passed Struct is made on the callers stack, and the address to that copy is then passed to the callee. If the compiler knew the Struct argument never changed, it could avoid making several copies for consecutive calls to such functions, and if the called function was pure, avoid making a copy at all. -- Oskar
Dec 17 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Oskar Linde wrote:
 Bill Baxter wrote:
 
 I can't speak for all the cases the original poster was talking about, 
 but const on value parameters is useless excess verbiage in my opinion.

 float sin(const float) { ... }

 Here I'm recording my design decision that my implementation of sin 
 will not change its copy of the float argument passed in.

 Great.  Callers just don't care.  Such uses of const clutter up the 
 interface for no good reason in my opinion.  Most other uses of const 
 on simple value types I'm ok with.

Isn't there a value in knowing the argument will never change when passing potentially large structs to functions by value? For example, calling void foo(Struct a); will in most cases mean that a copy of the passed Struct is made on the callers stack, and the address to that copy is then passed to the callee. If the compiler knew the Struct argument never changed, it could avoid making several copies for consecutive calls to such functions, and if the called function was pure, avoid making a copy at all.

If the compiler can verify that no attempts were made to modify the struct when you label it "const" then it can also detect that no attempts were made to modify it even if you don't label it "const". And if so, it can do whatever optimization it would have done given a "const" label without burdening the developer with thinking about it. --bb
Dec 17 2007
parent James Dennett <jdennett acm.org> writes:
Bill Baxter wrote:
 Oskar Linde wrote:
 Bill Baxter wrote:

 I can't speak for all the cases the original poster was talking
 about, but const on value parameters is useless excess verbiage in my
 opinion.

 float sin(const float) { ... }

 Here I'm recording my design decision that my implementation of sin
 will not change its copy of the float argument passed in.

 Great.  Callers just don't care.  Such uses of const clutter up the
 interface for no good reason in my opinion.  Most other uses of const
 on simple value types I'm ok with.

Isn't there a value in knowing the argument will never change when passing potentially large structs to functions by value? For example, calling void foo(Struct a); will in most cases mean that a copy of the passed Struct is made on the callers stack, and the address to that copy is then passed to the callee. If the compiler knew the Struct argument never changed, it could avoid making several copies for consecutive calls to such functions, and if the called function was pure, avoid making a copy at all.

If the compiler can verify that no attempts were made to modify the struct when you label it "const" then it can also detect that no attempts were made to modify it even if you don't label it "const". And if so, it can do whatever optimization it would have done given a "const" label without burdening the developer with thinking about it.

Separate compilation means that the compiler can verify clients against the interface separately from verifying that the implementation honors the interface. The compiler need not have access to the implementation when compiling client code. -- James
Dec 18 2007
prev sibling parent James Dennett <jdennett acm.org> writes:
Bill Baxter wrote:
 James Dennett wrote:
 Bruce Adams wrote:

 [snip]

 I personally agree that const on value types in C++ is a stupid idea.
 Meyers recommends it (effective C++) for the following case however:

 T operator*(T,T)

 if ((a*b)=c)) { //oops!

 instead of:

 if ((a*b)==c) {

 Personally I prefer to use lint to disallow assignment in boolean
 expressions.

A good compiler can do that for you too (though using lint is a fine idea to supplement even a good compiler). (Scott's advice here doesn't suit me; "do as the ints do" is
 There is a school that uses const value types even for builtins like
 int.
 I am currently trying to convince people at work that it is pointless
 and neither
 being const correct not just a style thing.

It's not pointless, and it's not just a style thing. It avoids defects, and it makes code clearer for those who are used to both styles. Maybe instead of setting out to convince people of something, you could set out to discover which solution really works better in practice. Using const has known advantages in expressiveness and defect elimination; apart from saving typing, what's the benefit of *not* recording the design decision that some value will not change during execution? It's a very lightweight, compile-time enforced invariant check.

I can't speak for all the cases the original poster was talking about, but const on value parameters is useless excess verbiage in my opinion. float sin(const float) { ... } Here I'm recording my design decision that my implementation of sin will not change its copy of the float argument passed in. Great. Callers just don't care. Such uses of const clutter up the interface for no good reason in my opinion. Most other uses of const on simple value types I'm ok with.

And you're close to the crucial point. For C++, it makes sense to write a function *definition* with top-level const as above, to avoid typos in the function which accidentally modify its copy of the argument. It means *nothing* to the caller, so the function's declaration (which is part of its interface) need not (and, for style reasons, should not) include the const -- in a non-definition, top-level const is a meaningless comment, and the compiler ignores it. -- James
Dec 18 2007
prev sibling parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Mon, 17 Dec 2007 16:12:05 -0000, James Dennett <jdennett acm.org> wro=
te:

 Bruce Adams wrote:

 [snip]

 I personally agree that const on value types in C++ is a stupid idea.=


 Meyers recommends it (effective C++) for the following case however:

 T operator*(T,T)

 if ((a*b)=3Dc)) { //oops!

 instead of:

 if ((a*b)=3D=3Dc) {

 Personally I prefer to use lint to disallow assignment in boolean
 expressions.

A good compiler can do that for you too (though using lint is a fine idea to supplement even a good compiler).

y I discovered that gcc in every case I could think of produces a is not a= n l-value syntax error.
 (Scott's advice here doesn't suit me; "do as the ints do" is

 There is a school that uses const value types even for builtins like =


 int.
 I am currently trying to convince people at work that it is pointless=


 and neither
 being const correct not just a style thing.

It's not pointless, and it's not just a style thing. It avoids defects, and it makes code clearer for those who are used to both styles. Maybe instead of setting out to convince people of something, you could set out to discover which solution really works better in practice. Using const has known advantages in expressiveness and defect elimination; apart from saving typing, what's the benefit of *not* recording the design decision that some value will not change during execution? It's a very lightweight, compile-time enforced invariant check. -- James

You misread me. I am strongly in favour of const-correctness. My point w= as about const on value returns, which do not contribute to const correctne= ss. const is about the interface contract for a function. There is no = difference between const valueType1 foobar(const valueType1) and valueType1 foobar(valueType2) valueType1 is not an l-value and therefore not assignable etc. valueType2 the situation is not quite the same but I would contend that.= . Whether or not valueType2 is modified by foobar is an implementation = detail and should not be part of the contract. You can just as easily say in the body: foobar(valueType2 bar) { const valueType2 IamConst =3D bar; ... use IamConst } or foobar(const valueType2) { valueType2 IamNotConst =3D bar; ... use IamNotConst } constness helps a little here but it affects the implementation only. Ideally I'd like the to be able to declare constness of an argument in the implementation but leave it out of the declaration. Implementation i= n = the compiler wise it would be trivial but syntactically its hard to think of somethin= g = appropriate. In D this is even weirder because you don't have separate declarations a= nd = implementations.
Dec 17 2007
next sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Mon, 17 Dec 2007 19:16:34 -0000, Bruce Adams  =

<tortoise_74 yeah.who.co.uk> wrote:

 not be part of the contract.
 You can just as easily say in the body:

 foobar(valueType2 bar)
 {
    const valueType2 IamConst =3D bar;
    ... use IamConst
 }

 or

 foobar(const valueType2)
 {
    valueType2 IamNotConst =3D bar;
    ... use IamNotConst
 }

 constness helps a little here but it affects the implementation only.
 Ideally I'd like the to be able to declare constness of an argument in=

 the implementation but leave it out of the declaration. Implementation=

 in the compiler
 wise it would be trivial but syntactically its hard to think of  =

 something appropriate.
 In D this is even weirder because you don't have separate declarations=

 and implementations.

u = like in your code and your documentation, doxygen or whatever should leave the const = = out.
Dec 17 2007
prev sibling parent reply Dan <murpsoft hotmail.com> writes:
Bruce Adams Wrote:
 const is about the interface contract for a function. There is no  
 difference
 between    const valueType1 foobar(const valueType1)
 and        valueType1 foobar(valueType2)
 
 valueType1 is not an l-value and therefore not assignable etc.
 
 valueType2 the situation is not quite the same but I would contend that..
 Whether or not valueType2 is modified by foobar is an implementation  
 detail and should
 not be part of the contract.
 You can just as easily say in the body:
 
 foobar(valueType2 bar)
 {
    const valueType2 IamConst = bar;
    ... use IamConst
 }
 
 or
 
 foobar(const valueType2)
 {
    valueType2 IamNotConst = bar;
    ... use IamNotConst
 }
 

What if I want to pass a struct by value without copying - because I only want the changed struct? (the function is SUPPOSED to modify my struct?) In fact, that's just about the only time I ever pass structs to functions - when I want to modify them, or when I want to get something out of them. Rarely ever because I want to copy them and keep both the unmodified copy and the modified one. Regards, Dan
Dec 17 2007
parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Tue, 18 Dec 2007 00:06:25 -0000, Dan <murpsoft hotmail.com> wrote:
 What if I want to pass a struct by value without copying - because I  
 only want the changed struct?  (the function is SUPPOSED to modify my  
 struct?)

 In fact, that's just about the only time I ever pass structs to  
 functions - when I want to modify them, or when I want to get something  
 out of them.  Rarely ever because I want to copy them and keep both the  
 unmodified copy and the modified one.

 Regards,
 Dan

I can imagine how to do that in assembly. Its not possible in C/C++ Can you do that in D currently? Presumably the syntax would be: foo(inout valueType bar); In C++ that would have to be done with pass by reference. But this is well off topic, it has nothing to do with constness of value types.
Dec 17 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 So, how do I declare dg to be a delegate which can call a const member
function?
 
 Would it be
 
     (const int delegate()) dg;
 
 ?

No. Storage class are not type constructors. To use const as a type constructor, use (): const(int delegate()) dg;
Nov 28 2007
prev sibling next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright wrote:

 Because those alternatives all look terrible. And frankly, how could 
 anyone be confused about what:
     const x = 3;
 means? I can't understand throwing that out to improve clarity?

Honestly, I am confused about what that means. If I define an integer constant x, don't I want a pointer to that constant to be invariant(int)*? Doesn't that mean that constants should rather be defined as: invariant x = 3; Or am I wrong? What am I missing here? I am to tired right now to reiterate my full thoughts on the keywords (and I am sure you are all very thankful for that :) ), but it still feels like the keywords are reversed: invariant === constant const === read only -- Oskar
Nov 28 2007
next sibling parent Sean Kelly <sean f4.ca> writes:
Oskar Linde wrote:
 
 I am to tired right now to reiterate my full thoughts on the keywords 
 (and I am sure you are all very thankful for that :) ), but it still 
 feels like the keywords are reversed:
 
 invariant === constant
     const === read only

I can't stand the use of "invariant" as a qualifier either, but what can you do. May as well place it next to "foreach_reverse" on the shelf of horrifying semantics and move on. As long as the semantics of const correctness improve then that will have to do. Sean
Nov 28 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Oskar Linde wrote:
 Walter Bright wrote:
 
 Because those alternatives all look terrible. And frankly, how could 
 anyone be confused about what:
     const x = 3;
 means? I can't understand throwing that out to improve clarity?

Honestly, I am confused about what that means. If I define an integer constant x, don't I want a pointer to that constant to be invariant(int)*? Doesn't that mean that constants should rather be defined as: invariant x = 3; Or am I wrong? What am I missing here?

For a basic type, the meaning of const and invariant overlap. The difference between them is when you have pointers or other references. For simple integers, you can use const or invariant.
 I am to tired right now to reiterate my full thoughts on the keywords 
 (and I am sure you are all very thankful for that :) ), but it still 
 feels like the keywords are reversed:
 
 invariant === constant
     const === read only

We went around this for a while. There is no word that unambiguously means "read only view" while another word meaning unambiguously "will never change". So, since C++ uses const to mean "read only view", it seemed best to leave that as it was. readonly, constant, invariant, immutable, are all synonyms.
Nov 28 2007
next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright wrote:
 Oskar Linde wrote:
 Walter Bright wrote:

 Because those alternatives all look terrible. And frankly, how could 
 anyone be confused about what:
     const x = 3;
 means? I can't understand throwing that out to improve clarity?

Honestly, I am confused about what that means. If I define an integer constant x, don't I want a pointer to that constant to be invariant(int)*? Doesn't that mean that constants should rather be defined as: invariant x = 3; Or am I wrong? What am I missing here?

For a basic type, the meaning of const and invariant overlap. The difference between them is when you have pointers or other references. For simple integers, you can use const or invariant.

Ok, but it is then unfortunate that having two constants: invariant a = 1; const b = 2; that typeof(&a) != typeof(&b). And that given those types, only one is convertible to the other. Since invariant(int)* is convertible to const(int)*, it seems to me that there is a good reason to define constants using invariant, rather than const. invariant(int)* also captures the full meaning of a pointer to constant data.
 I am to tired right now to reiterate my full thoughts on the keywords 
 (and I am sure you are all very thankful for that :) ), but it still 
 feels like the keywords are reversed:

 invariant === constant
     const === read only

We went around this for a while. There is no word that unambiguously means "read only view" while another word meaning unambiguously "will never change". So, since C++ uses const to mean "read only view", it seemed best to leave that as it was.

Thank you for this motivation. Regarding describing words, in my book the word "constant" pretty well describes something that "will never change". And just like: float means "floating point number" int means "integer" const could mean "constant" readonly could mean "read only view" It wouldn't be hard to explain or motivate. And regarding C++, it doesn't seem to me like const was originally intended to mean "read only view". I'd guess the logical evolution was 1. const as a storage class to replace #defines -> 2. pointers to const -> 3. contract const (I promise I wont change this). Should D really copy only the most unintended part of what const is from C++ and change the most intended one?
 readonly, constant, invariant, immutable, are all synonyms.

Only for a very weak definition of synonyms, i.e. "has overlapping meanings". That doesn't mean that they can (or should) be used interchangeably. For example, you can say that secure and protected are synonyms, but if you went to climb a cliff and told people you had "protected the rope", I bet at least a few of them would be confused. :) I'm only saying that I think there is another set of keywords that have a much higher match between the dictionary meaning of the words and their semantic meaning in D. -- Oskar
Nov 29 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Oskar Linde wrote:
 Ok, but it is then unfortunate that having two constants:
 
 invariant a = 1;
 const     b = 2;
 
 that typeof(&a) != typeof(&b).

It's not that bad. Consider: int a = 1; float b = 1; They're different types, too.
 And regarding C++, it 
 doesn't seem to me like const was originally intended to mean "read only 
 view". I'd guess the logical evolution was 1. const as a storage class 
 to replace #defines -> 2. pointers to const -> 3. contract const (I 
 promise I wont change this). Should D really copy only the most 
 unintended part of what const is from C++ and change the most intended one?

Is it really unintended? But even so, if you have invariant, you must have const too, since everything is implicitly convertible to const, and *that* is definitely intended in C++.
 I'm only saying that I think there is another set of keywords that have 
 a much higher match between the dictionary meaning of the words and 
 their semantic meaning in D.

I don't believe there is (and we looked!), and readonly isn't it. To me, readonly means ROM (burned in to read only memory chips), not read only view. I've even seen 'readonly' used in some languages to literally mean "this stuff goes into ROM".
Nov 29 2007
next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright wrote:
 Oskar Linde wrote:
 Ok, but it is then unfortunate that having two constants:

 invariant a = 1;
 const     b = 2;

 that typeof(&a) != typeof(&b).

It's not that bad. Consider: int a = 1; float b = 1; They're different types, too.

Oh, really? ;p. I don't think that is much of an argument. Think of the poor new user to D: Q: How do I define constants? A: Use invariant or const. Just pick one. Q: Are they equivalent? A: No, they result in distinct types. Q: So which one should I really use? A: Invariant is better. The references to the resulting type are then implicitly convertible to the other. Q: So why is the other one called "const"? A: ...
 And regarding C++, it doesn't seem to me like const was originally 
 intended to mean "read only view". I'd guess the logical evolution was 
 1. const as a storage class to replace #defines -> 2. pointers to 
 const -> 3. contract const (I promise I wont change this). Should D 
 really copy only the most unintended part of what const is from C++ 
 and change the most intended one?

Is it really unintended? But even so, if you have invariant, you must have const too, since everything is implicitly convertible to const, and *that* is definitely intended in C++.

No, I can't say it was unintended. Rephrased, I will instead say that the "read only view" part of const is just one part of what const is in C++. The other part is the constant storage class that I've tried to argue is, in the case of constants at least, better handled by "invariant" in D. We can probably agree that the "const" keyword was chosen as an abbreviation for "constant", and as such, it is unfortunate that it now has lost the last connection to the meaning of that word.
 I'm only saying that I think there is another set of keywords that 
 have a much higher match between the dictionary meaning of the words 
 and their semantic meaning in D.

I don't believe there is (and we looked!), and readonly isn't it. To me, readonly means ROM (burned in to read only memory chips), not read only view. I've even seen 'readonly' used in some languages to literally mean "this stuff goes into ROM".

I know you are of that view, and your strongest association with the word "readonly" is in the sense of ROM, but I do believe you are in a minority. Uses of read-only in the context of access attributes, registers and views are well established and maps very well to the current "const" keyword. I expanded those arguments in: http://www.digitalmars.com/d/archives/digitalmars/D/Const_sucks_58017.html#N58135 and http://www.digitalmars.com/d/archives/digitalmars/D/D_const_design_rationale_54725.html#N54970 But I agree that "readonly" is far from a perfect keyword. The worst thing about it is that it is a contraction of two words (albeit a quite common one, yielding 3.5 million hits on google). To summarize, I believe the arguments for the current keywords are: * readonly is out of question * following that, no better keywords have come up * invariant was already a keyword * const will be the most used form and therefore uses the most familiar keword * const has a similar meaning in C++ The only reason I keep posting about this must be because I am an incurable optimist. Or maybe I am just too stubborn to realize I've lost. Or maybe I'm just stuck at the romantic idea that the const issue could be resolved in a way that leaves const meaning constant. :P Another alternative to readonly could be to redefine the seldom used protection attribute "protected". Protected is perfect in many ways. A protected pointer or a protected slice of memory makes sense. It is a single word and has no confusing associations. Const could then be redefined to mean "constant" again. const -> protected invariant -> const The old protection attribute could be renamed: private(class), and at the same time, the package keyword could be removed and renamed private(package), with a net result of 1 less keyword than today, and no confusion about what keyword to use for defining constants. -- Oskar
Nov 29 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Janice Caron wrote:
<snip>
 So why not let "const" do double-duty?
 
 That is, we keep
 (*) const as a storage-class
 (*) const as a type-constructor
 (*) invariant as a type-constructor
 
 and we lose
 (*) invariant as a storage class
 
 with the caveat that when const-as-storage-class is applied to types
 which require storage, it actually means invariant (but when applied
 to member functions it retains its existing meaning). If we were to
 adopt this practice then:
 
      const x = 3;
      auto p = &x;
 
 would result in x having type invariant(int), and p having type
 invariant(int)*. Since invariant can implicitly cast to const, this is
 guaranteed not to break anything.

I think I'd rather loose: (*) const as a storage-class and make the separation between const and invariant more pronounced. There would be no need for the caveat above, however: const x = 5; would become illegal and this breaks existing valid 1.0 code. Regan
Nov 29 2007
parent Sean Kelly <sean f4.ca> writes:
Regan Heath wrote:
 
 I think I'd rather loose:
 (*) const as a storage-class
 
 and make the separation between const and invariant more pronounced.
 
 There would be no need for the caveat above, however:
 
 const x = 5;
 
 would become illegal and this breaks existing valid 1.0 code.

This is one reason I think "const" should be used for "data that will never change" in D 2.0. It's what it is in D 1.0. The concept is simply being extended. Sean
Nov 29 2007
prev sibling next sibling parent Kenny TM~ <kennytm gmail..com> writes:
Oskar Linde wrote:

 Const could then be redefined to mean "constant" again.
 const -> protected
 invariant -> const
 
 The old protection attribute could be renamed:
 
 private(class),
 
 and at the same time, the package keyword could be removed and renamed
 
 private(package),
 
 with a net result of 1 less keyword than today, and no confusion about 
 what keyword to use for defining constants.
 

No, please, no. This breaks too many codes, and makes protected ubyte[] bitmap_data; parsed to something unexpected. I think "readonly" is OK, and also used in C#, but the cons are also apparent as discussed before. Maybe "invariant" for read-only view? :P (ouch, that's even more confusing) -- -- Kenny.
Nov 29 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Invariant member functions: you argued these are unnecessary. I believe 
they are, for exactly the same reason as any other invariant references 
passed as parameters. Invariant is needed to support functional 
programming, better code optimization, and more reliable multithreaded code.


const as storage class: Const local variables can be initialized with 
const types, so there is definitely a place for them:

void foo(const int *p)
{
     const q = p;
}

q does *not* point to an invariant.

Furthermore, being able to declare const variables is necessary as they 
are a boundary case in writing generic code. Boundary cases need to be 
supported to make writing generic code easier, even if such code would 
be pointless if done manually. For example, the compiler doesn't 
disallow (0==0), even though it is pointless to write such.
Nov 29 2007
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Invariant member functions: you argued these are unnecessary. I believe 
 they are, for exactly the same reason as any other invariant references 
 passed as parameters. Invariant is needed to support functional 
 programming, better code optimization, and more reliable multithreaded 
 code.

I believe pure functions are required to support functional programming and more reliable multithreaded code. Given that an invariant function is allowed to change global state, it cannot be assumed that it is safe to reorder invariant function calls, cache results, or call multiple invariant functions without synchronization on multiple threads. invariant as a *storage* attribute is required for pure functions and reliable multithreaded calls. However, I think invariant functions would be good for optimization, as you can keep member data in registers between calls, etc. -Steve
Nov 29 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Oskar Linde wrote:
 Walter Bright wrote:
 Oskar Linde wrote:
 Ok, but it is then unfortunate that having two constants:

 invariant a = 1;
 const     b = 2;



FWIW my naive expectation was that "invariant a = 1" would give you something you can't take the address of because it may be inlined or whatever, whereas "const b=2" I expected to be deref-able and actually live somewhere in memory. I think the reason is that I had come to understand 'invariant' as being the one that lets the compiler do sneaky optimizations and make assumptions that something really will not change ever, whereas const merely says the reference you have isn't changing but someone else might be changing it.
 I know you are of that view, and your strongest association with the 
 word "readonly" is in the sense of ROM, but I do believe you are in a 
 minority. Uses of read-only in the context of access attributes, 
 registers and views are well established and maps very well to the 
 current "const" keyword. I expanded those arguments in:

I agree the meaning of "readonly" is perfect, (and how on earth could it mean "put this data in ROM"? It's physically impossible unless D was some kind of EPROM burning software). BUT those three extra letters over "const" do bother me. Because in function signatures it can often be repeated 5 or more times. So though probably in the minority, I'd rather have it become "ro" if it's going to change. There is some other language that uses that. I can't remember which one. But anyway I'm not holding my breath waiting for Walter to make such a change.
 The only reason I keep posting about this must be because I am an 
 incurable optimist. Or maybe I am just too stubborn to realize I've 
 lost. Or maybe I'm just stuck at the romantic idea that the const issue 
 could be resolved in a way that leaves const meaning constant. :P

Or D) all of the above. :-) --bb
Nov 29 2007
next sibling parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Bill Baxter wrote:
 I agree the meaning of "readonly" is perfect, (and how on earth could it 
 mean "put this data in ROM"? It's physically impossible unless D was 
 some kind of EPROM burning software).  BUT those three extra letters 
 over "const" do bother me.  Because in function signatures it can often 
 be repeated  5 or more times.  So though probably in the minority, I'd 
 rather have it become "ro" if it's going to change.  There is some other 
 language that uses that.  I can't remember which one.

For function signatures, the "in" parameter attribute should save some typing (makes the parameters const). It is a pity const-by-default didn't work out.
 But anyway I'm not holding my breath waiting for Walter to make such a 
 change.

That would certainly be an unhealthy practice.
 The only reason I keep posting about this must be because I am an 
 incurable optimist. Or maybe I am just too stubborn to realize I've 
 lost. Or maybe I'm just stuck at the romantic idea that the const 
 issue could be resolved in a way that leaves const meaning constant. :P

Or D) all of the above. :-)

I am afraid you might be right. -- Oskar
Nov 29 2007
prev sibling next sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Thu, 29 Nov 2007 20:31:09 -0000, Bill Baxter  
<dnewsgroup billbaxter.com> wrote:

 I agree the meaning of "readonly" is perfect, (and how on earth could it  
 mean "put this data in ROM"? It's physically impossible unless D was  
 some kind of EPROM burning software).  BUT those three extra letters  
 over "const" do bother me.  Because in function signatures it can often  
 be repeated  5 or more times.  So though probably in the minority, I'd  
 rather have it become "ro" if it's going to change.  There is some other  
 language that uses that.  I can't remember which one.

the like. Object module formats like ELF allow allow for separate memory regions with attributes meaning "put me in rom". Embedded C/C++ compilers sometimes have extensions for this or #pragma's or special purpose linker languages. This is where readonly, ro, rodata and other terms spring up. D is I believe intended as a systems language than can replace C and C++ even in contexts like these. Because it is broad in scope like C & C++ you have the same high-level versus low-level conflicts. Ultimately its what it says in the manual that counts however bad the fit with the dictionary word. Regards, Bruce.
Nov 29 2007
prev sibling parent Russell Lewis <webmaster villagersonline.com> writes:
Bill Baxter wrote:
 I agree the meaning of "readonly" is perfect, (and how on earth could it 
 mean "put this data in ROM"? It's physically impossible unless D was 
 some kind of EPROM burning software).

You can always put these variables into virtual pages which are readonly. From the program's viewpoint, that is little different than ROM.
Nov 30 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 
 I'm only saying that I think there is another set of keywords that 
 have a much higher match between the dictionary meaning of the words 
 and their semantic meaning in D.

I don't believe there is (and we looked!), and readonly isn't it. To me, readonly means ROM (burned in to read only memory chips), not read only view. I've even seen 'readonly' used in some languages to literally mean "this stuff goes into ROM".

I favor "view" but I suppose it's too common a word. "guard" seems reasonable as well, but is also probably too common. Sean
Nov 29 2007
prev sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Thu, 29 Nov 2007 01:48:02 -0000, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 Oskar Linde wrote:
 Walter Bright wrote:

 Because those alternatives all look terrible. And frankly, how could=



 anyone be confused about what:
     const x =3D 3;
 means? I can't understand throwing that out to improve clarity?



 constant x, don't I want a pointer to that constant to be  =


 invariant(int)*? Doesn't that mean that constants should rather be  =


 defined as:
  invariant x =3D 3;
  Or am I wrong? What am I missing here?

For a basic type, the meaning of const and invariant overlap. The =

 difference between them is when you have pointers or other references.=

 For simple integers, you can use const or invariant.

 I am to tired right now to reiterate my full thoughts on the keywords=


 (and I am sure you are all very thankful for that :) ), but it still =


 feels like the keywords are reversed:
  invariant =3D=3D=3D constant
     const =3D=3D=3D read only

We went around this for a while. There is no word that unambiguously =

 means "read only view" while another word meaning unambiguously "will =

 never change". So, since C++ uses const to mean "read only view", it  =

 seemed best to leave that as it was.

 readonly, constant, invariant, immutable, are all synonyms.

I still like my idea of compile time contracts / type contracts. Taking the thought experiment a bit too far... Give a type T with several interfaces you could define a contract which = = says that only certain interfaces of T can be used by the function. const T is like a (compile time) contract permitting only the use of the= = read only interface. invariant T is like a (compile time) contract that asserts X' =3D=3D X a= s a = pre-condition. i.e. the value of X will not change for the duration of the function. = Practically, this is still X is not writeable anywhere it can be used when the function is = called (i.e. it is invariant). If you had some way to specify constracts like this at compile time you = = could build head constness, tail constenss, invariantness and various other -nesses = as = library code. What you are doing with const and invariant is limiting the contracts = possible to (ideally) the most useful subset of those possible and adding syntactic sugar to let y= ou = apply them to a declaration easily. Another way to view this is that the contracts specialise the types they= = apply to. This is stretching inheritance a bit as constT needs to be a T i.e. publ= ic = inheritance / isa but needs to violate its normal interface in a way more suitable to = private inheritance / is implemented in terms of class T { int foo(); int bar() const; } const T =3D class constT: private T { int bar() const =3D T::bar; } or: class constT: public T { int bar() const =3D T::bar; private: override int foo() =3D compile_time_error("illegal attempt to invoke= = non-const method"); } You could almost have const(T) as a (particularly evil) template now. Perhaps this will inspire other options? Regards, Bruce. PS You could always invent words rov and wnc - wince ;)
Nov 29 2007
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Janice Caron wrote:
 Also, C++ has const as both a storage class and a type constructor, and
 yes, they have subtly different meanings. This doesn't seem to cause any
 major problems.

That's because C++ behaves as expected. If I declare const int * f(); then I get a function which returns a pointer to const int, /not/ a const member function which returns a pointer to mutable int.

What is expected, or what you are *used* to? Let me put it another way, can you explain the difference between C++ const storage class and C++ const type constructors? There's a big semantic difference.

Let me reword Walter's point, as I think he didn't really get it across completely :) Let's say someone had never used C++ and it's const system. Let's say that person is a virgin coder starting to dabble in D. Let's explain how const works to him/her: to declare that a type is constant, you do: const(Type) x; to declare that a method returns a const value, you do: const(Type) f(); to declare that a method is constant, that is, it will not modify any members of a class the method resides in, you do: const Type f(); If that method returns a const type, then the method is declared as: const const(Type) f(); for compatibility with C++ and C, you can also do: const Type x; for a const type declaration, and Type f() const for a const method declaration. To someone who never used const before in another language, this isn't any less or more intuitive than the way C++ does it. It's like saying using semicolons at the end of statements is intuitive. It's just a different way of expressing what you want to the compiler. -Steve
Nov 28 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 28 Nov 2007 23:02:06 -0500, Steven Schveighoffer wrote:

 If that method returns a const type, then the method is declared as:
 
 const const(Type) f();

 To someone who never used const before in another language, this isn't any 
 less or more intuitive than the way C++ does it.  It's like saying using 
 semicolons at the end of statements is intuitive.  It's just a different way 
 of expressing what you want to the compiler.

Got it. When I see two adjacent "const" keywords I need to realize that the first const refers to the second 'thing' and the second const refers to the first 'thing'. Very intuitive. However ... what if we tried to make things unintuitive ... like having the first const refer to the first thing and the second const refer to the second const ... Nah ... on second thoughts that would only confuse people. Who'd ever work out that ... const(Type) const f(); means that the 'Type' is const and the f() is also const? -- Derek (skype: derek.j.parnell) Melbourne, Australia 29/11/2007 3:07:14 PM
Nov 28 2007
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Derek Parnell" wrote
 On Wed, 28 Nov 2007 23:02:06 -0500, Steven Schveighoffer wrote:

 If that method returns a const type, then the method is declared as:

 const const(Type) f();

 To someone who never used const before in another language, this isn't 
 any
 less or more intuitive than the way C++ does it.  It's like saying using
 semicolons at the end of statements is intuitive.  It's just a different 
 way
 of expressing what you want to the compiler.

Got it. When I see two adjacent "const" keywords I need to realize that the first const refers to the second 'thing' and the second const refers to the first 'thing'. Very intuitive.

It is if you think in stacks :)
 However ... what if we tried to make things unintuitive ... like having 
 the
 first const refer to the first thing and the second const refer to the
 second const ... Nah ... on second thoughts that would only confuse 
 people.
 Who'd ever work out that ...

 const(Type) const f();

 means that the 'Type' is const and the f() is also const?

Hey, I'm not saying it's the best way :) I'm just saying it's no less or more intuitive than the C++ way: const Type f() const It's just different. I'm all for the const(this) format suggested in another branch of this thread, as I think that's the most intuitive. -Steve
Nov 28 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Wed, 28 Nov 2007 23:02:06 -0500, Steven Schveighoffer wrote:
 
 If that method returns a const type, then the method is declared as:

 const const(Type) f();

 To someone who never used const before in another language, this isn't any 
 less or more intuitive than the way C++ does it.  It's like saying using 
 semicolons at the end of statements is intuitive.  It's just a different way 
 of expressing what you want to the compiler.

Got it. When I see two adjacent "const" keywords I need to realize that the first const refers to the second 'thing' and the second const refers to the first 'thing'. Very intuitive.

To make a type const, you add parens: const(T) So, to make a const function that returns a constant T: const(const(T) f()) The storage class const is just a shorthand for putting ( ) around the whole declaration.
Nov 28 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 28 Nov 2007 21:27:35 -0800, Walter Bright wrote:

 To make a type const, you add parens:
 
 	const(T)
 
 So, to make a const function that returns a constant T:
 
 	const(const(T) f())

Is this supposed to compile? I can't get it to work. For the source line ... const(const(int*) f()){} I get the message ... test.d(3): unexpected identifer 'f' in declarator test.d(3): no identifier for declarator const(int*)() -- Derek (skype: derek.j.parnell) Melbourne, Australia 29/11/2007 5:07:48 PM
Nov 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 Is this supposed to compile? I can't get it to work. 
 
 For the source line ...
 
   const(const(int*) f()){}
 
 
 I get the message ...
 
 test.d(3): unexpected identifer 'f' in declarator
 test.d(3): no identifier for declarator const(int*)()

Uh, you're right. You can't put the identifier inside a declarator.
Nov 29 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Thu, 29 Nov 2007 01:06:32 -0800, Walter Bright wrote:

 Derek Parnell wrote:
 Is this supposed to compile? I can't get it to work. 
 
 For the source line ...
 
   const(const(int*) f()){}
 
 I get the message ...
 
 test.d(3): unexpected identifer 'f' in declarator
 test.d(3): no identifier for declarator const(int*)()

Uh, you're right. You can't put the identifier inside a declarator.

Yes, I know one can't, but I actually asked if one is supposed to able to do it. Did you intend that "const(const(int*) f()){}" should compile, even though currently it doesn't? -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Nov 29 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 Did you intend that "const(const(int*) f()){}" should compile, even though
 currently it doesn't?

No. I just didn't think it through.
Nov 29 2007
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Steven Schveighoffer, el 28 de noviembre a las 23:02 me escribiste:
 "Walter Bright" wrote
 Janice Caron wrote:
 Also, C++ has const as both a storage class and a type constructor, and
 yes, they have subtly different meanings. This doesn't seem to cause any
 major problems.

That's because C++ behaves as expected. If I declare const int * f(); then I get a function which returns a pointer to const int, /not/ a const member function which returns a pointer to mutable int.

What is expected, or what you are *used* to? Let me put it another way, can you explain the difference between C++ const storage class and C++ const type constructors? There's a big semantic difference.

Let me reword Walter's point, as I think he didn't really get it across completely :) Let's say someone had never used C++ and it's const system. Let's say that person is a virgin coder starting to dabble in D. Let's explain how const works to him/her: to declare that a type is constant, you do: const(Type) x; to declare that a method returns a const value, you do: const(Type) f(); to declare that a method is constant, that is, it will not modify any members of a class the method resides in, you do: const Type f(); If that method returns a const type, then the method is declared as: const const(Type) f();

This sounds right.
 for compatibility with C++ and C, you can also do:
 
 const Type x;
 for a const type declaration, and
 
 Type f() const
 for a const method declaration.

This is what it's confusing. 1) Why does D need C++ compatibility? 2) Here is were all possible confusion about "what does const Type f() const means? Is in some places const Type x means const(Type) x and in other places it doesn't, it's really confusing, specially when reading code that mix styles. I think C++ compatibility should be dropped. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- CHINO ATRAPA COTORRAS -- Crónica TV
Nov 29 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Leandro Lucarella wrote:
 Is in some places const Type x means const(Type) x and in other places it
 doesn't,

No, const Type X always means const(Type) x, where Type is the type of the declaration.
Nov 29 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 11/26/07, Walter Bright <newshound1 digitalmars.com> wrote:
 At the beginning will still work for function types. The at the end
 option is there for those who are building complex type declarations.

and
 No. "const char[] X;" and "const(char[]) X;" mean the same thing.

It seems to me that the following is still ambiguous: class A { int n; } class B { const A f() { /*...*/ } } Does it mean (a) class B { const(A) f() { /*...*/ } } or does it mean (b) class B { A f() const { /*...*/ } } ?

(b). Think of it this way: const A f() means: const (A f()) which applies the const to the function f, not A.
 Also, is it still possible to write
 
     class C
     {
         int n;
         void f() invariant { /*...*/ };
     }

Yes.
 (I put the keyword at the end to avoid confusion. In D2.007, you'd
 write "invariant" before "void"). My interpretation of the above code
 is that f is being called with a hidden parameter "this" of type
 "invariant(C)", which means that the function can never be called,
 unless by an invariant instance of C (since nothing implicitly casts
 to invariant). Have I got that right?

Yes.
Nov 27 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 Ah, but the natural interpretation of const(...) is that /everything/
 inside the brackets is const. Thus, if I write
 
     const(void f(char[] b))
 
 that "suggests" to me that b is const, since it is inside (albeit
 deeply inside) the const(...) brackets.

That's only if one thinks that const, when applied to a function, also influences its return type. There is no reason why transitive const should apply in this way.
 But I only want f's "this" to
 be const, not b. Perhaps this is a case where "const at the end"
 creates a more helpful mneumonic?

It leads to awful looking code when you have a lot of them to do.
Nov 27 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 11/27/07, Walter Bright <newshound1 digitalmars.com> wrote:
 That's only if one thinks that const, when applied to a function, also
 influences its return type. There is no reason why transitive const
 should apply in this way.

I do understand. But I'm thinking of the poor programmer who hasn't unravelled all the knots yet, and wants a member function to return a const ubyte[]. Such a programmer could easily accidentally end up writing const ubyte[] f() {/*...*/} thinking (not unreasonably) that this will return a const ubyte[] ... and being completely wrong.

Since a const array cannot be implicitly cast to a non-const, he'll get a compilation error. It's the big feature of const-correctness, the code won't compile if you get it wrong.
 Perhaps this is a case where "const at the end"
 creates a more helpful mneumonic?


If you're making const-at-the-end syntactically valid, then it's fair game to use it. If you don't want it used, don't make it legal. But if you /do/ make it legal, the (apparent) disambiguation it gives is, I feel, helpful. Other possible syntaxes have been suggested. Here's another one to consider: ReturnType const(functionName)(parameters...)

Sorry, but that's awful looking (besides looking confusingly like a template).
Nov 27 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 11/27/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Since a const array cannot be implicitly cast to a non-const, he'll get
 a compilation error.

Not necessarily. They might be relying on implicit cast to const in the first place. e.g. class A { const ubyte[] f() { ubyte[] b; /* initialise b */ return b; } } Here the programmer (who hasn't yet learned that "const ubyte[] f()" means "ubyte[] f() const" rather than "const(ubyte[]) f()") is assuming that when b is returned, it will be implicitly cast from ubyte[] to const ubyte[]. That assumption is wrong, but I believe the code still compiles.

Sure, but the code is also not broken.
 It's the big feature of const-correctness, the code
 won't compile if you get it wrong.

Of course, and that's why I love it. But not all incorrect code involving const is necessarily const-incorrect. :-)
 Other possible syntaxes have been suggested. Here's another one to consider:
     ReturnType const(functionName)(parameters...)


I completely agree. Sorry to have mentioned it.

Nov 27 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On Nov 27, 2007 9:13 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 Think of it this way:

         const A f()

 means:

         const (A f())

 which applies the const to the function f, not A.

Ah, but the natural interpretation of const(...) is that /everything/ inside the brackets is const. Thus, if I write const(void f(char[] b)) that "suggests" to me that b is const, since it is inside (albeit deeply inside) the const(...) brackets. But I only want f's "this" to be const, not b. Perhaps this is a case where "const at the end" creates a more helpful mneumonic?

What if it were const() A f() {...} or const(this) A f() {...} ? --bb
Nov 27 2007
parent reply David Gileadi <foo bar.com> writes:
Bill Baxter wrote:
 Janice Caron wrote:
 On Nov 27, 2007 9:13 AM, Walter Bright <newshound1 digitalmars.com> 
 wrote:
 Think of it this way:

         const A f()

 means:

         const (A f())

 which applies the const to the function f, not A.

Ah, but the natural interpretation of const(...) is that /everything/ inside the brackets is const. Thus, if I write const(void f(char[] b)) that "suggests" to me that b is const, since it is inside (albeit deeply inside) the const(...) brackets. But I only want f's "this" to be const, not b. Perhaps this is a case where "const at the end" creates a more helpful mneumonic?

What if it were const() A f() {...} or const(this) A f() {...} ? --bb

While maybe a bit of typing, I really like the const(this) form--its intent is very clear IMHO. And const(this) { int foo(); int bar(); } isn't too bad either.
Nov 27 2007
next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Janice Caron wrote:
 On 11/27/07, David Gileadi <foo bar.com> wrote:
 While maybe a bit of typing, I really like the const(this) form--its
 intent is very clear IMHO.  And

 const(this)
 {
      int foo();
      int bar();
 }

 isn't too bad either.

For what it's worth, I completely agree with this. I think it's a fine syntax, and makes the intent very clear. It can go at the front of a function without any confusion, it's obvious what it means, and the extention to invariant(this) is equally as obvious. In the extreme unlikelihood that I have a vote, this one gets mine.

Nov 28 2007
next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 A thought occurs to me. (And it's a good one). 

I think so, too. It'd help whenever I have a hairy method with lots of locals that I was having trouble keeping track of. There's a small problem of scope, but that's manageable: // var1 should be const starting here... const(var1) { ... // var2 should be const starting here... const(var2) { ... // make var1 non-const now } } const(var2) { ... // mave var2 non-const now } Of course, maintaining a large method with several of these const windows might be a bit troublesome. But it replaces some hard-to-find bugs with some easy-to-find ones.
Nov 28 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Christopher Wright wrote:
 Janice Caron wrote:
 A thought occurs to me. (And it's a good one). 

I think so, too. It'd help whenever I have a hairy method with lots of locals that I was having trouble keeping track of. There's a small problem of scope, but that's manageable: // var1 should be const starting here... const(var1) { ... // var2 should be const starting here... const(var2) { ... // make var1 non-const now } } const(var2) { ... // mave var2 non-const now } Of course, maintaining a large method with several of these const windows might be a bit troublesome. But it replaces some hard-to-find bugs with some easy-to-find ones.

I suspect you'd also be able to do something along the lines of: { const(var1): const(var2): ... }// end var1,var2 constness Anyway, the same kind of scoping mess exists now with 'with'. I've long wanted a 'with(var1):' form of it. --bb
Nov 28 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On Nov 28, 2007 11:45 AM, Don Clugston <dac nospam.com.au> wrote:
 
 A thought occurs to me. (And it's a good one). Since we're considering
 making const(this) an attribute, why not extend it to
 const(identifier), for any identifier. That is, const(identifier)
 would syntactically be considered an attribute, and could go anywhere
 version(identifier) could go. It's meaning would be: within the
 specified scope, the named identifier shall be consisdered const (and
 likewise for invariant). Allowing "this" to be merely a special case
 of identifier, does indeed allow us to declare const member functions:
 
     const(this) void f();
 
 as well as the abovementioned grouping.
 
     const(this)
     {
          int foo();
          int bar();
     }
 
 but, just look what /else/ you could do with it!
 
     const(x) /* let x be const within these braces */
     {
         /* x is const here */
     }

I guess in C++ you'd do that by making a const reference to x: { const int& const_x = x; /* use const_x insted of x here } But I can't say that I've wanted to do that very often in C++. Of course the renaming and introduction of indirection that the compiler may not optimize away is a bit more cumbersome than what you propose. Still, even if it wouldn't be particularly useful, the generality is elegant. :-)
 or...
 
     int x = 42;
     const(x): /* x is const from here on */
     x = 100; /* error */
 
 Why, it's almost like having final back! (Only, with crystal clear
 syntax and without the confusion). Other advantages include

I don't see how it replaces final (aka head-const). It just allows you to move the "const" around and scope it. int[] x = [42]; const(x): /* x is const from here on */ x[0] = 100; /* error - x is const, not head-const */
 
     const(outer)
 
 allows you to check const correctness via outer instead of via this.
 That is, "I promise not to modify any member variables of an outer
 class". And so on.

Now that does sound useful. --bb
Nov 28 2007
prev sibling parent Jason House <jason.james.house gmail.com> writes:
Janice Caron wrote:

 On 11/27/07, David Gileadi <foo bar.com> wrote:
 While maybe a bit of typing, I really like the const(this) form--its
 intent is very clear IMHO.  And

 const(this)
 {
      int foo();
      int bar();
 }

 isn't too bad either.

For what it's worth, I completely agree with this. I think it's a fine syntax, and makes the intent very clear. It can go at the front of a function without any confusion, it's obvious what it means, and the extention to invariant(this) is equally as obvious. In the extreme unlikelihood that I have a vote, this one gets mine.

I like it too. It's extremely clear and never ambiguous.
Nov 29 2007
prev sibling parent Brad Roberts <braddr puremagic.com> writes:
On Tue, 27 Nov 2007, Walter Bright wrote:

 Janice Caron wrote:
 On 11/27/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Since a const array cannot be implicitly cast to a non-const, he'll get
 a compilation error.

Not necessarily. They might be relying on implicit cast to const in the first place. e.g. class A { const ubyte[] f() { ubyte[] b; /* initialise b */ return b; } } Here the programmer (who hasn't yet learned that "const ubyte[] f()" means "ubyte[] f() const" rather than "const(ubyte[]) f()") is assuming that when b is returned, it will be implicitly cast from ubyte[] to const ubyte[]. That assumption is wrong, but I believe the code still compiles.

Sure, but the code is also not broken.
 It's the big feature of const-correctness, the code
 won't compile if you get it wrong.

Of course, and that's why I love it. But not all incorrect code involving const is necessarily const-incorrect. :-)


Also, don't forget the array vs slice changes that have been proposed in the past (page 26 of the conference presentation). That set of changes will help with this class of problem in a way that const can't. Later, Brad
Nov 27 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/26/07, Walter Bright <newshound1 digitalmars.com> wrote:
 At the beginning will still work for function types. The at the end
 option is there for those who are building complex type declarations.

and
 No. "const char[] X;" and "const(char[]) X;" mean the same thing.

It seems to me that the following is still ambiguous: class A { int n; } class B { const A f() { /*...*/ } } Does it mean (a) class B { const(A) f() { /*...*/ } } or does it mean (b) class B { A f() const { /*...*/ } } ? Also, is it still possible to write class C { int n; void f() invariant { /*...*/ }; } (I put the keyword at the end to avoid confusion. In D2.007, you'd write "invariant" before "void"). My interpretation of the above code is that f is being called with a hidden parameter "this" of type "invariant(C)", which means that the function can never be called, unless by an invariant instance of C (since nothing implicitly casts to invariant). Have I got that right?
Nov 26 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/27/07, Derek Parnell <derek nomail.afraid.org> wrote:
 On Mon, 26 Nov 2007 21:24:13 -0800, Walter Bright wrote:

 I'm not sure why one would need protection against changing the bitmap
 pointer. GammaAdjust should just take a byte[], not a ref byte[].

byte[] bitmap; bitmap = LoadBitMapFromFile("worldroadmap.bmp"); GammaAdjust(bitmap, 0.20); Render(device, bitmap); If the 'GammaAdjust' routine changed the pointer then 'Render' routine would not display the adjusted bitmap.

The GammaAdjust routine /cannot/ change the pointer, because it is passed in by value, not by reference. I guess it could change its own local copy of the pointer, but that would just be a bug local to the function.
Nov 26 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 27, 2007 9:13 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 Think of it this way:

         const A f()

 means:

         const (A f())

 which applies the const to the function f, not A.

Ah, but the natural interpretation of const(...) is that /everything/ inside the brackets is const. Thus, if I write const(void f(char[] b)) that "suggests" to me that b is const, since it is inside (albeit deeply inside) the const(...) brackets. But I only want f's "this" to be const, not b. Perhaps this is a case where "const at the end" creates a more helpful mneumonic?
Nov 27 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/27/07, Walter Bright <newshound1 digitalmars.com> wrote:
 That's only if one thinks that const, when applied to a function, also
 influences its return type. There is no reason why transitive const
 should apply in this way.

I do understand. But I'm thinking of the poor programmer who hasn't unravelled all the knots yet, and wants a member function to return a const ubyte[]. Such a programmer could easily accidentally end up writing const ubyte[] f() {/*...*/} thinking (not unreasonably) that this will return a const ubyte[] ... and being completely wrong.
 Perhaps this is a case where "const at the end"
 creates a more helpful mneumonic?

It leads to awful looking code when you have a lot of them to do.

If you're making const-at-the-end syntactically valid, then it's fair game to use it. If you don't want it used, don't make it legal. But if you /do/ make it legal, the (apparent) disambiguation it gives is, I feel, helpful. Other possible syntaxes have been suggested. Here's another one to consider: ReturnType const(functionName)(parameters...)
Nov 27 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/27/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Since a const array cannot be implicitly cast to a non-const, he'll get
 a compilation error.

Not necessarily. They might be relying on implicit cast to const in the first place. e.g. class A { const ubyte[] f() { ubyte[] b; /* initialise b */ return b; } } Here the programmer (who hasn't yet learned that "const ubyte[] f()" means "ubyte[] f() const" rather than "const(ubyte[]) f()") is assuming that when b is returned, it will be implicitly cast from ubyte[] to const ubyte[]. That assumption is wrong, but I believe the code still compiles.
 It's the big feature of const-correctness, the code
 won't compile if you get it wrong.

Of course, and that's why I love it. But not all incorrect code involving const is necessarily const-incorrect. :-)
 Other possible syntaxes have been suggested. Here's another one to consider:
     ReturnType const(functionName)(parameters...)

Sorry, but that's awful looking

I completely agree. Sorry to have mentioned it.
Nov 27 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/27/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 I agree.  I don't get why we need two ways to express it.  What's the
 benefit of writing const out in front?

1. It's semantically consistent. 2. It enables you to 'group' const member functions: const { int foo(); int bar(); }

Oh, well if you put it like that, I have to agree with you! :-) The question then becomes, why do we need const at the back?
Nov 27 2007
prev sibling next sibling parent "Chris Miller" <chris dprogramming.com> writes:
On Tue, 27 Nov 2007 13:59:47 -0500, Walter Bright  
<newshound1 digitalmars.com> wrote:

 2. It enables you to 'group' const member functions:

 const
 {
      int foo();
      int bar();
 }

 this cleans up things nicely when you have a number of member functions.  
 Contrast this with how the same code looks in C++.

The above could be the same as: int foo() const; int bar() const; and this apply to the return type: const int foo(); const int bar(); When grouping with : or { } it applies to the actual named declarations it encloses; but if const is applied before the return type, precedence (the juxtaposition) dictates association with the return type. This is what I would assume.
Nov 27 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/27/07, David Gileadi <foo bar.com> wrote:
 While maybe a bit of typing, I really like the const(this) form--its
 intent is very clear IMHO.  And

 const(this)
 {
      int foo();
      int bar();
 }

 isn't too bad either.

For what it's worth, I completely agree with this. I think it's a fine syntax, and makes the intent very clear. It can go at the front of a function without any confusion, it's obvious what it means, and the extention to invariant(this) is equally as obvious. In the extreme unlikelihood that I have a vote, this one gets mine.
Nov 27 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/27/07, Chris Miller <chris dprogramming.com> wrote:
 but if const is applied before the return type, precedence (the
 juxtaposition) dictates association with the return type.

 This is what I would assume.

It's what I would assume too. That's what the "principle of least surprise" would lead one to expect. But we'd both be wrong.
Nov 27 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On Nov 27, 2007 9:42 PM, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 The real annoyance with C++ const is the redundant methods in both const
 and non-const flavors.

Make that THREE flavours in D byte[] f(byte[] b) {/*...*/} const(byte)[] f(const(byte)[] b) {/*...*/} invariant(byte)[] f(invariant(byte)[] b) {/*...*/}
  That's the thing to target for elimination.

Absolutely agreed! My favourite proposed syntax for that is constType(byte)[] f(constType(byte)[] b) {/*...*/} "constType" is a new keyword. The idea is that the compiler generates at most three versions of the function, with "constType" replaced by "const", "invariant" or nothing at all in each case. The symbol "constType" would be available inside the body of the function too. If any of the three flavours don't compile, then it is not an error - instead, they are simply not instantiated. You don't have to invent a new keyword, of course. Any symbol will do instead of "constType", as long as it's clear what's going on, and not ambiguous - even "return". :-)
Nov 27 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Janice Caron" <caron800 googlemail.com> wrote in message 
news:mailman.147.1196235159.2338.digitalmars-d puremagic.com...
 On Nov 27, 2007 9:42 PM, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 The real annoyance with C++ const is the redundant methods in both const
 and non-const flavors.

Make that THREE flavours in D byte[] f(byte[] b) {/*...*/} const(byte)[] f(const(byte)[] b) {/*...*/} invariant(byte)[] f(invariant(byte)[] b) {/*...*/}
  That's the thing to target for elimination.

Absolutely agreed! My favourite proposed syntax for that is constType(byte)[] f(constType(byte)[] b) {/*...*/} "constType" is a new keyword. The idea is that the compiler generates at most three versions of the function, with "constType" replaced by "const", "invariant" or nothing at all in each case. The symbol "constType" would be available inside the body of the function too. If any of the three flavours don't compile, then it is not an error - instead, they are simply not instantiated. You don't have to invent a new keyword, of course. Any symbol will do instead of "constType", as long as it's clear what's going on, and not ambiguous - even "return". :-)

Read the conference slides? In fact you even guessed the keyword -- "return". I think it's something like: byte[] f(return byte[] b) { /* ... */ } The return type is then dependent upon the constness of the input. It's _kind of_ like a templated function, but unlike a templated function it can be virtual.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 28, 2007 4:29 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 Daniel Keep wrote:
 The way I've understood it is that const is both a type constructor
 *and* a storage class.

That's right. It's a floor wax *and* a dessert topping!
 When invoked with parenthesis, it's a type
 constructor.  When invoked without, it's a storage class that happens to
 apply the type constructor to whatever the type of the declaration is.

That's right, too.

I think we do all understand this. The problem is that it took us (...well, me at least...) a long time before I /did/ grok it. I had to ask questions on this newsgroup, and have it all explained to me. This overuse of the word "const" is, in fact, one of the things which caused complaints that the D2.007 const is "a mess". Yes - the head const/tail const distinction caused confusion, but in my book, the double-use of the the words "const" and "invariant" in situations where it's not always obvious which meaning is intended, was equally part of the confusion, and therefore falls into the category of that which you have to sort out. Similar problems would arise if you made "static" or "private" into a type constructor. I've no idea what such things might mean, but just hypothetically, if one could define a type static(int) or private(char)[], then /exactly the same/ confusion would arise. It is coincidence that "const int x;" means the same thing as "const(int) x;" It is not so helpful that the analogy does not extend to "const int f()" meaning the same things as "const(int) f()". I believe it is a mistake for the same keyword, "const", to double up with both meanings. (And the same logic applies to "invariant"). Since there is a clear need for the "const" type-constructor, we should keep that. But there is less of a need for "const" as an attribute. Perhaps we don't need it at all there. As has been suggested previously, attributes of "const(this)" and "invariant(this)" can be used to define const/invariant functions. "static" is largely taking over from "const" in meaning "available at compile time", and soon we'll have "macro" to fill out the remaining duties. If we ditch const-as-an-attribute altogether, it would require only minor changes to get code compiling again: e.g. const int[] table = [ 1, 2, 3, 4 ]; becomes const(int[]) table = [ 1, 2, 3, 4 ]; or perhaps even static int[] table = [ 1, 2, 3, 4 ]; All confusion is immediately eliminated, and everyone's happy. Even the compiler! After all, it will still have a context-free grammar and be fast to compile. It's a big win-win for everyone. The only downside I can see is that it would break code, but since it would break it in an easily fixable way, I don't see that as a big deal.
Nov 27 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 28, 2007 10:10 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 I think it would be pretty hard to give up:

       const x = 3;

Why? It would just turn into something else, like const(int) x = 3; static x = 3; macro x = 3; ...or something. I mean, you get to invent the syntax.
 Also, C++ has const as both a storage class and a type constructor, and
 yes, they have subtly different meanings. This doesn't seem to cause any
 major problems.

That's because C++ behaves as expected. If I declare const int * f(); then I get a function which returns a pointer to const int, /not/ a const member function which returns a pointer to mutable int. I other words, in C++, the const adheres to the return type, not the function's "this" pointer. The D behaviour is drastically different and vastly unintuitive. I don't know whether this is syntactically considered to be a storage class or a type constructor in C++, but really, it's irrelevant to me. What matters is the principle of least surprise. It should do what we expect it to do, and not something else.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 28, 2007 10:10 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 I think it would be pretty hard to give up:

         const x = 3;

 Also, C++ has const as both a storage class and a type constructor, and
 yes, they have subtly different meanings. This doesn't seem to cause any
 major problems.

Actually, I may have been misunderstood, so I want to be clear. There is a difference between an ATTRIBUTE and a STORAGE CLASS. C++ has storage classes, but D does not. D has attributes, but C++. They are not the same thing. The terms are not interchangeable. For example, in C++, "typedef" is (syntactically) a storage class, but it's certainly not an attribute in D. If you want to make the statement const x = 3; legal in D, it is perfectly possible to do that without making "const" an attribute. You only have to define a "const statement" or something in the grammar. Call that a "storage class" if you want, but you don't have to grant the keyword all the power of an attribute. To keep this conversation plain, let us not confuse the terms "attribute" and "storage class" again.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 28, 2007 11:45 AM, Don Clugston <dac nospam.com.au> wrote:
 Janice Caron wrote:
 On 11/27/07, David Gileadi <foo bar.com> wrote:
 While maybe a bit of typing, I really like the const(this) form--its
 intent is very clear IMHO.  And

 const(this)
 {
      int foo();
      int bar();
 }

 isn't too bad either.

For what it's worth, I completely agree with this.


A thought occurs to me. (And it's a good one). Since we're considering making const(this) an attribute, why not extend it to const(identifier), for any identifier. That is, const(identifier) would syntactically be considered an attribute, and could go anywhere version(identifier) could go. It's meaning would be: within the specified scope, the named identifier shall be consisdered const (and likewise for invariant). Allowing "this" to be merely a special case of identifier, does indeed allow us to declare const member functions: const(this) void f(); as well as the abovementioned grouping. const(this) { int foo(); int bar(); } but, just look what /else/ you could do with it! const(x) /* let x be const within these braces */ { /* x is const here */ } or... int x = 42; const(x): /* x is const from here on */ x = 100; /* error */ Why, it's almost like having final back! (Only, with crystal clear syntax and without the confusion). Other advantages include const(outer) allows you to check const correctness via outer instead of via this. That is, "I promise not to modify any member variables of an outer class". And so on. Going back to Walter's point that C++ allows const as a storage class just fine, I want to really stress that that claim is a red herring. When you declare a member function as const in C++, you are /not/ using "const the storage class". In fact, in C++, "const the storage class" is really the same thing as D's invariant type constructor - it is an assertion that the variable will never be changed, ever. It certainly doesn't mean "don't modify 'this'". That's why I think it's important to separate in our minds the distinction between "storage class" and "attribute". There are places in D where the existing const syntax is just fine - e.g. Walter's example const x = 5; However, just because we want that to be a legal statement, it does not follow that we need to give the keyword const (without brackets) all the syntactic abilities of an attribute. On the other hand, const(identifier) makes absolutely /perfect/ sense as an attribute. It's beautiful, and would give us expressive power that even C++ doesn't have, while at the same time allowing everything that Walter wants to keep with only minor syntactic changes (e.g. "const(this)" instead of "const" for member functions, const-statements in the grammar instead of "const" as an attribute). This is a testing time for const. We're already doing better than C++ in many respects, but the final deal still isn't nailed down yet, so while there's still time, let's consider this one. There are people who haven't upgraded to D2 yet - they obviously won't mind the change; there are those of us who like to "live on the edge" - well, we won't mind change either, because we understand that's the price for living on the edge. So the proposal is: (1) disallow "const" as an attribute (2) introduce "const statements" so that "const x = 5" still compiles. (3) allow "const(identifer)" to be an attribute, which makes identifier const within the scope of the attribute and likewise for invariant
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
 (2) introduce "const statements" so that "const x = 5" still compiles.

I guess that should really be "const declarations", not "const statements". But you get the drift.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/28/07, Sean Kelly <sean f4.ca> wrote:
 Um, "const x = 3" *is* legal in D.  Has been for about a year now.

 Sean

No need for the "Um". Of course I know that, and certainly I should have said, "if you want to /keep/ [it] legal", rather than "if you want to /make/ [it] legal". My apologies for the typographical faux pas, however it is entirely unimportant in the context of this discussion.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/28/07, Sean Kelly <sean f4.ca> wrote:
 You claimed that D does not
 have storage classes.  Could you explain this?

Certainly. In the grammar of C and C++, a rule is defined called "storage-class-specifier". The "storage-class-specifier" grammar rule forms part of the rule defining the syntax of a declaration. There is no corresponding grammar element in D. The storage class specifiers in C and C++ are auto, extern, mutable, register, static and typedef. See the grammar at http://www.kuzbass.ru:8086/docs/isocpp/gram.html The closest grammar element D has is "attribute". This is defined on the web page. http://www.digitalmars.com/d/attribute.html. D attributes are deprecated, static, final, override, abstract, const, auto, scope, and various other constructs as defined on that page. Though there is overlap between C's storage classes and D's attributes, they are not completely identical, and in consequence, some analogies don't work.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Because those alternatives all look terrible. And frankly, how could
 anyone be confused about what:
         const x = 3;
 means? I can't understand throwing that out to improve clarity?

You have misunderstood me. The suggestion is that "const" shall not be an attribute. This does not rule out allowing "const x = 3;" being a legal statement, it only means you have to change the grammar of a declaration in order to allow it.
 Let me put it another way,
 can you explain the difference between C++ const storage class and C++
 const type constructors? There's a big semantic difference.

I just checked the grammar of C++, and C++'s list of storage-class-specifiers doesn't actually include "const", so I think neither of us is using the term correctly. However, in C++, if a statement such as "const int x = 3;" occurs at global scope, then x is invariant. We can call that a storage class if you want. The similar looking, but drastically different statement "const int * p = whatever;" uses const as a type constructor, equivalent to D's const(int). In C++, this can also be written as "int const * p" (a style I prefer as it now reads from right to left as "p is a pointer to const int"). As far as member functions go, the C++ const storage class cannot be applied to them because it makes no sense (because a member function cannot be declared at global scope. By definition, it is always in the scope of the enclosing class). Thus, when I write (in C++) class A { int f() const; } The word "const" is /not/ acting as a "storage class". Instead, it tells us that "this" is const within f.
 D's const storage class applies to the declaration. That makes intuitive
 sense, although it is different from C++.

But what makes no sense is the very notion of a function (as opposed to its "this" pointer) being const. All functions are, by definition, invariant. That is, the bytes of machine code which are executed, may not be changed by any part of the program. If you take the address of those bytes of machine code, that's a pointer to invariant. The RAM utilitised during that function's execution (the stack, or the member variables, or both) are part of that function's context, and we care whether or not the function is allowed to modify those variables in the course of its execution. Thus, the kind of constness we are talking about is the constness of the function's context, not of the function itself. You can call that a "storage class" if you want, but it means something very different from a variable declared at global scope. So what you're saying as that the meaning of the word "const" changes from place to place, depending on what it applies to. For instance class A { const { int x; int f(); } } This /in fact/ means that the class A has a member variable called x, which is a const(int), and a function f, which does not modify anything through this. Those are two totally different meanings of "const"
 Every other storage class applies to the declaration, why should const
 be completely different?

Functions can't have storage classes. The notion of a function being anything other than invariant ... well, it just doesn't happen in today's world (though I do remember the old days of self-modifying code). The storage class of a function's /context/ - that's a different thing, and needs a different syntax. ...and that's all I'm suggesting really. Let the syntax be const(this), instead of merely const. (I can't claim credit for that idea. Someone else came up with that one. I just like it lots). That makes it clear that we're talking about the storage class of the function's context, and explicitly names that context as the function's "this". My objection to the existing syntax is more or less solely that const int f(); does not declare that f returns a const int, and I think that all newcomers to D will find that /hugely/ confusing.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/28/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Acutally, when comparing side by side with "static", that starts to
 sound reasonable.   Both are mucking with func's implicit 'this'
 paramter (static eliminates it, const makes it const).

Put like that, it does make sense. But then, at global scope, a static function is one which does compile-time function execution. Both const and static change their meaning when applied to functions inside a class. I can live with the existing const-at-the-front syntax. It's not what I'd prefer, but I can live with it. But if we do keep it, please let's ditch const-at-the-back. Two different syntaxes is even more confusing than what we have now. I mean, I can't write int f() static; now, can I?
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/28/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Janice, what are your thoughts about how the 'static' attribute fits
 into that.  I was thinking largely the way you were, but 'static' seems
 to be another example of a "storage class" that doesn't change anything
 about the function per-se, but rather it's parameters.  Just like the
 const "storage class", it mucks with the hidden "this" parameter.

 If you're going to allow "static" to modify the 'this' parameter by
 sticking it out in front of a function, then it seems you should allow
 const the same courtesy.

I have to agree with you. But as you mentioned earlier, "static" can't be a type constructor, so it never leads to (apparent) ambiguity. It's that (apparent) ambiguity that makes me feel uneasy. If the precence rules were changed such that in a declaration statement like const int f(); the const bound to the int, rather than to the f, then I don't see how it would break anything else. The only thing is that you'd then need a different way to say that "this" is const within f. Whether that's const-at-the-back, like in C++, or const(this), or whatever, it would be necessary, because you have to have /some/ way of saying that. Consider: const int delegate(int) dg; What is const here? Under Walter's rules, dg, which is a delegate (actually a pointer to those machine code bytes, and a pointer to some function's context) is what we're claiming is const, so dg could never be assigned, except in the declaration itself. How do we declare a delegate to a const (that is, whose this is const) function? I don't know. Can it even be done?
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 what makes more logical, intuitive sense?

 1) p is a non-constant pointer to const int?
 2) p is a const pointer to a non-const int?
 3) p is a const pointer to a const int?

 I doubt you'll get a consistent answer from programmers who aren't
 already very used to the wacky C++ semantics.

You are of course completely correct about that, Walter. However, nobody here is claiming that C++ got it right. Rather, we're saying that D /should/ get it right. Just because C++ is confusing, doesn't mean D has to be confusing too.
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 So I infer you agree that C++ is not consistent or intuitive <g>.

Of course I agree with that. How could I not!? :-)
 I don't think it adds anything but extra typing.

It adds the ability to parse the declaration differently, so that const binds to the type, not the identifier, so while const(this) int[] f(); is extra typing compared with the existing syntax, it would free up the existing syntax to mean const(int[]) f()
 Const is different in D than in C++, and it will take some getting used
 to for refugees from C++. Anyone who expects it to behave exactly like
 C++ will have difficulty. But if they spend a little time looking at it
 and give it a chance, I think they'll find that it is more consistent,
 intuitive, and usable than C++ const.

So, how do I declare dg to be a delegate which can call a const member function? Would it be (const int delegate()) dg; ?
Nov 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 29, 2007 8:50 AM, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 What would be the point of an invariant method? A const method promises not
 to change any contents of the object. An invariant method makes the same
 promise.
 So there is no difference to the caller of such a method.
 I can't see that it make any difference inside the method either.

It makes a difference. Watch... class A { int x; const int f() { return x; } invariant int g() { return x; } } A ma = A(3); writefln(ma.f()); /* error */ writefln(ma.g()); /* error */ const A ca = A(3); writefln(ca.f()); /* OK */ wriitefln(ca.g()); /* error */ invariant A ia = A(3); writefln(ca.f()); /* OK */ writefln(ia.g()); /* OK */
Nov 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Hope yall were paying attention and spotted that I screwed that up
completely. It should of course have been:

   class A
   {
       this(int n) { x = n; }
       int x;
       const int f() { return x; }
       invariant int g() { return x; }
   }

   A ma = new A(3);
   writefln(ma.f()); /* OK */
   writefln(ma.g()); /* error */

   const A ca = new A(3);
   writefln(ca.f()); /* OK */
   wriitefln(ca.g()); /* error */

   invariant A iaTemp = cast(invariant(A)) new A(3);
   writefln(ia.f()); /* OK */
   writefln(ia.g()); /* OK */

Anyway, here's the deal. When you declare a function as const, it
means that the variable "this" is const during the function's
execution. Likewise, when you declare a function as invariant, it
means that the variable "this" in invariant during the function's
execution.

In both cases, the callee code will fail to compile if the function
modifies any member variable.

The difference between the two is visible at the caller site, however.
A pointer can always be cast to const, so calling a function declared
as const is always OK. However, only no pointer can ever implicitly
cast to an invariant pointer (unless it was invariant to begin with),
so code at the caller site which tries to call an invariant function
will not compile, unless the class instance in invariant.

Of course, getting an invariant instance in the first place requires a cast!
Nov 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Yes, Oskar is right in his analysis of const vs invariant
declarations. And now that I think about it, it occurs to me that
NOTHING IS EVER CREATED CONST. Everything that is created in D, is
either created mutable, or is created invariant. All literals are
invariant. Everything created using new is mutable. .dup and .idup
generate mutable and invariant respectively, but there is no .cdup.

And this makes perfect sense. After all, if const means "a readonly
view of something", then there must be a "something" for it to be a
readonly view /of/.

Thus, as Oskar pointed out, a statement like

    const x = 3;

is actually pointless. Either you want x to be mutable, or you want x
to be constant. In the former case, "auto x = 3" would do the trick;
in the latter case, "invariant x = 3" should work. But "const x = 3"
is redundant, because nothing can modify x /anyway/. Casting away
const is undefined, so we can't do that, and since we can't do that,
nothing can change it - ergo, it is invariant. Thus, we should really
be writing

    invariant x = 3;

For this reason, it seems to me that "const as a storage class" makes
absolutely no sense whatsoever. Const things are not stored, /ever/.
Invariant things are stored (they occupy bytes of ROM or RAM
somewhere); mutable things are stored (they occupy bytes of RAM
somewhere), but there is really no such animal as a const "thing" -
there is only "pointer to const", or "array of const", or "custom
collection of const" ... and all of those things are examples of
const-as-a-type-constructor, not const-as-a-storage-class.

Ah, but what about functions?, I here you ask. Well, in the case of
class member functions, it's the other way round: we need const, but
not invariant. Further back up this thread, Bruce Adams pointed out
that it makes very little sense to define a class member function as
being invariant. I did respond to that pointing out that it had a
/literal/ meaning in the context of the D language, but in reality,
it's a nonsensical fiction. To create an invariant class instance,
you'd have to do:

    A a = cast(invariant(A))(new A);

and unless you do that, then you can never call an invariant member
function. What this means is that Bruce is basically right - defining
a member function to be invariant is as pointless as defining a const
literal. It seems to me that in both cases /one keyword will suffice/.
So why not let "const" do double-duty?

That is, we keep
(*) const as a storage-class
(*) const as a type-constructor
(*) invariant as a type-constructor

and we lose
(*) invariant as a storage class

with the caveat that when const-as-storage-class is applied to types
which require storage, it actually means invariant (but when applied
to member functions it retains its existing meaning). If we were to
adopt this practice then:

     const x = 3;
     auto p = &x;

would result in x having type invariant(int), and p having type
invariant(int)*. Since invariant can implicitly cast to const, this is
guaranteed not to break anything.

It would also mean that

    invariant x = 3;

would become a compile error, since invariant would no longer be an
attribute. This seems to me to be a slightly more preferable idea than
Oskar's one of redefining "protected", since, under my scheme, "const"
would be the keyword you'd use in practice, most of the time. You'd
use it when declaring variables, when declaring member functions
const, and when promising not to modify stuff. Only rarely would you
need to type the more lengthy "invariant", but it would still be there
(as a type-modifier only), should you need it.
Nov 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 29, 2007 3:17 PM, Regan Heath <regan netmail.co.nz> wrote:
 I think I'd rather loose:
 (*) const as a storage-class

But if you did that, you wouldn't be able to declare const member functions. You'd have to come up with a new syntax for that, or use "invariant" in place of const (... which, come to think of it is not such a bad idea)
Nov 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 30, 2007 12:16 AM, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 By the way to the const(this) advocates. Though the idea looks nice on the
 surface
 it isn't quite consistent. const works on type (declarations), this is a
 variable not
 a type.

Of course it's not consistent with existing syntax, otherwise it would already compile. const(this) is proposed /new/ syntax. In fact, I proposed extending the idea to const(lvalue), where lvalue is any lvalue known at compile time. This would allow you to do: class Outer { int outerX; class Inner { int innerX; const(outer) f(/*...*/) } } Here f promises not to modify anything accessed through outer, so any attempt to modify outerX (for example) would be picked up at compile time. Personally, I really like the idea. const(this) becomes a special case of that, and replaces const-for-member-functions very nicely.
Nov 30 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 29, 2007 8:25 PM, Walter Bright <newshound1 digitalmars.com> wrote:
 const as storage class: Const local variables can be initialized with
 const types, so there is definitely a place for them:

 void foo(const int *p)
 {
      const q = p;
 }

 q does *not* point to an invariant.

Actually is does. If you followed all the details of my previous post, you should recall that I said: "with the caveat that when const-as-storage-class is applied to types which require storage, it actually means invariant". Thus, with that caveat borne in mind, the above code actually says: void foo(invarant int * p) { invariant q = p; } I think you applied my caveat selectively! :-) However, I grant you that that is confusing, and I would be opposed to anything which is confusing, so clearly it won't fly. Unless ... we add one further rule... (*) disallow altogether both "const" and "invariant" as function parameter storage classes. This would force the writer of the function to declare void foo(const(int)* p) or void foo(const(int *) p) (although the latter is pointless since we don't really care about head-constness), instead of void foo(const int * p) and either way, the body of the function could then become auto q = p; so we'd end up with void foo(const(int)* p) { auto q = p; } which seems eminently readable to me.
Nov 30 2007