www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - about the new const regime

reply Daniel919 <Daniel919 web.de> writes:
(dmd 2.009)

What's the difference between "const T" and "invariant T", if T is a 
value type (builtin type or struct)?

Value types are always a copy and never refer to anything.
So a const(T) can't be changed.

Example:
typeid(string) == "invariant(char)[]" (default)

string s = "bla";
s[0] = 'x'; //Error, cannot change a invariant(char)
But then, where is the difference to const(char)?

If there really is no difference, shouldn't string be "const(char)[]" 
then? And maybe invariant(T) should be an error, when T is a valuetype?


About enum:
Instead of the current "abuse" of enum to also define values that take 
no space, I would like a new keyword, which can be used more general:

meta

metaprogramming <=> compile time programming
meta <=> compile time

meta x = 1; <=> meta int x = 1;
x is a compile time value of type int.

enum { red, green, blue } //<=>
meta red = 0, green = 1, blue = 2;


This could also help to make a better distinction:
class Foo { static int x; }
means x is not bound to an object. using static this(), x can be 
assigned at runtime

static if( ... ) ...
means evaluate the if at compile time

With the new keyword, it could be renamed to
meta if( ... ) ...

static <=> not bound to an object
meta <=> compile time


Best regards and happy new year,
Daniel
Jan 01 2008
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 1/1/08, Daniel919 <Daniel919 web.de> wrote:
 (dmd 2.009)

 What's the difference between "const T" and "invariant T", if T is a
 value type (builtin type or struct)?

Pretty much none. In both cases, the type is invariant in practice.
 If there really is no difference, shouldn't string be "const(char)[]"
 then? And maybe invariant(T) should be an error, when T is a valuetype?

You can do const x = 5; invariant x = 5; If the compiler is smart enough, in both cases, x will have type invariant(int). If the compiler doesn't try to be smart, then x will have type const(int) and invariant(int) respectively. Having two ways to say the same thing may seem redundant, but for reasons of generic programming, neither of them should be considered an error.
 enum { red, green, blue } //<=>
 meta red = 0, green = 1, blue = 2;

For those of us who support "real enums", those two are not equivalent, since "real enums" make no numerical value available to the programmer. You have no choice /but/ to use the enumerated names. But of course, for that very reason, many (myself included) have argued that a different keyword for manifest constants would be a good idea (possibly paving the way for "enum" to be retooled to make "real enums" in the future). "meta" is as good a name as any, although I think I like "manifest" better. I'd be happy with either. But right now, neither is on the table.
Jan 01 2008
prev sibling next sibling parent reply davidl <davidl 126.com> writes:
在 Wed, 02 Jan 2008 01:14:22 +0800,Daniel919 <Daniel919 web.de> 写道:

 (dmd 2.009)

 What's the difference between "const T" and "invariant T", if T is a  
 value type (builtin type or struct)?

 Value types are always a copy and never refer to anything.
 So a const(T) can't be changed.

 Example:
 typeid(string) == "invariant(char)[]" (default)

 string s = "bla";
 s[0] = 'x'; //Error, cannot change a invariant(char)
 But then, where is the difference to const(char)?

string s = "bla"; s = "abc"; // compile, you can reassign to an invariant array with another invariant array, but you can't change it partially. s[0] = 'x'; //error as you said
 If there really is no difference, shouldn't string be "const(char)[]"  
 then? And maybe invariant(T) should be an error, when T is a valuetype?


 About enum:
 Instead of the current "abuse" of enum to also define values that take  
 no space, I would like a new keyword, which can be used more general:

 meta

 metaprogramming <=> compile time programming
 meta <=> compile time

 meta x = 1; <=> meta int x = 1;
 x is a compile time value of type int.

 enum { red, green, blue } //<=>
 meta red = 0, green = 1, blue = 2;


 This could also help to make a better distinction:
 class Foo { static int x; }
 means x is not bound to an object. using static this(), x can be  
 assigned at runtime

 static if( ... ) ...
 means evaluate the if at compile time

 With the new keyword, it could be renamed to
 meta if( ... ) ...

 static <=> not bound to an object
 meta <=> compile time


 Best regards and happy new year,
 Daniel

-- 使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
Jan 02 2008
parent reply Daniel919 <Daniel919 web.de> writes:
 The difference is :
 
 string s = "bla";
 s = "abc";     // compile, you can reassign to an invariant array with 
 another invariant array, but you can't change it partially.
 s[0] = 'x'; //error as you said

But the same is true for a const(char)[]. const(char)[] s = "bla"; s = "abc"; s[0] = 'x'; //error const char[] s = "bla"; s = "abc" //error invariant char[] s = "bla"; s = "abc" //error
Jan 02 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Daniel919" wrote
 The difference is :

 string s = "bla";
 s = "abc";     // compile, you can reassign to an invariant array with 
 another invariant array, but you can't change it partially.
 s[0] = 'x'; //error as you said

But the same is true for a const(char)[]. const(char)[] s = "bla"; s = "abc"; s[0] = 'x'; //error const char[] s = "bla"; s = "abc" //error invariant char[] s = "bla"; s = "abc" //error

