www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Const sucks

reply Walter Bright <newshound1 digitalmars.com> writes:
Const, final, invariant, head const, tail const, it's grown into a 
monster. It tries to cover all the bases, but in doing so is simply not 
understandable.

Andrei and Bartosz have spent some time together going back to square 
one with what are we trying to achieve with const, and came up with 
another proposal.

What we are trying to achieve:

a) utility for functional programming
b) better modularity by self-documenting interfaces better
c) be able to treat reference types as if they were value types (i.e. 
strings should behave to the user like value types, even though they are 
references)

The insights they came up with were:

1) final (i.e. 'head const') is not necessary for a, b or c. final is a 
local thing, and not strictly necessary.

2) tail const can be handled in other ways, read on

So, what we are left with is:

o  no more final

o  const and invariant now mean "fully const" and "fully invariant", 
where fully means "head and tail combined":

const int x = 0;   // x is constant
const int* p = &x;  // neither p nor *p can be changed
const(int*) p = &x;  // neither p nor *p can be changed
const(int)* p = &x;  // p can change, *p cannot be changed

o  const and invariant are transitive. There is no way to specify a 
(pointer to)(const pointer to)(mutable int).

o  tail invariant for an array or pointer type can be done using the 
existing syntax:

  invariant(char)[] s;   // string, i.e. an array of invariant chars
  const(T)* p;  // pointer to tail const T

o  tail const of a struct would have to be done by making the struct a 
template:

   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const

o  one can construct a template to generically produce tail const or 
tail invariant versions of a type.

o  it will be illegal to attempt to change the key value of a foreach 
loop, but the compiler will not be able to diagnose all attempts to do so

o  static const/invariant means the initializer is evaluated at compile 
time. non-static const/invariant means it is evaluated at run time.

o  no initializer means it is assigned in the corresponding constructor.

o  const/invariant declarations will always allocate memory (so their 
addresses can be taken)

o  So, we still need a method to declare a constant that will not 
consume memory. We'll co-opt the future macro syntax for that:

     macro x = 3;
     macro s = "hello";
Sep 10 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Walter Bright wrote:
 o  tail const of a struct would have to be done by making the struct a 
 template:
 
   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const

How is this going to work for a more complex struct? struct Bar { int a; } class Baz { int a; } struct Foo { int a; float b; char[] c; Bar d; Baz e; }
 o  one can construct a template to generically produce tail const or 
 tail invariant versions of a type.

Including classes? I saw no mention of how tail-const will be applied to classes, will they be handled in the same way as structs? In general is this correct for a class reference: class Baz { int a; } const Baz b; //neither b nor b.a can be changed Regan
Sep 10 2007
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
Wow! Well, I'm happy! :-)

I think I'll wait for the documentation before I comment further,
except to mention again this suggestions which I've made elsewhere...

No casting away const except by special syntax.

const(int)*p;
auto q = (int *) p; // Error - p is const
Sep 10 2007
parent reply "Jb" <jb nowhere.com> writes:
"Janice Caron" <caron800 googlemail.com> wrote in message 
news:mailman.136.1189453181.16939.digitalmars-d puremagic.com...
 Wow! Well, I'm happy! :-)

 I think I'll wait for the documentation before I comment further,

Am I the only one who though it odd that the current documentation spends twice as much time talking about C++ than it does about D? It's almost feels like the coverage of final and invariant were last minute additions just before handing it in for marking.
Sep 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jb wrote:
 Am I the only one who though it odd that the current documentation spends 
 twice as much time talking about C++ than it does about D?

Half of D programmers come from C++, and the easiest way to learn something new is to relate it to something already known.
Sep 10 2007
parent reply "Jb" <jb nowhere.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:fc4mu5$2j7u$3 digitalmars.com...
 Jb wrote:
 Am I the only one who though it odd that the current documentation spends 
 twice as much time talking about C++ than it does about D?

Half of D programmers come from C++, and the easiest way to learn something new is to relate it to something already known.

Yeah thats fair enough up to a point, but imo the D section was sorely lacking in examples and detailed explanations. Hence all the "What does const xyz mean?" questions in the newgroup.
Sep 10 2007
parent "Jb" <jb nowhere.com> writes:
"Jb" <jb nowhere.com> wrote in message news:fc4stj$2snr$1 digitalmars.com...
 "Walter Bright" <newshound1 digitalmars.com> wrote in message 
 news:fc4mu5$2j7u$3 digitalmars.com...
 Jb wrote:
 Am I the only one who though it odd that the current documentation 
 spends twice as much time talking about C++ than it does about D?

Half of D programmers come from C++, and the easiest way to learn something new is to relate it to something already known.

Yeah thats fair enough up to a point, but imo the D section was sorely lacking in examples and detailed explanations. Hence all the "What does const xyz mean?" questions in the newgroup.

Sorry to clarify, i meant the the D section was too light, not the C++ section too heavy.
Sep 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/10/07, Jb <jb nowhere.com> wrote:
 Am I the only one who though it odd that the current documentation spends
 twice as much time talking about C++ than it does about D?

As of the start of this thread, the current documentation is out the window. :-)
Sep 10 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Regan Heath wrote:
 In general is this correct for a class reference:
 
 class Baz { int a; }
 const Baz b;  //neither b nor b.a can be changed

Yes.
Sep 10 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 
 So, what we are left with is:
 
 o  no more final

By this you meant that "final" in 2.0 would work exactly as in 1.0, correct?
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":
 
 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed

So the presence of parenthesis would be simply to limit the extent of the type to which "const" applies? As an aside, I'm still not terribly fond of 'invariant', but that's a cosmetic issue which could easily be resolved later.
 o  const and invariant are transitive. There is no way to specify a 
 (pointer to)(const pointer to)(mutable int).

Sounds great to me.
 o  tail invariant for an array or pointer type can be done using the 
 existing syntax:
 
  invariant(char)[] s;   // string, i.e. an array of invariant chars
  const(T)* p;  // pointer to tail const T
 
 o  tail const of a struct would have to be done by making the struct a 
 template:
 
   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const
 
 o  one can construct a template to generically produce tail const or 
 tail invariant versions of a type.

Could you please explain this further? Why would templates be needed in the above two points? For the rest, this design sounds a lot more comprehensible than the old one, and still seems to address all the situations where I would use const. Sean
Sep 10 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 So, what we are left with is:

 o  no more final

By this you meant that "final" in 2.0 would work exactly as in 1.0, correct?

Yes. There'll still be final classes and final virtual functions.
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":

 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed

So the presence of parenthesis would be simply to limit the extent of the type to which "const" applies?

Yes. Think of the () as presenting an 'argument' to be const-ified.
 o  tail const of a struct would have to be done by making the struct a 
 template:

   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const

 o  one can construct a template to generically produce tail const or 
 tail invariant versions of a type.

Could you please explain this further? Why would templates be needed in the above two points?

Think of a struct which was your own implementation of arrays: struct Array(T) { T[] a; } To create a tail const array, instead of const(T)[], we'd do Array!(const(T)).
Sep 10 2007
next sibling parent reply Alexander Panek <a.panek brainsware.org> writes:
Walter Bright wrote:
 To create a tail const array, instead of const(T)[], we'd do 
 Array!(const(T)).

What exactly is the is the difference between head and tail const?
Sep 10 2007
parent Bobba Fett <sucks notmymail.com> writes:
Janice Caron Wrote:

 On 9/10/07, Alexander Panek <a.panek brainsware.org> wrote:
 What exactly is the is the difference between head and tail const?

Suppose you have a pointer to pointer to pointer to pointer to pointer to int. (The length of this chain is irrelevant. I just threw lots in for the hell of it). Head const would be const pointer to pointer to pointer to pointer to pointer to int. Tail const would be pointer to const pointer to const pointer to const pointer to const pointer to const int.

would be pointer to pointer to pointer .... to const int. What you have described I would call "rest const" using naive intuition.
 Total const would be
 const pointer to const pointer to const pointer to const pointer to
 const pointer to const int.
 
 i.e. head const and tail const both at the same time.

Sep 10 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 
 o  tail const of a struct would have to be done by making the struct 
 a template:

   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const

 o  one can construct a template to generically produce tail const or 
 tail invariant versions of a type.

Could you please explain this further? Why would templates be needed in the above two points?

Think of a struct which was your own implementation of arrays: struct Array(T) { T[] a; } To create a tail const array, instead of const(T)[], we'd do Array!(const(T)).

Okay, so basically if I want a mutable struct that may reference constant data I'll have to pass the const attribute through via a template rather than preceding the struct decl with a "tail const" keyword. Is that right? Seems fair enough. C++ requires the same approach, as far as I know. Sean
Sep 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/10/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Think of a struct which was your own implementation of arrays:

 struct Array(T)
 {
      T[] a;
 }

 To create a tail const array, instead of const(T)[], we'd do
 Array!(const(T)).

While that does make sense, I guess what we really want to know is why we won't be able to do this: struct MyArray { int[] p; } and then make a tail const version with some magic keyword like tailconst(MyArray) That is, tailconst(T) could be some built-in keyword which created a new type which was just like T except that all its pointers would be const. Are you saying this is such a rare thing to want to do that it's not worth supporting generically? I can't say I've ever needed it up to now.
Sep 10 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/10/07, Alexander Panek <a.panek brainsware.org> wrote:
 What exactly is the is the difference between head and tail const?

Suppose you have a pointer to pointer to pointer to pointer to pointer to int. (The length of this chain is irrelevant. I just threw lots in for the hell of it). Head const would be const pointer to pointer to pointer to pointer to pointer to int. Tail const would be pointer to const pointer to const pointer to const pointer to const pointer to const int. Total const would be const pointer to const pointer to const pointer to const pointer to const pointer to const int. i.e. head const and tail const both at the same time.
Sep 10 2007
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Mon, 10 Sep 2007 12:15:09 -0700, Walter Bright wrote:

 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.

Agreed!
 Andrei and Bartosz have spent some time together going back to square 
 one with what are we trying to achieve with const, and came up with 
 another proposal.
 
 What we are trying to achieve:
 
 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they are 
 references)

Ok, nice to see this documented.
 The insights they came up with were:
 
 1) final (i.e. 'head const') is not necessary for a, b or c. final is a 
 local thing, and not strictly necessary.
 
 2) tail const can be handled in other ways, read on
 
 So, what we are left with is:
 
 o  no more final

Not a problem for me as I never used it.
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":

Remind me again what the difference between 'const' and 'invariant' is.
 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed

What do these below mean ... const int (* p) const int *(p) const int (*) p
 o  tail const of a struct would have to be done by making the struct a 
 template:
 
    struct S(T) { T member; }
    S!(int)   // tail mutable
    S!(const(int)) // tail const

But most structs contain multiple members, and usually of different types. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Sep 10 2007
next sibling parent reply Gregor Richards <Richards codu.org> writes:
Derek Parnell wrote:
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":

Remind me again what the difference between 'const' and 'invariant' is.

const: I promise not to change this, but acknowledge that somebody else might. invariant: Neither I nor anyone else will change this. const has a more limited use for optimization than invariant, since the value could be changed by other threads/programs-with-shm/whatever from under you. - Gregor Richards PS: Or at least, this is my understanding :)
Sep 10 2007
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Mon, 10 Sep 2007 12:55:29 -0700, Gregor Richards wrote:

 Derek Parnell wrote:
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":

Remind me again what the difference between 'const' and 'invariant' is.

const: I promise not to change this, but acknowledge that somebody else might. invariant: Neither I nor anyone else will change this. const has a more limited use for optimization than invariant, since the value could be changed by other threads/programs-with-shm/whatever from under you. - Gregor Richards PS: Or at least, this is my understanding :)

Thank you. I do rememeber now ... silly how I couldn't get that from the keywords. 'const' is sort of a locally write protected. 'invariant' is globally write protected. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Sep 10 2007
parent reply renoX <renosky free.fr> writes:
Derek Parnell a écrit :
 Thank you. I do remember now ... silly how I couldn't get that from the
 keywords.
 
 'const' is sort of a locally write protected.
 'invariant' is globally write protected.

So am I, each time I see 'invariant' I try to think 'value', which is much more easy to associate with the correct properties: values are immutable and you can't take the address of a value either, exactly like 'invariant'. For const, I've not found an easy to memorize keyword, but as D's const are like C++ const (AFAIK), it's not an issue for me. Of course with a Limbo syntax for variable declaration instead of the weird C-like, the parenthesis would be less(not?) useful.. renoX
Sep 11 2007
parent renoX <renosky free.fr> writes:
renoX a écrit :
 Derek Parnell a écrit :
 Thank you. I do remember now ... silly how I couldn't get that from the
 keywords.

 'const' is sort of a locally write protected.
 'invariant' is globally write protected.

So am I, each time I see 'invariant' I try to think 'value', which is much more easy to associate with the correct properties: values are immutable and you can't take the address of a value either, exactly like 'invariant'.