The difference is subtle. invariant means "nothing can change this". const means "I can't change this". So for a const array, for instance: char[] s = "bla".dup; const(char)[] c = s; // ok invariant(char)[] i = s; // error invariant(char)[] i2 = c; // error This is an error because since you could potentially change i by changing s, you can't assign s to i or c to i without an explicit cast. The explicit cast tells the compiler you are ensuring that s will no longer be changed by anything: invariant(char)[] i3 = cast(invariant(char)[])s; // ok This is ok, but you can no longer change s. If you do, the behavior is undefined. Do you see the difference now? In most cases, if you are initializing a const variable with a literal, or a new() statement, you can change the modifier to invariant because logically, nothing can change that variable. It would be nice if the compiler automatically did this for you, but I don't think this is the case today. -Steve
Jan 02 2008
next sibling parent reply Daniel919 <Daniel919 web.de> writes:
 The difference is subtle.  invariant means "nothing can change this".  const 
 means "I can't change this".

So I can read this only from this pointer/ref. Ok, I already posted some thoughts about it under this topic.
 So for a const array, for instance:
 
 char[] s = "bla".dup;
 const(char)[] c = s; // ok
 invariant(char)[] i = s; // error
 invariant(char)[] i2 = c; // error
 
 This is an error because since you could potentially change i by changing s, 

char[] s = "bla".dup; const(char)[] c = s; writefln( c ); //bla s = "new".dup; writefln( c ); //bla So c got a copy of s. But then, how can I change c by using s? If I can't, then I see no difference to invariant(char)[]. If c was a reference to s, then it would make sense to distinct between: 1. can't change the data of c via c = const(char)[] 2. data of c will never change = invariant(char)[]
 you can't assign s to i or c to i without an explicit cast.  The explicit 
 cast tells the compiler you are ensuring that s will no longer be changed by 
 anything:
 
 invariant(char)[] i3 = cast(invariant(char)[])s; // ok
 
 This is ok, but you can no longer change s.  If you do, the behavior is 
 undefined.
 
 Do you see the difference now?  In most cases, if you are initializing a 
 const variable with a literal, or a new() statement, you can change the 
 modifier to invariant because logically, nothing can change that variable. 
 It would be nice if the compiler automatically did this for you, but I don't 
 think this is the case today.
 
 -Steve 
 
 

Jan 02 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Daniel919" wrote
 So for a const array, for instance:

 char[] s = "bla".dup;
 const(char)[] c = s; // ok
 invariant(char)[] i = s; // error
 invariant(char)[] i2 = c; // error

 This is an error because since you could potentially change i by changing 
 s,

char[] s = "bla".dup; const(char)[] c = s; writefln( c ); //bla s = "new".dup; writefln( c ); //bla So c got a copy of s. But then, how can I change c by using s? If I can't, then I see no difference to invariant(char)[]. If c was a reference to s, then it would make sense to distinct between: 1. can't change the data of c via c = const(char)[] 2. data of c will never change = invariant(char)[]

c is not a copy of s or a reference to s. c references the same data that s references. When you change s to reference different data, it does not change c to reference this data. i.e. after the assignment, s.ptr != c.ptr. Maybe this example will help: int x = 5; int *p = &x; int *p2 = p; writefln(*p, *p2); // outputs 55 int y = 6; p = &y; writefln(*p, *p2); // outputs 65 Assigning p to &y is similar to your code assigning s to "new".dup; Now, if you modified a piece of the data s is pointing to, then c will change. Try this: s[0..3] = "new"; This copies the data from the array on the right into the array on the left (I didn't yet compile this, so I'm not sure if it compiles as coded). -Steve
Jan 02 2008
parent Daniel919 <Daniel919 web.de> writes:
 c is not a copy of s or a reference to s.  c references the same data that s 
 references.  When you change s to reference different data, it does not 
 change c to reference this data.  i.e. after the assignment, s.ptr != c.ptr. 
 Maybe this example will help:
 
 int x = 5;
 int *p = &x;
 int *p2 = p;
 writefln(*p, *p2); // outputs 55
 
 int y = 6;
 p = &y;
 
 writefln(*p, *p2); // outputs 65
 
 Assigning p to &y is similar to your code assigning s to "new".dup;
 
 Now, if you modified a piece of the data s is pointing to, then c will 
 change.  Try this:
 
 s[0..3] = "new";
 This copies the data from the array on the right into the array on the left 
 (I didn't yet compile this, so I'm not sure if it compiles as coded).

s[0..3] = "new"; writefln( c ); //newt This made it clear, shows that c is not a copy of s. And as expected: c[0..3] = "new";//Error: slice c[cast(uint)0..cast(uint)3] is not mutable const(char)[] c = s; elements c refers to can't be changed via c invariant(char)[] i = s; elements i refers to will never change Thanks for your explanation.
Jan 02 2008
prev sibling parent Dan <murpsoft hotmail.com> writes:
Steven Schveighoffer Wrote:
 "Daniel919" wrote
 const char[] s = "bla";
 s = "abc" //error

 invariant char[] s = "bla";
 s = "abc" //error

The difference is subtle. invariant means "nothing can change this". const means "I can't change this".

The difference being that the compiler is allowed to optimize an invariant under the assumption that it won't ever *be* changed may entail caching results of evaluating it, and other similar optimizations. Lemma 1) accepting that our algorithms are incorrect if changed by influences outside our control. This lemma is inherent to all programs though. One cannot control the environment on which they run. Sure, we can perform sanity checks, but even bootloaders and OS's are run on emulators and can be influenced in unexpected ways. I would therefore suggest that everything that is const within an entire program is therefore inherently invariant for our purposes. Regards, Dan
Jan 02 2008
prev sibling parent reply Daniel919 <Daniel919 web.de> writes:
 What's the difference between "const T" and "invariant T", if T is a 
 value type (builtin type or struct)?
 
 Value types are always a copy and never refer to anything.
 So a const(T) can't be changed.

There is a little difference: struct St { T ptr2sth; } //T is a class or a pointer to sth const St st = St(&sth); st.ptr2sth is an invariant ptr to: sth that might change invariant St st = St(&someobj); st.ptr2sth is an invariant ptr to: sth that will never change However, const(valuetype) is always equivalent to invariant(valuetype) That's also why: const Foo* ptr2foo; a const ptr to a const Foo <=> an invariant ptr to a const Foo So the distinction between const and invariant is only important, when it comes to the question, what a ptr is pointing to: 1. const(Foo)* ptr2constfoo = &foo; 2. invariant(Foo)* ptr2invariantfoo = &foo; My opinion is that const reads like: this will never change I know there is no difference between const,invariant,readonly,immutable,... in the English language. And that in C++ const also doesn't mean "this will never change". But I think, the best fitting one to express what we really want in case 1 is: readonly Because it stands for what we intend to do: Use this ptr to read Foo only. So a ptr to sth readonly means: readonlyview AND NOT: ptr to readonly data (just like function params in,inout,out express what we intend to do) Of course one could argue that readonly reads like: this Foo is readonly and thus will never change. But for const it's the other way round: I argue that const reads like: this Foo is const and thus will never change And not that it means: we can only read this Foo from this ptr (but it might get changed from somewhere) It's the problem that all these words are synonyms in the English language. But I can better live with: readonly(T)* to mean: ptr that is used to read only and NOT to mean: ptr to sth that will never change than const(T)* to mean: ptr that is used to read only and NOT to mean: ptr to sth that will never change This way const T foo = ...; would mean that nothing of foo will ever change. With the current system, you would have to use invariant. But then, for consistency, you should not use "const x = 1;" but "invariant x = 1;" So you would mostly use invariant, and const only where you want to say: ptr that is used to read only But then it would be easier if we change: const to readonly invariant to const You can give it a try, just compile like: cat ./test.d | sed -e 's/const/invariant/g;s/readonly/const/g' > test2.d && dmd.exe test2.d | sed -e 's/const/readonly/g;s/invariant/const/g' && ./test2 Best regards, Daniel
Jan 02 2008
next sibling parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Wed, 02 Jan 2008 14:25:59 -0000, Daniel919 <Daniel919 web.de> wrote:
 My opinion

 is that const reads like: this will never change
 I know there is no difference between  
 const,invariant,readonly,immutable,... in the English language.
 And that in C++ const also doesn't mean "this will never change".

leave thoughts about syntax to one side until the semantics are thoroughly clear to everyone.
Jan 02 2008
parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Bruce Adams wrote:
 On Wed, 02 Jan 2008 14:25:59 -0000, Daniel919 <Daniel919 web.de> wrote:
 My opinion

 is that const reads like: this will never change
 I know there is no difference between 
 const,invariant,readonly,immutable,... in the English language.
 And that in C++ const also doesn't mean "this will never change".

leave thoughts about syntax to one side until the semantics are thoroughly clear to everyone.

I fully agree. The actual keywords are secondary to the semantics (of course), but in this case, I think we may experience something similar to the Stroop effect in psychology. Imagine trying to explain the colors of a painting to someone, but insisting on using the word "red" to mean blue and "blue" to mean red. Of all the "synonyms" above, "constant" is the one understood by most people. Unfortunately, the D keyword "const" doesn't mean constant -- it means something different and another keyword means constant. This fact can't be very helpful in making the semantics thoroughly clear to everyone. :) -- Oskar
Jan 04 2008
prev sibling parent Sascha Katzner <sorry.no spam.invalid> writes:
Daniel919 wrote:
 But then it would be easier if we change:
 const to readonly
 invariant to const

I like this idea to exchange the meaning of const and invariant *VERY* much. Personally I prefer "invariant" to "readonly", but this is negligible. The very nice side effect of this change would be, that we could kick the recent enum changes (which practically nobody likes *eg*), in favor to the much more intuitive and backward compatible "const PI = 3.14" form for manifest constants. LLAP, Sascha Katzner
Jan 04 2008