Oops, I didn't see the change that now you can take the address of an invariant. So now invariant != value, argh. renoX
 
 For const, I've not found an easy to memorize keyword, but as D's const 
 are like C++ const (AFAIK), it's not an issue for me.
 
 
 Of course with a Limbo syntax for variable declaration instead of the 
 weird C-like, the parenthesis would be less(not?) useful..
 
 renoX

Sep 11 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Gregor Richards wrote:
 const has a more limited use for optimization than invariant, since the 
 value could be changed by other threads/programs-with-shm/whatever from 
 under you.

const is useless for optimization, invariant is very useful for optimization. But it turns out that if you have invariant, soon you'll discover that you need const. Otherwise you'll find that every function will have to be implemented two or more times (*). (*) Because invariants cannot be implicitly converted to mutable, and vice versa.
Sep 10 2007
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Derek Parnell wrote:
 On Mon, 10 Sep 2007 12:15:09 -0700, Walter Bright wrote:

 o  tail const of a struct would have to be done by making the struct a 
 template:

    struct S(T) { T member; }
    S!(int)   // tail mutable
    S!(const(int)) // tail const


So then it would be ok to completely overrwrite an S, but not to set it's only member directly? alias S!(const(int)) T; T a; T b; a = b; // ok?? a.member = b.member; // not ok?? It seems a little odd since they do exactly the same thing. Hopefully copying a struct will be treated the same as elementwise copying of the members (so both should be illegal above).
 But most structs contain multiple members, and usually of different types.

Probably someone clever will create a type-constructor template that generates constified version of a given struct type using __traits or something. Then you'll just do alias MakeConst!(MyStruct) MyConstStruct; If not, we'll just keep pestering Walter until it is possible to write such a template. :=) Probably more realistic is a case where you want to switch between struct T { int* x; int y; } and struct T { const(int)*; int y; } So the const doesn't start on the member level, but stuff-pointed-to-by-members level --bb
Sep 10 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 Remind me again what the difference between 'const' and 'invariant'
 is.

'const' means a I can't change it (but someone else can, such as someone in a different thread). 'invariant' means nuttin' can change it, not no-how, not no-way.
 What do these below mean ...
 
 const int (* p)

syntax error
 const int *(p)

syntax error
 const int (*) p

syntax error Imagine if const were a template called Const, and we used that template to form a new type that was const of the old type. We'd invoke it like: Const!(T) t; Now, just s/Const!/const/, and the syntax should make sense.
 o  tail const of a struct would have to be done by making the
 struct a template:
 
 struct S(T) { T member; } S!(int)   // tail mutable S!(const(int))
 // tail const

But most structs contain multiple members, and usually of different types.

That's up to the struct designer.
Sep 10 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 10 Sep 2007 15:22:23 -0700, Walter Bright wrote:

 Derek Parnell wrote:
 Remind me again what the difference between 'const' and 'invariant'
 is.

'const' means a I can't change it (but someone else can, such as someone in a different thread). 'invariant' means nuttin' can change it, not no-how, not no-way.

Thank you. Because the terms in English are synonyms, its a bit hard, without constant usage, to remember this stuff. Of course, once it is permanently in D and I'm using it all the time, no doubt it'll stick ... pity about the newbies though.
 What do these below mean ...
 
 const int (* p)

syntax error
 const int *(p)

syntax error
 const int (*) p

syntax error Imagine if const were a template called Const, and we used that template to form a new type that was const of the old type. We'd invoke it like: Const!(T) t; Now, just s/Const!/const/, and the syntax should make sense.

Thanks. I thought as much.
 o  tail const of a struct would have to be done by making the
 struct a template:
 
 struct S(T) { T member; } S!(int)   // tail mutable S!(const(int))
 // tail const

But most structs contain multiple members, and usually of different types.

That's up to the struct designer.

What does that answer mean?!?!?! Ok, let's back up a second. You say "struct S(T) { T member; } S!(int) // tail mutable S!(const(int)) >>> // tail const" to which I assumed you meant that you can ONLY have tail const structs if every member of the struct is of a type mentioned in the template argument. In other words, if I have a struct with three members, each of a different type, I need to code ... struct S3(T, U, V) { T member1; U member2; V member3; } S3!(const(int), const(float), const(bool)); and so on for 4, 5, 6, .... 23 member structs. I'm sure I'm misunderstanding you, because this is really silly. -- Derek (skype: derek.j.parnell) Melbourne, Australia 11/09/2007 10:52:30 AM
Sep 10 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 11 Sep 2007 10:10:33 +0100, Janice Caron wrote:

 On 9/11/07, Derek Parnell <derek nomail.afraid.org> wrote:
 In other words, if I have a struct with three members, each of a different
 type, I need to code ...

 struct S3(T, U, V)
 {
    T member1;
    U member2;
    V member3;
 }

 S3!(const(int), const(float), const(bool));

 and so on for 4, 5, 6, .... 23 member structs.

 I'm sure I'm misunderstanding you, because this is really silly.

I don't think you're misunderstanding. I think that's what Walter is saying.

But why templates?!?!? How is that different from ... struct S3 { const(int) member1; const(float) member2; const(bool) member3; } No template involved. I'm reading Walter as saying that if any struct definition involves any of its members being const/invariant, then one must define that struct in terms of templates. Is this what Walter is really saying? -- Derek (skype: derek.j.parnell) Melbourne, Australia 12/09/2007 6:31:57 PM
Sep 12 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Derek Parnell wrote:
 On Tue, 11 Sep 2007 10:10:33 +0100, Janice Caron wrote:
 
 On 9/11/07, Derek Parnell <derek nomail.afraid.org> wrote:
 In other words, if I have a struct with three members, each of a different
 type, I need to code ...

 struct S3(T, U, V)
 {
    T member1;
    U member2;
    V member3;
 }

 S3!(const(int), const(float), const(bool));

 and so on for 4, 5, 6, .... 23 member structs.

 I'm sure I'm misunderstanding you, because this is really silly.


But why templates?!?!? How is that different from ... struct S3 { const(int) member1; const(float) member2; const(bool) member3; } No template involved. I'm reading Walter as saying that if any struct definition involves any of its members being const/invariant, then one must define that struct in terms of templates. Is this what Walter is really saying?

No, I don't think so. I believe he is saying that if you have a struct, eg. struct S1 { int a; float b; } and you want to use it, as is, in one place in your code but also use a const version of this same struct somewhere else then you need to use a template to create a modified struct definition to use. If you simply want to convert a struct from non-const aware to const-aware you can just add const to the definition and recompile. If you simply want to have a const copy of the struct somewhere you can just use const(S1), eg. class Bob { const(S1) data; } Regan
Sep 12 2007
parent Regan Heath <regan netmail.co.nz> writes:
Regan Heath wrote:
 Derek Parnell wrote:
 On Tue, 11 Sep 2007 10:10:33 +0100, Janice Caron wrote:

 On 9/11/07, Derek Parnell <derek nomail.afraid.org> wrote:
 In other words, if I have a struct with three members, each of a 
 different
 type, I need to code ...

 struct S3(T, U, V)
 {
    T member1;
    U member2;
    V member3;
 }

 S3!(const(int), const(float), const(bool));

 and so on for 4, 5, 6, .... 23 member structs.

 I'm sure I'm misunderstanding you, because this is really silly.

saying.

But why templates?!?!? How is that different from ... struct S3 { const(int) member1; const(float) member2; const(bool) member3; } No template involved. I'm reading Walter as saying that if any struct definition involves any of its members being const/invariant, then one must define that struct in terms of templates. Is this what Walter is really saying?

No, I don't think so. I believe he is saying that if you have a struct, eg. struct S1 { int a; float b; } and you want to use it, as is, in one place in your code but also use a const version of this same struct somewhere else then you need to use a template to create a modified struct definition to use. If you simply want to convert a struct from non-const aware to const-aware you can just add const to the definition and recompile. If you simply want to have a const copy of the struct somewhere you can just use const(S1), eg. class Bob { const(S1) data; }

To clarify (I hope). A template is only required if you want to _tail_ const certain members (or even all) of an existing struct in some instances but not all instances. Complete const can be achived with const(S1) and tail const on all instances can be achieved by editing the struct difinition and recompiling. Regan
Sep 12 2007
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 You might want to allow
 
 const(invariant(int)*)* p;
 
 p is a mutable pointer to a const pointer to an invariant int.
 
 Since invariant is "stronger" than const, you want to be able to do
 const(invariant(...)), but not invariant(const(...))

invariant(const(...)) will have to be accepted, and it will be treated as invariant(invariant(...)). This is just for building a type - it doesn't re-type any data.
Sep 10 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Derek Parnell wrote:
  
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":

Remind me again what the difference between 'const' and 'invariant' is.

'invariant' = constant 'const' = read-only It's simple I think. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 10 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 9/11/07, *Janice Caron* <caron800 googlemail.com 
 <mailto:caron800 googlemail.com>> wrote:
 
     It's simple, but - I would argue - the wrong choice. A better choice
     (in my humble opinion) would be
 
     const = constant
     readonly = read-only
 
     If it's not too late to change the keywords, that, to me, would be
     the way to go.
 
 
 
 By which I mean, if it's at all feasable to consider ditching the 
 keyword "invariant" altogether, and introducing a new keyword 
 "readonly", then let's do it. "readonly" should indicate a read-only 
 view of data which someone else might modify, and "const" should imply 
 that no-one can modify it ever, no way, nohow.
 
 I'm not quite sure why no one before now has suggested using "readonly" 
 to mean read-only and "const" to mean constant, but seems kind of a 
 no-brainer to me. You know - calling a thing what it is, instead of 
 something it's not. I know I'd be dead confused if int meant float, for 
 example.

It has been suggested before several times. The problem is there's disagreement over what it should mean. To some it is obvious that "readonly" should mean permanently unwriteable, just like "read only memory" is unwriteable. To others it is equally obvious that it should mean a read-only view of data that is writeable through some other means. --bb
Sep 11 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Bill Baxter wrote:
 Janice Caron wrote:
 On 9/11/07, *Janice Caron* <caron800 googlemail.com
 <mailto:caron800 googlemail.com>> wrote:

     It's simple, but - I would argue - the wrong choice. A better choice
     (in my humble opinion) would be

     const = constant
     readonly = read-only

     If it's not too late to change the keywords, that, to me, would be
     the way to go.



 By which I mean, if it's at all feasable to consider ditching the
 keyword "invariant" altogether, and introducing a new keyword
 "readonly", then let's do it. "readonly" should indicate a read-only
 view of data which someone else might modify, and "const" should imply
 that no-one can modify it ever, no way, nohow.

 I'm not quite sure why no one before now has suggested using
 "readonly" to mean read-only and "const" to mean constant, but seems
 kind of a no-brainer to me. You know - calling a thing what it is,
 instead of something it's not. I know I'd be dead confused if int
 meant float, for example.

It has been suggested before several times. The problem is there's disagreement over what it should mean. To some it is obvious that "readonly" should mean permanently unwriteable, just like "read only memory" is unwriteable. To others it is equally obvious that it should mean a read-only view of data that is writeable through some other means. --bb

The problem is that, broadly speaking, all these words mean the same thing. Once you get down to splitting hairs over exactly how constant/invariant/immutable/readonly/final something is, you're always going to find some (possibly obscure) argument against the way you want it to work. Aren't natural languages fun?! :P -- Daniel
Sep 11 2007
next sibling parent Derek Parnell <derek psych.ward> writes:
On Tue, 11 Sep 2007 19:10:29 +1000, Daniel Keep wrote:

 Bill Baxter wrote:
 Janice Caron wrote:
 On 9/11/07, *Janice Caron* <caron800 googlemail.com
 <mailto:caron800 googlemail.com>> wrote:

     It's simple, but - I would argue - the wrong choice. A better choice
     (in my humble opinion) would be

     const = constant
     readonly = read-only

     If it's not too late to change the keywords, that, to me, would be
     the way to go.



 By which I mean, if it's at all feasable to consider ditching the
 keyword "invariant" altogether, and introducing a new keyword
 "readonly", then let's do it. "readonly" should indicate a read-only
 view of data which someone else might modify, and "const" should imply
 that no-one can modify it ever, no way, nohow.

 I'm not quite sure why no one before now has suggested using
 "readonly" to mean read-only and "const" to mean constant, but seems
 kind of a no-brainer to me. You know - calling a thing what it is,
 instead of something it's not. I know I'd be dead confused if int
 meant float, for example.

It has been suggested before several times. The problem is there's disagreement over what it should mean. To some it is obvious that "readonly" should mean permanently unwriteable, just like "read only memory" is unwriteable. To others it is equally obvious that it should mean a read-only view of data that is writeable through some other means. --bb

The problem is that, broadly speaking, all these words mean the same thing. Once you get down to splitting hairs over exactly how constant/invariant/immutable/readonly/final something is, you're always going to find some (possibly obscure) argument against the way you want it to work. Aren't natural languages fun?!

Therefore let's invent some new keywords, ones that are unlikely to clash with user declared identifiers ... I'll start this ball rolling. 'rodata' instead of 'invariant' (Read Only Data) 'roview' instead of 'const' (Read Only View) alias string rodata(char)[]; -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Sep 11 2007
prev sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Daniel Keep wrote:
 Janice Caron wrote:
     It's simple, but - I would argue - the wrong choice. A better choice
     (in my humble opinion) would be

     const = constant
     readonly = read-only


The problem is that, broadly speaking, all these words mean the same thing. Once you get down to splitting hairs over exactly how constant/invariant/immutable/readonly/final something is, you're always going to find some (possibly obscure) argument against the way you want it to work. Aren't natural languages fun?!

They are. The meaning of words is very domain dependent is often based on convention rather than anything else. I am sorry, but I can't pass this chance to present my side of the argument once more. :) To an electrical engineer, "read-only" will surely give an association of memory that is "hard to change quickly or easily" (wikipedia), or maybe read-only pins, or read-only registers. To a mathematician, "constant" is a fixed value, the opposite of a "variable". In computer science, read-only could have a number of meanings. The top search results on citeseer for read-only yields the following terms: * read-only transactions * read-only memory * read-only file system * read-only query * read-only fields * read-only methods * read-only replication * read-only access * read-only parameters * read-only actions * read-only state * read-only optimizations * read-only file of which "read-only memor(y|ies)" corresponds to a mere 3.7 % of the papers, while "read-only transaction(|s)" corresponds to 29 %. The prevailing meaning of read-only in CS seems to be that in regard to access. Using read-only in the meaning of access will also not conflict with existing uses (even for an electrical engineer). On the other hand, using "const"(ant) in the meaning of something that may change, or is only access protected does conflict with the mathematical definition. It is therefore, in my view, obvious that substituting the words (readonly,const) for todays (const,invariant) would result in a much better match between the semantics of D and that of established and natural languages. The crucial separation here is: * read-only -> access * constant -> data Does the following conversation make sense? A: "I have a function that requires a read-only reference to some data" B: "Fine, I can use it for both my variable and my constant data" -- Oskar
Sep 11 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Oskar Linde wrote:
 Daniel Keep wrote:
 Janice Caron wrote:
     It's simple, but - I would argue - the wrong choice. A better 
 choice
     (in my humble opinion) would be

     const = constant
     readonly = read-only


The problem is that, broadly speaking, all these words mean the same thing. Once you get down to splitting hairs over exactly how constant/invariant/immutable/readonly/final something is, you're always going to find some (possibly obscure) argument against the way you want it to work. Aren't natural languages fun?!

They are. The meaning of words is very domain dependent is often based on convention rather than anything else. I am sorry, but I can't pass this chance to present my side of the argument once more. :) To an electrical engineer, "read-only" will surely give an association of memory that is "hard to change quickly or easily" (wikipedia), or maybe read-only pins, or read-only registers. To a mathematician, "constant" is a fixed value, the opposite of a "variable". In computer science, read-only could have a number of meanings. The top search results on citeseer for read-only yields the following terms: * read-only transactions * read-only memory * read-only file system * read-only query * read-only fields * read-only methods * read-only replication * read-only access * read-only parameters * read-only actions * read-only state * read-only optimizations * read-only file of which "read-only memor(y|ies)" corresponds to a mere 3.7 % of the papers, while "read-only transaction(|s)" corresponds to 29 %. The prevailing meaning of read-only in CS seems to be that in regard to access. Using read-only in the meaning of access will also not conflict with existing uses (even for an electrical engineer). On the other hand, using "const"(ant) in the meaning of something that may change, or is only access protected does conflict with the mathematical definition. It is therefore, in my view, obvious that substituting the words (readonly,const) for todays (const,invariant) would result in a much better match between the semantics of D and that of established and natural languages. The crucial separation here is: * read-only -> access * constant -> data Does the following conversation make sense? A: "I have a function that requires a read-only reference to some data" B: "Fine, I can use it for both my variable and my constant data"

I agree. (Damn, I always agree with you Oskar :P ) But I'll add: It's not just that the "read-only" meaning as in "read-only access/permissions" is the most popular one. It's also the meaning most congruent with the natural language term "read-only". That is, it's the most "meaningful". ROM memory "is" read-only in the same sense that a dear is mammal. A deer is indeed a mammal, but it's more than a mammal, it's... a deer. :P It's likely due to that that the alternative terminology wouldn't sound so strange for an electrical engineer, while the current one sounds strange for (most of) us CS programmers. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 11 2007
prev sibling next sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bruno Medeiros wrote:
 Derek Parnell wrote:
  
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":

Remind me again what the difference between 'const' and 'invariant' is.

'invariant' = constant 'const' = read-only It's simple I think.

I must withdraw somewhat my comment that "it's simple", since "read-only" is indeed a somewhat subjective term. If you think read-only as in permissions, you get the intended meaning. But if you think read-only as in memory (ROM), as Bill mentioned, you get the same meaning as constant. Still, I think the "read-only as in permissions" meaning for "read-only" is becoming more prevalent as it is more... meaningful. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 11 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Janice Caron wrote:
 
 I'm not quite sure why no one before now has suggested using "readonly" 
 to mean read-only and "const" to mean constant, but seems kind of a 
 no-brainer to me. You know - calling a thing what it is, instead of 
 something it's not. I know I'd be dead confused if int meant float, for 
 example.
 

It was quite naive to think that no one had suggested keyword changes for this before. :P -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 11 2007
parent Nicolai Waniek <nospam inval.id> writes:
On Tue, 11 Sep 2007 12:09:21 +0100, Janice Caron wrote:

  class C
  {
      private int my_n;
      public int n() { return my_n; }
 
      /* all my private code refers to my_n */
  }
  }
 I'd like to be able to do
 
  class C
  {
      (privately read-write but publicly read-only) int n;
 
      /* all my private code refers to n */
  }
  }

This would translate to something else coming from Delphi, the "property" tag with the read and write options. You could easily translate this to a D-ish way: private { int my_n; } public { property int my_n: read my_n; } so, whenever you're inside your class, you always have the reference onto the private member. Whenever wanting to read the value of my_n from outside the class, you will get the public member that is just a "wrapper" to the internal one. But that would make it possible for something like: private { int my_n; int get_my_n () { // do some calculation that may change the responded value of my_n, // for example the object wanting to have the information return theCorrectValue; } void set_my_n () { // do some calculation, check the range, whatever my_n = theCorrectValue; } } public { property int my_n: read get_my_n write set_my_n } Well this looks like a Delphi declaration with C Syntax, so i I guess it might not make it into D ;-) Apart from that it would cover your problem with "internal RW-access, external R-access only". -- .71 nicolai dot waniek at sphere71 dot com
Sep 11 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
Oh wait! I do have one comment, or question, or whatever.

 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed

Two ways of writing the same thing. I don't understand the need for the bracketless version. Could we not just insist that the brackets must always be there? Or does the lack of brackets actually mean something? (if it does, it's back to being confusing again).
Sep 10 2007
parent Regan Heath <regan netmail.co.nz> writes:
Janice Caron wrote:
 Oh wait! I do have one comment, or question, or whatever.
 
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed

Two ways of writing the same thing. I don't understand the need for the bracketless version. Could we not just insist that the brackets must always be there? Or does the lack of brackets actually mean something? (if it does, it's back to being confusing again).

I thought the same thing. I suspect the brackets will remain optional, but I reckon "good style" would require them. For clarity if nothing else. Regan
Sep 10 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
Oh wait! I do have one comment, or question, or whatever.

 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed

Two ways of writing the same thing. I don't understand the need for the bracketless version. Could we not just insist that the brackets must always be there? Or does the lack of brackets actually mean something? (if it does, it's back to being confusing again).
Sep 10 2007
next sibling parent Nathan Reed <nathaniel.reed gmail.com> writes:
Janice Caron wrote:
 Oh wait! I do have one comment, or question, or whatever.
 
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed

Two ways of writing the same thing. I don't understand the need for the bracketless version. Could we not just insist that the brackets must always be there? Or does the lack of brackets actually mean something? (if it does, it's back to being confusing again).

I'd rather have the bracketless version to save typing in the case when I want to have the whole thing be const (which is the most common case, I believe). Thanks, Nathan Reed
Sep 10 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed

Two ways of writing the same thing. I don't understand the need for the bracketless version. Could we not just insist that the brackets must always be there? Or does the lack of brackets actually mean something? (if it does, it's back to being confusing again).

The bracketless version is for two reasons: 1) it's what people expect to have to type, for example: void foo(const T t) { } It will be the most common case. 2) it enables things like: const { int a = 3; int b = 4; } which is nice for a grouped collection of them.
Sep 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
 What do these below mean ...

   const int (* p)
   const int *(p)
   const int (*) p

I believe I can answer that. They are all syntax errors. The stuff inside the brackets needs to be a valid type expression.
Sep 10 2007
prev sibling next sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribió:
 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.
 
 Andrei and Bartosz have spent some time together going back to square 
 one with what are we trying to achieve with const, and came up with 
 another proposal.
 
 What we are trying to achieve:
 
 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they are 
 references)
 
 The insights they came up with were:
 
 1) final (i.e. 'head const') is not necessary for a, b or c. final is a 
 local thing, and not strictly necessary.
 
 2) tail const can be handled in other ways, read on
 
 So, what we are left with is:
 
 o  no more final
 
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":
 
 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed
 
 o  const and invariant are transitive. There is no way to specify a 
 (pointer to)(const pointer to)(mutable int).
 
 o  tail invariant for an array or pointer type can be done using the 
 existing syntax:
 
  invariant(char)[] s;   // string, i.e. an array of invariant chars
  const(T)* p;  // pointer to tail const T
 
 o  tail const of a struct would have to be done by making the struct a 
 template:
 
   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const
 
 o  one can construct a template to generically produce tail const or 
 tail invariant versions of a type.
 
 o  it will be illegal to attempt to change the key value of a foreach 
 loop, but the compiler will not be able to diagnose all attempts to do so
 
 o  static const/invariant means the initializer is evaluated at compile 
 time. non-static const/invariant means it is evaluated at run time.
 
 o  no initializer means it is assigned in the corresponding constructor.
 
 o  const/invariant declarations will always allocate memory (so their 
 addresses can be taken)
 

I won't comment on all that since I'm still lost in all that const thing. But...
 o  So, we still need a method to declare a constant that will not 
 consume memory. We'll co-opt the future macro syntax for that:
 
     macro x = 3;
     macro s = "hello";

Am I the only one who doesn't like this syntax? I guess it kinda makes sense, but I just don't like how it looks. -- Carlos Santander Bernal
Sep 10 2007
next sibling parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Carlos Santander wrote:
 Walter Bright escribió:
 o  So, we still need a method to declare a constant that will not 
 consume memory. We'll co-opt the future macro syntax for that:

     macro x = 3;
     macro s = "hello";

Am I the only one who doesn't like this syntax? I guess it kinda makes sense, but I just don't like how it looks.

Seems to me like this unnecessarily departs from the macro syntax introduced at the conference: macro foo(e) { e = 3; } (On the other hand, in one of them the macro is for an expression while in the other one it's a sequence of declarations/statements. So maybe it is reasonable to have the different syntax.) Thanks, Nathan Reed
Sep 10 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Nathan Reed wrote:
 Carlos Santander wrote:
 Walter Bright escribió:
 o  So, we still need a method to declare a constant that will not 
 consume memory. We'll co-opt the future macro syntax for that:

     macro x = 3;
     macro s = "hello";

Am I the only one who doesn't like this syntax? I guess it kinda makes sense, but I just don't like how it looks.

Seems to me like this unnecessarily departs from the macro syntax introduced at the conference: macro foo(e) { e = 3; } (On the other hand, in one of them the macro is for an expression while in the other one it's a sequence of declarations/statements. So maybe it is reasonable to have the different syntax.)

It's not far off from things used in some functional programming languages, so it doesn't look to bad to me. The word 'macro' typically implies a direct, maybe even textual, substitution. It's also a lot like #define, just with an '=' there which I think is an improvement over #define. '=' for the no-arg macros, '{ }' for the arg-ful macros. Seems ok to me. --bb
Sep 10 2007
parent reply Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Bill Baxter wrote:
 Nathan Reed wrote:
 Carlos Santander wrote:
 Walter Bright escribió:
 o  So, we still need a method to declare a constant that will not
 consume memory. We'll co-opt the future macro syntax for that:

     macro x = 3;
     macro s = "hello";

Am I the only one who doesn't like this syntax? I guess it kinda makes sense, but I just don't like how it looks.

Seems to me like this unnecessarily departs from the macro syntax introduced at the conference: macro foo(e) { e = 3; } (On the other hand, in one of them the macro is for an expression while in the other one it's a sequence of declarations/statements. So maybe it is reasonable to have the different syntax.)

It's not far off from things used in some functional programming languages, so it doesn't look to bad to me. The word 'macro' typically implies a direct, maybe even textual, substitution. It's also a lot like #define, just with an '=' there which I think is an improvement over #define. '=' for the no-arg macros, '{ }' for the arg-ful macros. Seems ok to me.

And let's take another step forward (might scare off some old C farts, though, but I like it): T sum(T)(T a, T b) = a + b; For consistency's sake! :D
Sep 10 2007
parent Downs <default_357-line yahoo.de> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Jari-Matti Mäkelä wrote:
 Bill Baxter wrote:
 Nathan Reed wrote:
 Carlos Santander wrote:
 Walter Bright escribió:
 o  So, we still need a method to declare a constant that will not
 consume memory. We'll co-opt the future macro syntax for that:

     macro x = 3;
     macro s = "hello";

sense, but I just don't like how it looks.

introduced at the conference: macro foo(e) { e = 3; } (On the other hand, in one of them the macro is for an expression while in the other one it's a sequence of declarations/statements. So maybe it is reasonable to have the different syntax.)

languages, so it doesn't look to bad to me. The word 'macro' typically implies a direct, maybe even textual, substitution. It's also a lot like #define, just with an '=' there which I think is an improvement over #define. '=' for the no-arg macros, '{ }' for the arg-ful macros. Seems ok to me.

And let's take another step forward (might scare off some old C farts, though, but I like it): T sum(T)(T a, T b) = a + b; For consistency's sake! :D

How about so? T sum(T)(T a, T b) a + b; This contains two things that I've feature requested before: implicit return of the last expression in a function, and omitting { } for one-statement functions. My point being, it's somewhat closer to our current D than your approach, in that it only requires two backwards-compatible extensions instead of a whole new syntax :) --downs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFG5daKpEPJRr05fBERApOlAJ9+t/uJ1CskJhPikz/ni1SqgT/fkgCeO/yG lFhsq9awCy0Gi1fSTUNBWU4= =pA1m -----END PGP SIGNATURE-----
Sep 10 2007
prev sibling parent reply Bruce Adams <tortoise_74 yeah.nospam.mehoo.co.uk> writes:
Carlos Santander Wrote:

 Walter Bright escribió:
 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.
 


 
 o  So, we still need a method to declare a constant that will not 
 consume memory. We'll co-opt the future macro syntax for that:
 
     macro x = 3;
     macro s = "hello";

Am I the only one who doesn't like this syntax? I guess it kinda makes sense, but I just don't like how it looks. -- Carlos Santander Bernal

I'm not clear why this is even needed. Surely "static const" implies some level of don't consume any memory by virtue of meaning "available at compile time". If you need more than that, rather than trusting the compiler then I second "inline" rather than macro, but honestly I can't think of an example where this will give you a worthwhile performance gain (unlike an inline function). Regards, Bruce.
Sep 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruce Adams wrote:
 o  So, we still need a method to declare a constant that will not
  consume memory. We'll co-opt the future macro syntax for that:
 
 macro x = 3; macro s = "hello";


some level of don't consume any memory by virtue of meaning "available at compile time". If you need more than that, rather than trusting the compiler then I second "inline" rather than macro, but honestly I can't think of an example where this will give you a worthwhile performance gain (unlike an inline function).

windows.h in all its terrifying glory has 10,000 macro declarations. If each one occupied 4 bytes of static data, there's 40K of bloat in every exe.
Sep 10 2007
parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Bruce Adams wrote:
 o  So, we still need a method to declare a constant that will not
  consume memory. We'll co-opt the future macro syntax for that:

 macro x = 3; macro s = "hello";


some level of don't consume any memory by virtue of meaning "available at compile time". If you need more than that, rather than trusting the compiler then I second "inline" rather than macro, but honestly I can't think of an example where this will give you a worthwhile performance gain (unlike an inline function).

windows.h in all its terrifying glory has 10,000 macro declarations. If each one occupied 4 bytes of static data, there's 40K of bloat in every exe.

That sounds about right. When we converted const values to enum values in the Tango Win32 headers, app size dropped by roughly 46k. But there were issues with some of those enum values having the wrong type (-1 set to int instead of uint), which led to a few strange bugs. Sean
Sep 10 2007
parent reply Bruce Adams <tortoise_74 yahoo.couknowthedrill.com> writes:
Sean Kelly Wrote:

 Walter Bright wrote:
 Bruce Adams wrote:
 o  So, we still need a method to declare a constant that will not
  consume memory. We'll co-opt the future macro syntax for that:

 macro x = 3; macro s = "hello";


some level of don't consume any memory by virtue of meaning "available at compile time". If you need more than that, rather than trusting the compiler then I second "inline" rather than macro, but honestly I can't think of an example where this will give you a worthwhile performance gain (unlike an inline function).

windows.h in all its terrifying glory has 10,000 macro declarations. If each one occupied 4 bytes of static data, there's 40K of bloat in every exe.

That sounds about right. When we converted const values to enum values in the Tango Win32 headers, app size dropped by roughly 46k. But there were issues with some of those enum values having the wrong type (-1 set to int instead of uint), which led to a few strange bugs. Sean

I seek enlightenmnet for at least two problems with that explanation. windows.h is a C binding not a D binding. I'm not clear how one translates to the other yet as I've avoided bi-lingual stuff so far. Presumably the D binding is created using htod. I thought D followed the you only pay for what you use philosophy. So if I import my windows.d module I will only pay the cost of the variables I actually use. My friendly neighbourhoold compiler, being clever, mightl also make an effort to minimise the cost of those too. While we're on the subject of windows. 40K means nothing. In an embedded system yes. Windows is bloated to the point where 40K in a user application means de nada. (I shudder at the 4Gb games that follow in its wake too). I have know idea where the windows mobile / CE / pocket PC api falls here. Regards, Bruce.
Sep 10 2007
parent Sean Kelly <sean f4.ca> writes:
Bruce Adams wrote:
 Sean Kelly Wrote:
 
 Walter Bright wrote:
 Bruce Adams wrote:
 o  So, we still need a method to declare a constant that will not
  consume memory. We'll co-opt the future macro syntax for that:

 macro x = 3; macro s = "hello";


some level of don't consume any memory by virtue of meaning "available at compile time". If you need more than that, rather than trusting the compiler then I second "inline" rather than macro, but honestly I can't think of an example where this will give you a worthwhile performance gain (unlike an inline function).

each one occupied 4 bytes of static data, there's 40K of bloat in every exe.

in the Tango Win32 headers, app size dropped by roughly 46k. But there were issues with some of those enum values having the wrong type (-1 set to int instead of uint), which led to a few strange bugs. Sean

I seek enlightenmnet for at least two problems with that explanation. windows.h is a C binding not a D binding. I'm not clear how one translates to the other yet as I've avoided bi-lingual stuff so far. Presumably the D binding is created using htod.

htod works for simple cased. More complex cases require GregorR's BCD or simply a manual effort.
 I thought D followed the you only pay for what you use philosophy.
 So if I import my windows.d module I will only pay the cost of the
 variables I actually use. My friendly neighbourhoold compiler, being clever,
mightl also make an effort to minimise the cost of those too.

D currently links at the file level, so if you use anything in a file you get the whole thing. Some linkers are able to link at the section level (a piece of a file), but D does not support this yet (see comments regarding gc-sections using ld, I believe--there's an issue in BugTracker about this).
 While we're on the subject of windows. 40K means nothing. In an embedded
system yes. Windows is bloated to the point where 40K in a user application
means de nada. (I shudder at the 4Gb games that follow in its wake too). I have
know idea where the windows mobile / CE / pocket PC api falls here.

It matters to some people, particularly those coming from C and comparing EXE sizes. It may not be relevant as far as general development on Windows is concerned, but it's something library developers must take into consideration. Sean
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
You might want to allow

const(invariant(int)*)* p;

p is a mutable pointer to a const pointer to an invariant int.

Since invariant is "stronger" than const, you want to be able to do
const(invariant(...)), but not invariant(const(...))
Sep 10 2007
prev sibling next sibling parent reply Russell Lewis <webmaster villagersonline.com> writes:
Walter Bright wrote:
 (snip)
 o  static const/invariant means the initializer is evaluated at compile 
 time. non-static const/invariant means it is evaluated at run time.

I'd like to suggest that we use some keyword other than "static." Yeah, I know that "static" can imply "static analysis" (compile time), but the static modifier on a variable has a well-known meaning from C: it's not exported. I think that that was a poor design choice in C, but now we're stuck with millions of programmers who expect it. I think that this would just cause confusion, and perhaps we need a new keyword to express this new concept. While I understand that we don't want to multiply keywords, multiplying concepts, and then overloading them all into a single keyword, is, IMHO, even worse.
 o  no initializer means it is assigned in the corresponding constructor.
 
 o  const/invariant declarations will always allocate memory (so their 
 addresses can be taken)
 
 o  So, we still need a method to declare a constant that will not 
 consume memory. We'll co-opt the future macro syntax for that:
 
     macro x = 3;
     macro s = "hello";

How about "inline"? That seems to clearly express what we want to express (there is a clear parallel between inline functions and inline constants). I don't recall: is inline a keyword in D or not?
Sep 10 2007
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/10/07, Russell Lewis <webmaster villagersonline.com> wrote:

 the
 static modifier on a variable has a well-known meaning from C: it's not
 exported.

We use private for that purpose in D, so static is free to be retooled to mean "compile-time".
Sep 10 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Russell Lewis wrote:
 Walter Bright wrote:
  > (snip)
 o  static const/invariant means the initializer is evaluated at 
 compile time. non-static const/invariant means it is evaluated at run 
 time.

I'd like to suggest that we use some keyword other than "static." Yeah, I know that "static" can imply "static analysis" (compile time), but the static modifier on a variable has a well-known meaning from C: it's not exported. I think that that was a poor design choice in C, but now we're stuck with millions of programmers who expect it.

No one cares (or at least, should care) what that means in C. That's ancient history. More important is the other meaning that 'static' in *D itself*: the meaning of "non-instance / unique" which is really unrelated to "compile-time". -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 10 2007
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.
 
 Andrei and Bartosz have spent some time together going back to square 
 one with what are we trying to achieve with const, and came up with 
 another proposal.
 
 What we are trying to achieve:
 
 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they are 
 references)

Does c) include being able to pass literal arguments to functions expecting const-references? I.e. sort of the opposite: being able to treat value types as const-references, even though they are const values.
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":
 
 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed

I'm very happy that 'const int' will mean something other than 'just plain int'. The always-off-by-one part of the current design is hard to get enthusiastic about. But this one makes more sense. Maybe you might consider Janice's suggestion for the structs, though: const(S)* // Pointer to S nonconst, S is const const(S) // S const const*(S) // S nonconst, but stuff accessed via S is const const**(S) // S and S.member nonconst, but stuff accessed via S.member is const --bb
Sep 10 2007
prev sibling next sibling parent reply Russell Lewis <webmaster villagersonline.com> writes:
Walter Bright wrote:
 What we are trying to achieve:
 
 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they are 
 references)

Is there a way to express (for a pointer passed as a function parameter) "the callee is allowed to modify this object through the pointer, but the caller ensures that no other code will be modifying it at the same time"? Sort of a "you have exclusive write access" modifier? Is that a common enough case to even support? Russ
Sep 10 2007
next sibling parent reply Gregor Richards <Richards codu.org> writes:
Russell Lewis wrote:
 Walter Bright wrote:
 What we are trying to achieve:

 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they 
 are references)

Is there a way to express (for a pointer passed as a function parameter) "the callee is allowed to modify this object through the pointer, but the caller ensures that no other code will be modifying it at the same time"? Sort of a "you have exclusive write access" modifier? Is that a common enough case to even support? Russ

It would probably be easy enough to add such a type modifier, but literally impossible to to hold that restriction, so I don't think it's worth it. There's no magic-bullet for concurrency. - Gregor Richards
Sep 10 2007
parent Russell Lewis <webmaster villagersonline.com> writes:
Gregor Richards wrote:
 Russell Lewis wrote:
 Walter Bright wrote:
 What we are trying to achieve:

 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they 
 are references)

Is there a way to express (for a pointer passed as a function parameter) "the callee is allowed to modify this object through the pointer, but the caller ensures that no other code will be modifying it at the same time"? Sort of a "you have exclusive write access" modifier? Is that a common enough case to even support? Russ

It would probably be easy enough to add such a type modifier, but literally impossible to to hold that restriction, so I don't think it's worth it. There's no magic-bullet for concurrency.

I agree that there's no magic-bullet for concurrency, but there *are* times where it could be enforced: 1) The caller's variable is a local variable, and either there have been no pointers to the local (including frame pointers created by delegate literals), or else all of those locals were passed to external code as "const." 2) The caller already holds the variable in "exclusive" mode. (I haven't figured out any way, yet, to make it work for globals.) Whether it's worth the trouble is a whole 'nother question, of course.
Sep 10 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Russell Lewis wrote:
 Walter Bright wrote:
 What we are trying to achieve:

 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they 
 are references)

Is there a way to express (for a pointer passed as a function parameter) "the callee is allowed to modify this object through the pointer, but the caller ensures that no other code will be modifying it at the same time"? Sort of a "you have exclusive write access" modifier? Is that a common enough case to even support?

That's sort of what the often proposed 'unique' type qualifier does. The problem is, there's just no reasonable way to implement it in a *checkable* manner. And if it ain't checkable, it's about as useful as painting a black stripe across your lap when driving instead of wearing a seatbelt.
Sep 10 2007
next sibling parent Bruce Adams <tortoise_74 yahoo.couknowthedrill.com> writes:
Walter Bright Wrote:

 Russell Lewis wrote:
 Walter Bright wrote:
 What we are trying to achieve:

 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they 
 are references)

Is there a way to express (for a pointer passed as a function parameter) "the callee is allowed to modify this object through the pointer, but the caller ensures that no other code will be modifying it at the same time"? Sort of a "you have exclusive write access" modifier? Is that a common enough case to even support?

That's sort of what the often proposed 'unique' type qualifier does. The problem is, there's just no reasonable way to implement it in a *checkable* manner. And if it ain't checkable, it's about as useful as painting a black stripe across your lap when driving instead of wearing a seatbelt.

I see a least two ways of interpreting it at run-time. Acquire a lock before entering the function (or at least the parts where the value is modified) or in an STM atomic transaction roll-back and try again if the value is modified. Being run-time only I suspect a function or template and lazy evaluation might suffice. At compile time you could use it as a hint for 'linting' race conditions out of your code. That is a useful instrumentation to have. (cyclist wearing a flourescent band as opposed to a black stripe) These are just suggestions I wouldn't advocate anything without more thought. As I said on the next branch over this is off topic for a discussion about const. Regards, Bruce.
Sep 10 2007
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright wrote:
 Russell Lewis wrote:
 Walter Bright wrote:
 What we are trying to achieve:

 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they 
 are references)

Is there a way to express (for a pointer passed as a function parameter) "the callee is allowed to modify this object through the pointer, but the caller ensures that no other code will be modifying it at the same time"? Sort of a "you have exclusive write access" modifier? Is that a common enough case to even support?

That's sort of what the often proposed 'unique' type qualifier does. The problem is, there's just no reasonable way to implement it in a *checkable* manner. And if it ain't checkable, it's about as useful as painting a black stripe across your lap when driving instead of wearing a seatbelt.

Except for the most pathological cases, I believe there are some checkable ways to do this. Consider a piece of code that uniquely has write privilege to a variable (and is only ever called from a specific thread). In that case, it can give out scoped invariant access when calling functions within its thread, or const access to anything. Violation of the unique write privilege should be easy enough to check/enforce. For example, calling a function (or passing a delegate) with non-const access. Of course, this all relies on avoiding multi-threaded calls to a function. While I haven't thought it all out, I think the answer may lie with thread-local storage.
Sep 10 2007
parent reply Jason House <jason.james.house gmail.com> writes:
Jason House wrote:
 Except for the most pathological cases...

The pathological case that I'm aware of is recursion.
Sep 10 2007
parent Bruce Adams <tortoise_74 ya.hoo.nospamme.uk.ceo> writes:
Jason House Wrote:

 Jason House wrote:
 Except for the most pathological cases...

The pathological case that I'm aware of is recursion.

[OT] recursion is an odd beast. Its a fundermental programming concept and yet I noticed recently that I almost never ever need it in C++. In fact it only seems to come up in templates. In D we have static looping constructs which remove the need even there. I wonder how often it will show up in practice. Bruce.
Sep 11 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.
 

Took you guys a while to figure that out... -_-'
 1) final (i.e. 'head const') is not necessary for a, b or c. final is a 
 local thing, and not strictly necessary.
 

Then, there will no longer be a final "storage class" for var declarations?
 2) tail const can be handled in other ways, read on
 

 
 o  tail const of a struct would have to be done by making the struct a 
 template:
 
   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const
 
 o  one can construct a template to generically produce tail const or 
 tail invariant versions of a type.
 

As Regan asked before, and how would that work for classes? This is a most fundamental issue that has not been mentioned. Without a way to declare tail const/invariant, this design, no matter how simpler or more understandable, is fundamentally *broken*. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 10 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruno Medeiros wrote:
 As Regan asked before, and how would that work for classes? This is a 
 most fundamental issue that has not been mentioned. Without a way to 
 declare tail const/invariant, this design, no matter how simpler or more 
 understandable, is fundamentally *broken*.

What, exactly, is the use case that needs a solution?
Sep 10 2007
next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Bruno Medeiros wrote:
 As Regan asked before, and how would that work for classes? This is a 
 most fundamental issue that has not been mentioned. Without a way to 
 declare tail const/invariant, this design, no matter how simpler or 
 more understandable, is fundamentally *broken*.

What, exactly, is the use case that needs a solution?

Before hand, let me just say that by broken, I didn't mean subvertable, but rather that using const/invariant with classes would be impractical and unmanageable. Simple example: Let's say you have a class Foo that is comparable. How do you sort an array of const(Foo)'s ? const(Foo)[] sort(const(Foo)[] arr) { ... ? The sorting algorithm doesn't matter, what matters is: how would one swap two elements in the array? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruno Medeiros wrote:
 Walter Bright wrote:
 What, exactly, is the use case that needs a solution?

Before hand, let me just say that by broken, I didn't mean subvertable, but rather that using const/invariant with classes would be impractical and unmanageable. Simple example: Let's say you have a class Foo that is comparable. How do you sort an array of const(Foo)'s ? const(Foo)[] sort(const(Foo)[] arr) { ... ? The sorting algorithm doesn't matter, what matters is: how would one swap two elements in the array?

You can't sort an (array of)(const T). You can sort an (array of)(something of)(const T).
Sep 10 2007
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Walter Bright Wrote:

 Bruno Medeiros wrote:
 Walter Bright wrote:
 What, exactly, is the use case that needs a solution?

Before hand, let me just say that by broken, I didn't mean subvertable, but rather that using const/invariant with classes would be impractical and unmanageable. Simple example: Let's say you have a class Foo that is comparable. How do you sort an array of const(Foo)'s ? const(Foo)[] sort(const(Foo)[] arr) { ... ? The sorting algorithm doesn't matter, what matters is: how would one swap two elements in the array?

You can't sort an (array of)(const T). You can sort an (array of)(something of)(const T).

That worries me (not that case in particular, but general constness + classes). My coding style tends to use a lot of invariant objects (for thread safety reasons), so I get the feeling that the inability to *easily* express a mutable reference to a constant class will bite me like a ferret.
Sep 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Robert Fraser wrote:
 That worries me (not that case in particular, but general constness +
 classes). My coding style tends to use a lot of invariant objects
 (for thread safety reasons), so I get the feeling that the inability
 to *easily* express a mutable reference to a constant class will bite
 me like a ferret.

It worries me in the abstract, but I've not been able to find a real case where it matters.
Sep 10 2007
parent Robert Fraser <fraserofthenight gmail.com> writes:
Walter Bright Wrote:

 Robert Fraser wrote:
 That worries me (not that case in particular, but general constness +
 classes). My coding style tends to use a lot of invariant objects
 (for thread safety reasons), so I get the feeling that the inability
 to *easily* express a mutable reference to a constant class will bite
 me like a ferret.

It worries me in the abstract, but I've not been able to find a real case where it matters.

Here's an easy one: returning constant class references from an opApply. Or, if you argue the variable holding the references is reassigned each time, how about iterating through constant nodes in a linked list? const(node) current; while(current = current.next) { .... } ... or really any sort of iteration using the same variable. Or how about (took looking at half a page of code to find an example similar to this): const(Foo) foo = bar(); if(null is foo) foo = new Foo(); Obviously, there may be a way around this, but I use this idiom fairly frequently, especially when getting stuff that might or might not be in an associative array/map. ... Or what about cached objects, which are passed to the function handling the cache as const (since the function handling the cache shouldn't be changing them), but the references the cache holds are constantly changing? ... I could go on all day like this. Not that any of these are insurmountable (I'd just stop using const for class references altogether and cast when I was sending stuff to a library that wanted it, so there's a way to surmount it in my own code), but to make const usable, these cases need to be considered. In fact, for curiosity's sake, I just ran Eclipse's "use final when possible" on Descent's (incomplete) Java port of the DMD front-end, and it looks like a little more than _half_ of the class references were reassigned sometime during their lifespan. None of these class references could be made to point to constant data.
Sep 10 2007
prev sibling parent Nathan Reed <nathaniel.reed gmail.com> writes:
Walter Bright wrote:
 Bruno Medeiros wrote:
 Walter Bright wrote:
 What, exactly, is the use case that needs a solution?

Before hand, let me just say that by broken, I didn't mean subvertable, but rather that using const/invariant with classes would be impractical and unmanageable. Simple example: Let's say you have a class Foo that is comparable. How do you sort an array of const(Foo)'s ? const(Foo)[] sort(const(Foo)[] arr) { ... ? The sorting algorithm doesn't matter, what matters is: how would one swap two elements in the array?

You can't sort an (array of)(const T). You can sort an (array of)(something of)(const T).

I may be misunderstanding this. When you declare an array const(C)[] where C is a class type, it's really an array of *references* to objects (of class C), right? So it seems unreasonable to restrict such an array from being sorted, since you can sort the references without modifying the objects' contents. In general, I'd think there would be lots of situations where you want to be able to hold a reference to an object, through which you don't want to allow the object's contents to be changed, but you *do* want to be able to change the reference to refer to different objects at different times. In other words, tail-const. (One concrete example comes from interactive graphics. Say we have some set of objects that represent pieces of geometry to be drawn. The rendering routine shouldn't modify the geometry, so the objects' contents should be const. But it should sort the geometry for the most efficient rendering order, so it needs to be able to have a non-const array of references to constant objects.) Thanks, Nathan Reed
Sep 10 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Bruno Medeiros wrote:
 As Regan asked before, and how would that work for classes? This is a 
 most fundamental issue that has not been mentioned. Without a way to 
 declare tail const/invariant, this design, no matter how simpler or 
 more understandable, is fundamentally *broken*.

What, exactly, is the use case that needs a solution?

Here's another use case, which is significantly different than the other (there should be lots more to be found). Say you have a Person class, which has a name, and for whatever reasons you want the name be a String class instead of const(char)[]. Then you have: class Person { const(String) name; ... but now how do you change the name of the Person?... -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruno Medeiros wrote:
 Walter Bright wrote:
 What, exactly, is the use case that needs a solution?

Here's another use case, which is significantly different than the other (there should be lots more to be found). Say you have a Person class, which has a name, and for whatever reasons you want the name be a String class instead of const(char)[]. Then you have: class Person { const(String) name; ... but now how do you change the name of the Person?...

I'd redesign String so it holds invariant data, then just have: class Person { String name; ...
Sep 10 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Bruno Medeiros wrote:
 Walter Bright wrote:
 What, exactly, is the use case that needs a solution?

Here's another use case, which is significantly different than the other (there should be lots more to be found). Say you have a Person class, which has a name, and for whatever reasons you want the name be a String class instead of const(char)[]. Then you have: class Person { const(String) name; ... but now how do you change the name of the Person?...

I'd redesign String so it holds invariant data, then just have: class Person { String name; ...

Err... you do realize what this implies? This example holds not just for String but for the general case where one would want to use aggregation of immutable objects. In all those cases you'd have to redesign the aggregated class to be immutable, with all the annoyances that has. That basicly amounts to not be able to use const/invariant for classes, and then in that regard D would revert to the same status quo as Java and other languages. (Another alternative is to use an actual pointer to a class reference, but that's sure to bring another slew of inconveniences) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 11 2007
prev sibling parent BCS <ao pathlink.com> writes:
There is no way to find an optimal const solution without trying every possible 
const solution.

(IIRC what "NP Complete" is, might be "NP really stinking hard" I'm thinking 
of <G>)
Sep 10 2007
prev sibling next sibling parent reply Miles <_______ _______.____> writes:
Walter Bright wrote:
 Const, final, invariant, head const, tail const, it's grown into a
 monster. It tries to cover all the bases, but in doing so is simply not
 understandable.

I was going to say that, that D's const sucks, many months ago, but didn't because I was afraid being regarded as a troll. Its not that I like const-less languages like Java (in fact, I hate them), but I think that C++'s const, with all its problems, was the right answer (for that time). It fixes the problem without putting much burden onto the programmer. D's const puts way too much burden onto the programmer, and gives very little back. Too many different combinations of const, final, invariant, pointers and references (and the fact that in D you don't immediately know if a type "T" is a reference of a value, specially while writing templates, adds to this confusion) with confusing semantics when combined (with a few special cases and lack of orthogonality). When the new const was being designed, I told in this list: "Don't try to fix what ain't broken". What I meant was: Don't try to do a new const just because you want a better const than C++s. Many people dislike C++s const, but are unable to point exactly why it is wrong. Most people doesn't even understand C++'s const, and try to get more from it than it was actually intended to provide (that is the whole point behind the new D's invariant). Sometimes people give use-cases to exemplify (STL's need for a difference between iterator<> and const_iterator<> is the first thing that comes to my mind), but then you ask what a language could provide to fix this, they don't know. This is where you should aim first you laser. When I first saw D, I was amazed with the possibilities. Now I'm somewhat scared. Perhaps one of the things that are scaring me is just like you said: there is a (at least one) monster in the language. Just my two cents... I have no interest in trolling anyone.
Sep 10 2007
next sibling parent reply Downs <default_357-line yahoo.de> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Miles wrote:
 Sometimes people give use-cases to exemplify (STL's need for a
 difference between iterator<> and const_iterator<> is the first thing
 that comes to my mind), but then you ask what a language could provide
 to fix this, they don't know.

 This is where you should aim first you laser.
 

Wait, what? I presume this is a typo, but I can't for the life of me figure out what it's supposed to be. No offense :) --downs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFG5e4tpEPJRr05fBERAvl+AKCdSR5obRn4Nr8xGeG5TFAaOtR6vACfYlCw UlAVONsjnf4ymEw8IH85wkg= =7Z1J -----END PGP SIGNATURE-----
Sep 10 2007
parent Miles <_______ _______.____> writes:
Downs wrote:
 I presume this is a typo, but I can't for the life of me figure out what
 it's supposed to be.

Yes, it was... :-P read: This is where you should first aim your laser. I tried to make a joke... you usually kill bugs with insecticide or slippers (or a little programming), but D's const was like using a huge laser beam to exterminate a small misfeature of C++... Since you are using a laser to kill bugs (i.e. a major redesign), aim it first on those bugs that are really annoying us way back from C++.
Sep 10 2007
prev sibling parent eao197 <eao197 intervale.ru> writes:
On Tue, 11 Sep 2007 04:50:32 +0400, Miles <_______ _______.____> wrote:

 Walter Bright wrote:
 Const, final, invariant, head const, tail const, it's grown into a
 monster. It tries to cover all the bases, but in doing so is simply not
 understandable.

I was going to say that, that D's const sucks, many months ago, but didn't because I was afraid being regarded as a troll. Its not that I like const-less languages like Java (in fact, I hate them), but I think that C++'s const, with all its problems, was the right answer (for that time). It fixes the problem without putting much burden onto the programmer. D's const puts way too much burden onto the programmer, and gives very little back. Too many different combinations of const, final, invariant, pointers and references (and the fact that in D you don't immediately know if a type "T" is a reference of a value, specially while writing templates, adds to this confusion) with confusing semantics when combined (with a few special cases and lack of orthogonality). When the new const was being designed, I told in this list: "Don't try to fix what ain't broken". What I meant was: Don't try to do a new const just because you want a better const than C++s. Many people dislike C++s const, but are unable to point exactly why it is wrong. Most people doesn't even understand C++'s const, and try to get more from it than it was actually intended to provide (that is the whole point behind the new D's invariant). Sometimes people give use-cases to exemplify (STL's need for a difference between iterator<> and const_iterator<> is the first thing that comes to my mind), but then you ask what a language could provide to fix this, they don't know. This is where you should aim first you laser. When I first saw D, I was amazed with the possibilities. Now I'm somewhat scared. Perhaps one of the things that are scaring me is just like you said: there is a (at least one) monster in the language.

I'm totaly agree with all you have said. -- Regards, Yauheni Akhotnikau
Sep 11 2007
prev sibling next sibling parent Myron Alexander <someone somewhere.com> writes:
Walter Bright wrote:
 o  tail const of a struct would have to be done by making the struct a 
 template:
 
   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const
 

Walter, how are references handled and can you create a tail const of a reference? Which of the following are correct syntax and how is the "constness" defined: class SomeClass { some constructors some variables } const SomeClass x = new SomeClass (); SomeClass const(x) = new SomeClass (); SomeClass x = const new SomeClass (); SomeClass x = new const SomeClass (); (anything I missed out?) How can I create a tail const such that the reference is immutable but the public instance variables and public methods are mutable? What about arrays? Regards, Myron.
Sep 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

On 9/11/07, Bruno Medeiros <brunodomedeiros+spam com.gmail> wrote:
 'invariant' = constant
 'const' = read-only

It's simple, but - I would argue - the wrong choice. A better choice (in my humble opinion) would be const = constant readonly = read-only If it's not too late to change the keywords, that, to me, would be the way to go.
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

On 9/11/07, Janice Caron <caron800 googlemail.com> wrote:
 It's simple, but - I would argue - the wrong choice. A better choice (in
 my humble opinion) would be

 const = constant
 readonly = read-only

 If it's not too late to change the keywords, that, to me, would be the way
 to go.

By which I mean, if it's at all feasable to consider ditching the keyword "invariant" altogether, and introducing a new keyword "readonly", then let's do it. "readonly" should indicate a read-only view of data which someone else might modify, and "const" should imply that no-one can modify it ever, no way, nohow. I'm not quite sure why no one before now has suggested using "readonly" to mean read-only and "const" to mean constant, but seems kind of a no-brainer to me. You know - calling a thing what it is, instead of something it's not. I know I'd be dead confused if int meant float, for example.
Sep 11 2007
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Walter Bright wrote:
 Const, final, invariant, head const, tail const, it's grown into a
 monster. It tries to cover all the bases, but in doing so is simply not
 understandable.

Does that mean I'm a genius, or insane? :P
 [snip]
 
 o  no more final

Not 100% sure what you mean by "no more". But then, "final" was always kind of like the appendix of the new const system; I always viewed it as something of a side issue since it didn't affect the type of declarations.
 o  const and invariant now mean "fully const" and "fully invariant",
 where fully means "head and tail combined":
 
 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed

Ok.
 o  const and invariant are transitive. There is no way to specify a
 (pointer to)(const pointer to)(mutable int).
 
 o  tail invariant for an array or pointer type can be done using the
 existing syntax:
 
  invariant(char)[] s;   // string, i.e. an array of invariant chars
  const(T)* p;  // pointer to tail const T

This makes me happy, because although I understood the what, I never could grasp the why of "invariant(char)[]" meaning that you couldn't change the elements of the array.
 o  tail const of a struct would have to be done by making the struct a
 template:
 
   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const

This is... not so good. I'll come back to this...
 o  one can construct a template to generically produce tail const or
 tail invariant versions of a type.

This makes me rather nervous; it seems like a bad thing that we can no longer do tail-const, and have to resort to wrapping templates to do it. I mean, in the end, you can make a const version of any type by replacing all fields with read-only properties; but then you can't cleanly cast between them.
 o  it will be illegal to attempt to change the key value of a foreach
 loop, but the compiler will not be able to diagnose all attempts to do so

I assume this means that for built-in types, instead of being ( ref key, ref value ; aggregate ) We'll have ( const ref key, ref value ; aggregate ) If so; I'll be happy about that. So long as I can do evil cr*p like: ( ref idx, ref chr ; string.foo ) ++idx; Why, you ask? Er, you don't wanna know... :P
 o  static const/invariant means the initializer is evaluated at compile
 time. non-static const/invariant means it is evaluated at run time.

Ok.
 o  no initializer means it is assigned in the corresponding constructor.

Sounds good.
 o  const/invariant declarations will always allocate memory (so their
 addresses can be taken)

I guess this is good from a consistency standpoint.
 o  So, we still need a method to declare a constant that will not
 consume memory. We'll co-opt the future macro syntax for that:
 
     macro x = 3;
     macro s = "hello";

The syntax doesn't *really* speak to me; honestly, I was expecting you to use "alias" before I scrolled down and saw "macro". Thing is, if this works for arbitrary expressions, does that mean this will be possible: macro string = char[]; My main problem with this is the loss of tail const on structs/classes. Others have already listed several useful things that you won't be able to do because of this, so I won't bother with that. This problem seems to be unique to structs and classes. This is because every other reference type is written in such a way that you can "split" the type in such a way as to get tail-const semantics. For example: const(char[]) versus const(char)[]. This can't be done with structs or classes since their types are single identifiers. So what we need is some way to write that split. Really, what we're saying is "I don't want *this* to be const, but I do want what it references to be const". Back when I was writing my article on the upcoming const changes, I gave each kind of const a different, longer name to help differentiate them; the names I gave const and invariant were "reference const" and "reference immutable". So, stealing a little bit of C++ syntax, what about "const& T" being a tail-const version of some struct or class T? Since this could potentially introduce synonyms in other places, I would also propose the following: const& int a; // Error: cannot use reference const on an atomic type const& int* b; // Error: equivalent to const(int)* struct V { int a; } const& V c; // Warning: V does not have any references struct R { int* a; } const& R d; // OK class C {} const& C e; // OK Yes, this means there is a distinction between structs, classes and "everything else". But honestly, I don't think we can have tail const without this distinction. Maybe disallowing const& on arrays and pointers isn't a good idea; but I do think we need some kind of tail-const for structs and classes. Thoughts? -- Daniel
Sep 11 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Daniel Keep wrote:
  > This problem seems to be unique to structs and classes.  This is because
 every other reference type is written in such a way that you can "split"
 the type in such a way as to get tail-const semantics.  For example:
 const(char[]) versus const(char)[].  This can't be done with structs or
 classes since their types are single identifiers.

I came to the same conclusion, the basic problem is the inability to put a * or [] outside the () of const.
 So what we need is some way to write that split.  Really, what we're
 saying is "I don't want *this* to be const, but I do want what it
 references to be const".

Exactly.
 So, stealing a little bit of C++ syntax, what about "const& T" being a
 tail-const version of some struct or class T?  Since this could
 potentially introduce synonyms in other places, I would also propose the
 following:
 
   const& int a; // Error: cannot use reference const on an atomic type
   const& int* b; // Error: equivalent to const(int)*
   struct V { int a; }
   const& V c; // Warning: V does not have any references
   struct R { int* a; }
   const& R d; // OK
   class C {}
   const& C e; // OK
 
 Yes, this means there is a distinction between structs, classes and
 "everything else".  But honestly, I don't think we can have tail const
 without this distinction.  Maybe disallowing const& on arrays and
 pointers isn't a good idea; but I do think we need some kind of
 tail-const for structs and classes.
 
 Thoughts?

In D when we write: Foo f; we are declaring a reference to a Foo. The problem, as you mentioned is the inability to seperate the reference from the Foo for purposes of declaring const, so... I was initially thinking that for classes we might write: class Foo { int a; } const(Foo)& pFoo; //pFoo can change, pFoo.a cannot Where the & doesn't introduce another reference or level of indirection, in fact it does _nothing_ at all except allowing us to place it outside the () of const. Meaning, that these two declarations would in fact be identical: Foo& pFoo; Foo pFoo; which will no doubt bother some people, perhaps a lot of people? The first form could be made illegal, after all it's pointless(right?) to take a reference to a reference. Alternately, and I think I might prefer this solution, perhaps _adding_ something to the const() is the solution. eg. class Foo { int a; } const(*Foo) pFoo; //pFoo can change, pFoo.a cannot As in, were saying "the value of Foo is const", therefore implying the reference is not. As for structs, as someone else has mentioned there isn't really any difference between making a struct variable const and making all members of that struct const, without a pointer or reference they are the same thing. So, saying: struct Bar { int a; Foo pFoo; } const(Bar) bar; Is perfectly sufficient in that case. The difference occurs when you want to make _some_ of the members consts and not the others, typically you want to make references/pointers const, or tail const. The syntax: struct Bar { int a; Foo pFoo; } const(Bar)& bar; doesn't make the same sense for structs because there is no reference involved (as many programmers would expect upon reading that). Neither does the * syntax I proposed above: struct Bar { int a; Foo pFoo; } const(*Bar) bar; because that would logically make all members of the struct const, and we get that already. Assuming there is even a requirement to make select members of a struct (initially declared without const) const (if there isn't we have no problem) then using a template seems the most sensible solution. It would also allow you to apply const or tail const where appropriate. eg. From this initial struct: struct Bar { int a; //we want this to be const Foo pFoo; //we want this to be tail const } Our template generates this: struct BarConst { const int a; const(*Foo) pFoo; } Regan
Sep 11 2007
parent Regan Heath <regan netmail.co.nz> writes:
Regan Heath wrote:
 Assuming there is even a requirement to make select members of a struct 
 (initially declared without const) const (if there isn't we have no 
 problem) then using a template seems the most sensible solution.  It 
 would also allow you to apply const or tail const where appropriate. eg.

Talking to myself, a sure sign of mental instability ;) The same applies to making select members of a class const. I think it's important to remember that we're talking about _modifying_ an existing class/struct definition _adding_ const where it was not initially. This would only be required in cases where: 1. you need different const rules for instances of the same class/struct in the same version of the code. 2. you need to const correct a 3rd party API. In any other case you can simply apply const to the class/struct definition directly, no need for a template to do it. eg. struct Something { const(*Foo) pFoo; const int a; int b; } There will be no need to _modify_ this with a template because it will always need to have a mutable reference to a const Foo and that requirement won't be different for some instances and not others. It may change in future versions of the code, but in that case a simple change to the struct definition is all you need, not a template. I think the ability to tail const a class reference with something like: class Foo { int a; } const(*Foo) pFoo; Handles 99% of the cases which we should realistically be worried about. Regan
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

On 9/11/07, Derek Parnell <derek nomail.afraid.org> wrote:
 In other words, if I have a struct with three members, each of a different
 type, I need to code ...

 struct S3(T, U, V)
 {
    T member1;
    U member2;
    V member3;
 }

 S3!(const(int), const(float), const(bool));

 and so on for 4, 5, 6, .... 23 member structs.

 I'm sure I'm misunderstanding you, because this is really silly.

I don't think you're misunderstanding. I think that's what Walter is saying. But here's another idea. If it were allowable that (1) an alias template parameter could accept a type constructor, and (2) "auto" were accepted as a do-nothing type constructor then you would be able to do this: struct S(alias X) { X(int)* pi; X(float)* pf; X(double)* pd; }; S(const) k; // k's members are tail-const S(auto) m; // m's members are mutable
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

I always think of "read only" as meaning "a read only VIEW". Examples
abound: e.g. opening a file in read-only mode. Even the read-only attribute
of a file (which you can change).

Still - another keyword you might consider appropriate to the meaning "read
only view" is "in". Now there's a thought;

Oh - and here's something else I'd like to be able to do. Instead of:

 class C
 {
     private int my_n;
     public int n() { return my_n; }

     /* all my private code refers to my_n */
 }

I'd like to be able to do

 class C
 {
     (privately read-write but publicly read-only) int n;

     /* all my private code refers to n */
 }

That would be cool.
Thanks
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

 Therefore let's invent some new keywords, ones that are unlikely to clash
 with user declared identifiers ...

Coined words! Wow - now there's a thought! brillig = invariant data slithy = head const mimsy = read-only view ...with apologies to Lewis Carroll And that would give us lots of other lovely keywords to play with! Oh my - frumious, vorpal, uffish, whiffling, tulgey. The possibilities are boundless! O frabjous day! /* This post is in jest */
Sep 11 2007
prev sibling next sibling parent eao197 <eao197 intervale.ru> writes:
On Mon, 10 Sep 2007 23:15:09 +0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 o  const and invariant are transitive. There is no way to specify a  
 (pointer to)(const pointer to)(mutable int).

What's wrong with such pointers and why they are disabled by language? -- Regards, Yauheni Akhotnikau
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

On 9/11/07, Nicolai Waniek <nospam inval.id> wrote:
 Apart from that it would cover your problem
 with "internal RW-access, external R-access only".

I think you may have missed the point there. We can already do that with D. I /have/ no problem with "internal RW-access, external R-access only". There is no problem to solve. Nonetheless, it would still be cool if there were a new keyword or some other way to mean: privately_readwrite_but_publicly_readonly int x; See? I already know how to do it without the "privately_readwrite_but_publicly_readonly" keyword.
Sep 11 2007
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
Overall, I like this solution MUCH better than before.  I have some points 
to make, but let me say thanks for working on this and taking everyone's 
opinion into account.

"Walter Bright" wrote
 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.

Hear hear.
 o  const and invariant now mean "fully const" and "fully invariant", where 
 fully means "head and tail combined":

 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed

What about const(int) x = 0? I'm guessing the same as const int x = 0? Is it safe to say that const(T) x is equivalent to const T x? What about classes?
 o  tail const of a struct would have to be done by making the struct a 
 template:

   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const

I didn't see it brought up by anyone else, so I'll ask. Did you mean: struct S(T) { T * member; } ??? Because I always thought tail const had to do with pointers. If not, can someone please explain the difference between the two structs in Walter's example?
 o  one can construct a template to generically produce tail const or tail 
 invariant versions of a type.

This prevents casting, which from what I can interpret from the previous 2.0 proposal (the monster), you could do. i.e. struct MyNiftyPointer(T) {T * ptr; int someOtherCoolValue;} void myGreatFunction(MyNiftyPointer!(const(int)) x) { // do something that doesn't modify *x.ptr } void test() { MyNiftyPointer!(int) p; // set up p ... myGreatFunction(p); // will this compile? } Not that I really care much. You may be able to implement an opImplicitCast that worked around this problem. Really, I only see this feature being useful for a smart-pointer/array type, so I'm pretty sure I'll never use it.
 o  So, we still need a method to declare a constant that will not consume 
 memory. We'll co-opt the future macro syntax for that:

     macro x = 3;
     macro s = "hello";

First, I don't like the syntax. I understand it's roots, as #define is now being replaced by macro, but #define seems a more descriptive keyword than macro when trying to define something as being something else. I agree with Daniel Keep when he said he expected alias. Second, I'm trying to understand what the real purpose of this is. Could someone define the different types of memory and why we need different declarations to put things in those different types? For example, "doesn't consume memory" doesn't make sense to me. The bytes gotta go somewhere! Third, how would you define constants that are not implicitly typed, like a structure? I can see this being useful for something like a configuration table, or a table of CRC values. -Steve
Sep 11 2007
next sibling parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Steven Schveighoffer wrote:
 Second, I'm trying to understand what the real purpose of this is.  Could 
 someone define the different types of memory and why we need different 
 declarations to put things in those different types?  For example, "doesn't 
 consume memory" doesn't make sense to me.  The bytes gotta go somewhere!

For numeric constants which are known at compile time, there's not necessarily a location in the executable's data-sections where they are stored; they may just be incorporated as immediate values into the machine instructions, and/or folded into other expressions at compile time. (For string constants known at compile time they are still going to have to be stored in the executable image.) However, if you ever take the address of such a constant it needs to actually be stored. For a regular declaration of a const variable, the compiler can optimize away the stored version if it can determine that the address is never taken, but this often isn't possible (think of class member variables - the compiler can't guarantee that no client code will ever take the address). On the other hand it would be illegal to take the address of a macro. Thanks, Nathan Reed
Sep 11 2007
next sibling parent reply 0ffh <spam frankhirsch.net> writes:
Janice Caron wrote:
 I dislike your macro idea, because I want my consts to have a type. I
 /like/ type safety, and I want to keep it. I want my constants to be
 of the type I declare them to be, not just a piece of text.

Hmmmm... if I got that right, macros will definitely *not* be "pieces of text", but abstract syntax trees. So, if you'd do "macro x=5;" (or whatever the macro syntax will be), and you try `char[] s="abc"~x;` you'll get a nice, clean type error. As you liek it! :) regards, Frank
Sep 11 2007
next sibling parent Regan Heath <regan netmail.co.nz> writes:
Janice Caron wrote:
 Hmmmm... if I got that right, macros will definitely *not* be "pieces
 of text", but abstract syntax trees.

Macros have their place. I'm not knocking them. But they're not the right mechanism for declaring constants. On the other hand, const Type name = value; is. (replace const with static if necessary - see earlier in thread). Of course, it really should be invariant Type name = value; with the current keywords, though if we switch from invariant/const to const/readonly that mistake will likely disappear.
 So, if you'd do "macro x=5;" (or whatever the macro syntax will be),
 and you try `char[] s="abc"~x;` you'll get a nice, clean type error.

Looks the same as C to me. #define x 5 strcat(s,"abc"); strcat(s,x); /* compile error */ That doesn't count as strong typing.

You're jumping to conclusions here. I doubt _very_ much that Walter plans to implement macro like #define in C. Especially after all the trouble he went through to remove the preprocessor completely. Regan
Sep 11 2007
prev sibling parent 0ffh <spam frankhirsch.net> writes:
Janice Caron wrote:
 So, if you'd do "macro x=5;" (or whatever the macro syntax will be),
 and you try `char[] s="abc"~x;` you'll get a nice, clean type error.


Isn't!!!111!!
 #define x 5
 strcat(s,"abc");
 strcat(s,x); /* compile error */
 
 That doesn't count as strong typing.

It is! You'll be able to do "typeof(x)" and probably get "int"... :-) Regards, Frank
Sep 11 2007
prev sibling parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Janice Caron wrote:
 On 9/11/07, Nathan Reed <nathaniel.reed gmail.com> wrote:
 For a regular declaration of a const variable, the
 compiler can optimize away the stored version if it can determine that
 the address is never taken, but this often isn't possible (think of
 class member variables - the compiler can't guarantee that no client
 code will ever take the address).

Then just decree that it be illegal to take the address of a const class member variable. Problem solved.

That places totally unnecessary restrictions on the programmer...there's absolutely no reason why you shouldn't be able to take the address of something const.
 I dislike your macro idea, because I want my consts to have a type. I
 /like/ type safety, and I want to keep it. I want my constants to be
 of the type I declare them to be, not just a piece of text.

It's not /my/ macro idea. :) (For the record, I don't really like the idea of using the macro keyword for this, either.) Although, as other people have pointed out, this doesn't break type safety, just makes the type not explicitly stated in the code. Thanks, Nathan Reed
Sep 11 2007
next sibling parent Regan Heath <regan netmail.co.nz> writes:
Nathan Reed wrote:
 Janice Caron wrote:
 On 9/11/07, Nathan Reed <nathaniel.reed gmail.com> wrote:
 For a regular declaration of a const variable, the
 compiler can optimize away the stored version if it can determine that
 the address is never taken, but this often isn't possible (think of
 class member variables - the compiler can't guarantee that no client
 code will ever take the address).

Then just decree that it be illegal to take the address of a const class member variable. Problem solved.

That places totally unnecessary restrictions on the programmer...there's absolutely no reason why you shouldn't be able to take the address of something const.
 I dislike your macro idea, because I want my consts to have a type. I
 /like/ type safety, and I want to keep it. I want my constants to be
 of the type I declare them to be, not just a piece of text.

It's not /my/ macro idea. :) (For the record, I don't really like the idea of using the macro keyword for this, either.) Although, as other people have pointed out, this doesn't break type safety, just makes the type not explicitly stated in the code.

In that way it's a bit like 'auto' Regan
Sep 11 2007
prev sibling parent reply Nathan Reed <nathaniel.reed gmail.com> writes:
Janice Caron wrote:
 class C
 {
     const int n = 42;
     /* other stuff */
 }
 
 Suppose you new a thousand of those things. What's the point of making
 a thousand ints, all the same. Why not just do:
 
 class C
 {
     static const int n = 42;
     /* other stuff */
 }
 
 and only consume four bytes.
 
 I argue that per-class-instance const variables is so silly, that you
 might just as well let the compiler "disappear" them.

Fine for things that are known at compile time. But it's also possible to have const variables whose values are not known till runtime. For instance void foo (int bar) { const int baz = bar * 47; ... } or class instance variables: class Foo { const int bar; this (int baz) { this.bar = baz; } } The variable needs to be initialized but is then not supposed to change over its lifetime.
 Although, as other
 people have pointed out, this doesn't break type safety, just makes the
 type not explicitly stated in the code.

And this is different to #define how exactly?

#define does text substitution. In C, you could write something like this: #define THREE 3 float pi = THREE.14159; With D macros that would not be allowed. Thanks, Nathan Reed
Sep 11 2007
parent Regan Heath <regan netmail.co.nz> writes:
Janice Caron wrote:
 On 9/11/07, Nathan Reed <nathaniel.reed gmail.com> wrote:
 #define does text substitution.  In C, you could write something like this:

 #define THREE 3
 float pi = THREE.14159;

 With D macros that would not be allowed.

True. But I want more than that. I want to be able to distinguish between const int x = 42; const uint x = 42;

No problem: macro x = 42; //int macro x = 42U; //uint macro x = 42L; //long macro x = 42UL; //ulong In addition: macro x = 2_147_483_648; //long See: "Decimal Literal" http://www.digitalmars.com/d/lex.html Regan
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/11/07, Nathan Reed <nathaniel.reed gmail.com> wrote:
 For a regular declaration of a const variable, the
 compiler can optimize away the stored version if it can determine that
 the address is never taken, but this often isn't possible (think of
 class member variables - the compiler can't guarantee that no client
 code will ever take the address).

Then just decree that it be illegal to take the address of a const class member variable. Problem solved. What's wrong with: class C { const int X = 42; /* not stored; address may not be taken */ static int Y = 42; /* consumes memory; address may be taken */ } I dislike your macro idea, because I want my consts to have a type. I /like/ type safety, and I want to keep it. I want my constants to be of the type I declare them to be, not just a piece of text.
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
 Hmmmm... if I got that right, macros will definitely *not* be "pieces
 of text", but abstract syntax trees.

Macros have their place. I'm not knocking them. But they're not the right mechanism for declaring constants. On the other hand, const Type name = value; is. (replace const with static if necessary - see earlier in thread). Of course, it really should be invariant Type name = value; with the current keywords, though if we switch from invariant/const to const/readonly that mistake will likely disappear.
 So, if you'd do "macro x=5;" (or whatever the macro syntax will be),
 and you try `char[] s="abc"~x;` you'll get a nice, clean type error.

Looks the same as C to me. #define x 5 strcat(s,"abc"); strcat(s,x); /* compile error */ That doesn't count as strong typing.
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/11/07, Janice Caron <caron800 googlemail.com> wrote:
 What's wrong with:

  class C
  {
      const int X = 42; /* not stored; address may not be taken */
      static int Y = 42; /* consumes memory; address may be taken */
 }

Of course, I actually meant: class C { const int X = 42; /* not stored; address may not be taken */ static const int Y = 42; /* consumes memory; address may be taken */ } The point being that for constant data to consume memory *per-class-instance* is silly.
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/11/07, Nathan Reed <nathaniel.reed gmail.com> wrote:

 Then just decree that it be illegal to take the address of a const
 class member variable. Problem solved.

That places totally unnecessary restrictions on the programmer...there's absolutely no reason why you shouldn't be able to take the address of something const.

Remember, this is a special circumstance - /as a class member variable/ Consider: class C { const int n = 42; /* other stuff */ } Suppose you new a thousand of those things. What's the point of making a thousand ints, all the same. Why not just do: class C { static const int n = 42; /* other stuff */ } and only consume four bytes. I argue that per-class-instance const variables is so silly, that you might just as well let the compiler "disappear" them.
 It's not /my/ macro idea. :)

My apologies. I lost track of to whom I was replying.
 Although, as other
 people have pointed out, this doesn't break type safety, just makes the
 type not explicitly stated in the code.

And this is different to #define how exactly?
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/11/07, Janice Caron <caron800 googlemail.com> wrote:
 I argue that per-class-instance const variables is so silly, that you
 might just as well let the compiler "disappear" them.

Otherwise I'll end up doing class C { enum : int { n = 42; } } because I want my type specified at the point of declaration.
Sep 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/11/07, Nathan Reed <nathaniel.reed gmail.com> wrote:

 void foo (int bar)
 {
      const int baz = bar * 47;
      ...
 }

That's not a class member variable. That's a local variable. It's a different animal. In the case of local variables, the compiler can decide whether or not memory storage is required.
 class Foo
 {
      const int bar;
      this (int baz)
      {
          this.bar = baz;
      }
 }

 The variable needs to be initialized but is then not supposed to change
 over its lifetime.

That can be done in different ways. Walter was originally going to allow final for that purpose, but final is now dropped. I think that the above example will not compile under Walter's new plans, because const is unmodifiable even in a constructor. Of course, I could be wrong. One way to rewrite that, though, would be class Foo { private int bar_; this (int baz) { bar_ = baz; } int bar() return bar_; }
 #define does text substitution.  In C, you could write something like this:

 #define THREE 3
 float pi = THREE.14159;

 With D macros that would not be allowed.

True. But I want more than that. I want to be able to distinguish between const int x = 42; const uint x = 42; etc. And if I want type deduction, of course the following would work just fine: const x = 42;
Sep 11 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/11/07, Regan Heath <regan netmail.co.nz> wrote:
 No problem:

 macro x = 42;   //int
 macro x = 42U;  //uint
 macro x = 42L;  //long
 macro x = 42UL; //ulong

Maybe it's just a gut feeling thing. const ulong x = 42; /* obvious to newbies what it does */ macro x = 42UL; /* looks weird */
Sep 11 2007
prev sibling next sibling parent Myron Alexander <someone somewhere.com> writes:
This is a mess!

The examples given do not indicate how references will be taken care of 
nor do they indicate how to deal with templated functions/methods that 
change behaviour based on type.

In this method, I want to make toValue final (like Java) if it is a 
reference, and const if it is a primitive/structure, how do I do that?

void doSomthing(T) (*declare final* T toValue) {

    *whole bunch of common code*

    static if (is(T : someobject)) {
      work with someobject
    }

    static if (is(T == int)) {
      work with int
    }

    static if (is(T == char[])) {
      work with char[]
    }

    ...

    *whole bunch of other common code*
}

In D, there are five main type classes:

1. Primitives (including pointers)
2. Structs - value type
3. Classes - reference semantics
4. Arrays - reference semantics
5. Delegates/Function pointers

Until constness can deal with all types simply (and elegantly), then it 
should not be introduced. This way leads to C++ and the kitchen sink 
approach. Java does well because it is not a kitchen sink.

Please demote D2.0 from the front-page and revert back to D1.0 so that 
new people are not confused with experimental versions.

Regards,

Myron.
Sep 11 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/10/07, Walter Bright <newshound1 digitalmars.com> wrote:
 o  const/invariant declarations will always allocate memory (so their
 addresses can be taken)

I'd hadn't really thought about this before. But now I have. I want to be able to *express the idea* that the value I am declaring shall never change. To my mind: const int NUM_DAYS_IN_WEEK = 7; successfully expresses that idea, and frankly, I whether or not NUM_DAYS_IN_WEEK consumes memory should not be my call. That is an implementation detail, and should be the compiler's problem, not mine.
 o  So, we still need a method to declare a constant that will not
 consume memory. We'll co-opt the future macro syntax for that:

Isn't that a bit like "register" or "inline"? Those keywords went the way of the dodo because it was decreed that the compiler could do a better job at figuring out what to inline or put in registers than the programmer ever could. Isn't this the same? Surely, the compiler can make the decision as to whether or not the value gets stored in memory. Why should I have to? If the program does not take the address, and if sizeof the variable is sufficiently small, then don't store in memory, otherwise do. If it's a library and you don't know what the calling code will do, let the compiler take its best guess. If it guesses wrongly, then either a few bytes of memory will have been consumed, or some jiggery pokery will be required at the call site. But either way, don't make the programmer choose.
 macro x = 3;
 macro s = "hello";

And bang goes type safety, right there!
Sep 11 2007
parent Nathan Reed <nathaniel.reed gmail.com> writes:
Janice Caron wrote:
 I want to be able to *express the idea* that the value I am declaring
 shall never change. To my mind:
 
 const int NUM_DAYS_IN_WEEK = 7;
 
 successfully expresses that idea, and frankly, I whether or not
 NUM_DAYS_IN_WEEK consumes memory should not be my call. That is an
 implementation detail, and should be the compiler's problem, not mine.

Agree. Thanks, Nathan Reed
Sep 11 2007
prev sibling next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright wrote:
 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.

I have not really been following the discussions thus far, but would still like to offer my uninformed view on things. (More questions than opinions actually) 1. I think one reason it is not understandable is the poor choice of keywords. My latest reiteration on the keywords (elsewhere in this thread): http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=58135 (This may sound like a silly issue, but the advantages to using descriptive names should not be underestimated. Imagine using banana/apple/orange instead of int/float/void.) 2. There are two distinct problems. Access control vs constantness of data. The old proposal doesn't really take advantage of that distinction. 3. For write access control, I don't see transitivity making as much sense. There are cases where you want write access to a certain depth. Certain members of a class need not be part of that class, and therefore not bound by the same non-mutability requirements. Robert Fraser has posted a few use cases too. What are the motivations for transitivity here? What problems does it solve? Are there alternative solutions to those problems? 4. Transitivity could still make sense for constant data (there are few cases were it makes sense to have for example a compile time rommable pointer pointing to mutable data). An alternative suggestion would be to make a better separation between constness and access control by making const intransitive and only referring to access control, i.e const would prevent modification access to data in the following forms. - writing though a pointer - modifying class fields - calling non-readonly methods on classes - changing element of an array - appending to an array This means, more or less the D 2.000 const but without transitivity, coupled with the recently suggested invariant proposal. Combine this with my wish to rename invariant->const, const->readonly: * readonly handle to access control * const refers to constness of the actual data * readonly would be intransitive * const would remain transitive data -- const -- transitive access -- readonly -- intransitive simple, right? :) The behavior of readonly would become (all of this should be deducible, so just skip this part): A readonly type prevents modifying access through that type. readonly int * a; // no writing allowed through a readonly int b; // readonly doesn't apply to non-reference types readonly Class c; // c can be reassigned but is a readonly reference readonly Struct d; // members are readonly, d isn't readonly int[] e; // e may change, e[] may not Structs are value types, so readonly doesn't apply to them. It does however apply it its members. struct S { int[] arr; String str; } readonly S s; Here, s.arr and s.str are readonly. This means that as far as readonly is concerned, a struct containing a reference to data behaves the same as a free reference to data. For arrays: readonly Class[] g; // readonly array of mutable classes readonly(Class)[] h; // mutable array of readonly classes readonly readonly(Class)[] i; // readonly array of readonly classes TransitiveReadonly!() is possible to implement. references to constant data are automatically readonly. const(int)[] j; // j is readonly Intransitivity allows all combinations of mutability [m] and immutability [i] for nested types: [m][m][m] int[][][] [i][m][m] readonly int[][][] [m][i][m] readonly(int[][])[] [m][m][i] readonly(int[])[][] [i][i][m] readonly(readonly(int[][]))[] [m][i][i] readonly(readonly(int[])[])[] [i][m][i] readonly readonly(int[])[][] [i][i][i] readonly(readonly(readonly(int[]))[])[] As far as I can see this resolves the issues with head/tail const and the issues with class references in the latest proposal. But I have probably missed many issues. -- Oskar
Sep 11 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Ok, seems my Oskar "agreing spree" has ended. :P

Oskar Linde wrote:
 TransitiveReadonly!() is possible to implement.
 

Are you sure? How would that work in general terms, for say, a class? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 12 2007
prev sibling parent charles <charlie d.com> writes:
Yes.  Const does suck.

Walter Bright wrote:

 Const, final, invariant, head const, tail const, it's grown into a 
 monster. It tries to cover all the bases, but in doing so is simply not 
 understandable.
 
 Andrei and Bartosz have spent some time together going back to square 
 one with what are we trying to achieve with const, and came up with 
 another proposal.
 
 What we are trying to achieve:
 
 a) utility for functional programming
 b) better modularity by self-documenting interfaces better
 c) be able to treat reference types as if they were value types (i.e. 
 strings should behave to the user like value types, even though they are 
 references)
 
 The insights they came up with were:
 
 1) final (i.e. 'head const') is not necessary for a, b or c. final is a 
 local thing, and not strictly necessary.
 
 2) tail const can be handled in other ways, read on
 
 So, what we are left with is:
 
 o  no more final
 
 o  const and invariant now mean "fully const" and "fully invariant", 
 where fully means "head and tail combined":
 
 const int x = 0;   // x is constant
 const int* p = &x;  // neither p nor *p can be changed
 const(int*) p = &x;  // neither p nor *p can be changed
 const(int)* p = &x;  // p can change, *p cannot be changed
 
 o  const and invariant are transitive. There is no way to specify a 
 (pointer to)(const pointer to)(mutable int).
 
 o  tail invariant for an array or pointer type can be done using the 
 existing syntax:
 
  invariant(char)[] s;   // string, i.e. an array of invariant chars
  const(T)* p;  // pointer to tail const T
 
 o  tail const of a struct would have to be done by making the struct a 
 template:
 
   struct S(T) { T member; }
   S!(int)   // tail mutable
   S!(const(int)) // tail const
 
 o  one can construct a template to generically produce tail const or 
 tail invariant versions of a type.
 
 o  it will be illegal to attempt to change the key value of a foreach 
 loop, but the compiler will not be able to diagnose all attempts to do so
 
 o  static const/invariant means the initializer is evaluated at compile 
 time. non-static const/invariant means it is evaluated at run time.
 
 o  no initializer means it is assigned in the corresponding constructor.
 
 o  const/invariant declarations will always allocate memory (so their 
 addresses can be taken)
 
 o  So, we still need a method to declare a constant that will not 
 consume memory. We'll co-opt the future macro syntax for that:
 
     macro x = 3;
     macro s = "hello";

Sep 12 2007