Thread overview
Difference betwee storage class and type (invariant/const)?
Jun 18, 2007
Robert Fraser
Jun 18, 2007
Kirk McDonald
Jun 18, 2007
Robert Fraser
Jun 19, 2007
Christian Kamm
Jun 19, 2007
Christian Kamm
Jun 19, 2007
Robert Fraser
Jun 20, 2007
Daniel Keep
Jun 20, 2007
Robert Fraser
Jun 19, 2007
Daniel919
June 18, 2007
Can someone explain to me what the difference is between a storage class and a type is, with regards to const and invariant? For example, what do these do differently?

invariant int foo;
invariant(int) foo;

...? I know this is probably second-nature to people with C++ backgrounds, but I find the documentation quite confusing, as I've only really worked in Java before.
June 18, 2007
Robert Fraser wrote:
> Can someone explain to me what the difference is between a storage class and a type is, with regards to const and invariant? For example, what do these do differently?
> 
> invariant int foo;
> invariant(int) foo;
> 
> ...? I know this is probably second-nature to people with C++ backgrounds, but I find the documentation quite confusing, as I've only really worked in Java before.

Between those? Nothing. The difference is here:

invariant int* foo;
invariant(int)* bar;

The former is equivalent to:
invariant(int*) foo;

That is, when it is used as a storage class, it applies to the whole type.

-- 
Kirk McDonald
http://kirkmcdonald.blogspot.com
Pyd: Connecting D and Python
http://pyd.dsource.org
June 18, 2007
Ah, I see, thanks! So the difference is mainly when it applies to a naturally-reference type such as an object?

"const(Foo) bar" only the data inside the object is const, while with "const Foo bar", both the data and the reference are constant?

Kirk McDonald Wrote:

> Robert Fraser wrote:
> > Can someone explain to me what the difference is between a storage class and a type is, with regards to const and invariant? For example, what do these do differently?
> > 
> > invariant int foo;
> > invariant(int) foo;
> > 
> > ...? I know this is probably second-nature to people with C++ backgrounds, but I find the documentation quite confusing, as I've only really worked in Java before.
> 
> Between those? Nothing. The difference is here:
> 
> invariant int* foo;
> invariant(int)* bar;
> 
> The former is equivalent to:
> invariant(int*) foo;
> 
> That is, when it is used as a storage class, it applies to the whole type.
> 
> -- 
> Kirk McDonald
> http://kirkmcdonald.blogspot.com
> Pyd: Connecting D and Python
> http://pyd.dsource.org

June 19, 2007
> invariant int foo;

This is like a compile-time constant

invariant int foo = 1;
foo = 2; // Fails: foo is not an lvalue
auto fooptr = &foo; // Fails: 1 is not an lvalue

> invariant(int) foo;

This is something else and some of its behaviour I don't understand.

invariant(int) foo = 1; // typeof(foo) seems to be int?
foo = 2; // ok!
auto fooptr = &foo;
*fooptr = 3; // the docs say this should fail, but it compiles fine

Note that
invariant(int)* fooptr = &foo;
fails with "cannot implicitly convert expression (& foo) of type int* to
invariant(int)*", although the documentation says that should work.

So making a type invariant seems to have no effect on plain data at the moment. It makes a difference for data containing references though:

struct S { int x; int* p; }
invariant(S) bar;
bar.x = 1; // ok
*bar.p = 1; // fails: not mutable

auto barptr = &bar; //typeof(barptr) is invariant(S)*
barptr.x = 2 // fails: not mutable

Note also that
static if(is(invariant(S*) == invariant(S)*))
does not pass, but
invariant(S*) barptr2 = &bar;
static if(is(typeof(barptr2) == invariant(S)*))
passes... is there a logical explanation for that?

Cheers,
Christian
June 19, 2007
>> For example, what do these do differently?
>> 
>> invariant int foo;
>> invariant(int) foo;
> 
> Nothing.

I thought so too, but experimenting with the new compiler gives

void main()
{
  invariant int foo;
  invariant(int) bar;
  static if(is(typeof(foo) == typeof(bar)))
    pragma(msg, "Same types");

  bar = 1;
  foo = 1; // line 9
}

dmd invartest
Same types
invartest.d(9): Error: constant foo is not an lvalue

So... they are the same type but behave differently? Is that a bug?

Christian
June 19, 2007
> For example, what do these do differently?
>
> invariant int foo;
> invariant(int) foo;

There is a big difference.


invariant(int) foo;
1. the data of foo is an invariant int.
2. the brackets mean: you can assign another int to foo.

You can't change the data of foo.
But you are allowed to assign an other int to foo.
So this doesn't make sense for a simple storage type like int.
(for classes this is useful)



invariant int foo;
1. the data of foo is an invariant int.
2. the reference of foo is invariant, too

So you can't change the data of foo and you
can't assign an other int to foo.
And this makes sense ;)
(for classes this isn't possible, since they can't be assigned
at compile-time)



If you want the same thing for a class, you have to write:
final invariant(MyClass) foo = cast(invariant) new MyClass("mydata");

invariant(MyClass) foo;
You can't change the data of foo (like: foo.x=10;).
But you could assign an other MyClass to foo
(like: foo = cast(invariant) new MyClass("an other MyClass");)

final forbids the last step.
It means that you can't assign an other MyClass to foo.



This is equivalent: "final invariant(int)" and "invariant int".



It also shows the need for having 3 different keywords.
Even with explicit casting a class can't be assigned at compile-time:
invariant MyClass foo = cast(invariant) new MyClass("data");
//Error: non-constant expression cast(invariant MyClass)new MyClass

invariant(MyClass) foo = cast(invariant) new MyClass("data");
This is ok, but an other MyClass could be assigned to foo.
So we mark it as final:
final invariant(MyClass) foo = cast(invariant) new MyClass("data");
and get the same behavior as if we had:
invariant MyClass foo = cast(invariant) new MyClass("data");
//Error: non-constant expression cast(invariant MyClass)new MyClass

Just to mention it: const creates a read-only access to sth that might be mutable.
invariant(MyClass*) ptr = ... //the data of MyClass will never ever change
const(MyClass*) ptr = ... //you can't use ptr to change the data, ptr is pointing to


Please correct me, if I was wrong with anything.

Best regards,
Daniel
June 19, 2007
Christian Kamm Wrote:

[...]

> struct S { int x; int* p; }
> invariant(S) bar;
> bar.x = 1; // ok
> *bar.p = 1; // fails: not mutable

Wait, so non-reference-types are mutable, but the data pointed to by reference types isn't...? Hwah...? I understand that structs are value types, but does this mean that the values within the struct can be changed while the values the struct points to can't be? And this differs from a class because a class is a reference type by default...?

So, wait,
struct Foo { int x; }
class Bar { int x; }
const(Foo) foo;
const(Bar) bar = new Bar();
foo.x = 5; // Ok...?
bar.x = 5; // Compile-time error...?

Weird. I've always just used structs as stack-allocated classes without inheritence, constructors, or the overhead. Now there's another difference to worry about, I guess because they're value types...

> static if(is(invariant(S*) == invariant(S)*))
> does not pass, but
> invariant(S*) barptr2 = &bar;
> static if(is(typeof(barptr2) == invariant(S)*))
> passes... is there a logical explanation for that?

Now I'm even more confused... I'm from a Java background, so I don't really understand constness already, and the implementation here seems to be way weird...

Anyways, thanks all...
I guess I'll wait until the implementation matches the docs and there's a tutorial or something out there which explains this all without bringing up a lot of gray areas.
June 20, 2007

Robert Fraser wrote:
> Christian Kamm Wrote:
> 
> [...]
> 
>> struct S { int x; int* p; }
>> invariant(S) bar;
>> bar.x = 1; // ok
>> *bar.p = 1; // fails: not mutable
> 
> Wait, so non-reference-types are mutable, but the data pointed to by reference types isn't...? Hwah...? I understand that structs are value types, but does this mean that the values within the struct can be changed while the values the struct points to can't be? And this differs from a class because a class is a reference type by default...?
> 
> So, wait,
> struct Foo { int x; }
> class Bar { int x; }
> const(Foo) foo;
> const(Bar) bar = new Bar();
> foo.x = 5; // Ok...?
> bar.x = 5; // Compile-time error...?
> 
> Weird. I've always just used structs as stack-allocated classes without inheritence, constructors, or the overhead. Now there's another difference to worry about, I guess because they're value types...
> 
>> static if(is(invariant(S*) == invariant(S)*))
>> does not pass, but
>> invariant(S*) barptr2 = &bar;
>> static if(is(typeof(barptr2) == invariant(S)*))
>> passes... is there a logical explanation for that?
> 
> Now I'm even more confused... I'm from a Java background, so I don't really understand constness already, and the implementation here seems to be way weird...
> 
> Anyways, thanks all...
> I guess I'll wait until the implementation matches the docs and there's a tutorial or something out there which explains this all without bringing up a lot of gray areas.

Does this help?  It was written before the release of D 2.0, but it does try to explain the difference between the different kinds of const: http://while-nan.blogspot.com/2007/06/you-cant-touch-this.html

As for the second thing, I'm not entirely sure either, but given that
invariant(char)[] seems to have the behaviour one would expect from
invariant(char[]), it could be that there's simply no difference and
invariant(char)[] is the canonical form in the compiler.

	-- Daniel
June 20, 2007
Yup, that sort of makes sense, thanks.

Now I'm just wierded out by the whole thing where struct members can change value but things pointed to by the struct members can't. But I'll get used to it eventually. I think for the time being, I'll just put "in" on all my method signatures, remove it when the compiler complains, and not worry about the rest.

Daniel Keep Wrote:

> 
> 
> Robert Fraser wrote:
> > Christian Kamm Wrote:
> > 
> > [...]
> > 
> >> struct S { int x; int* p; }
> >> invariant(S) bar;
> >> bar.x = 1; // ok
> >> *bar.p = 1; // fails: not mutable
> > 
> > Wait, so non-reference-types are mutable, but the data pointed to by reference types isn't...? Hwah...? I understand that structs are value types, but does this mean that the values within the struct can be changed while the values the struct points to can't be? And this differs from a class because a class is a reference type by default...?
> > 
> > So, wait,
> > struct Foo { int x; }
> > class Bar { int x; }
> > const(Foo) foo;
> > const(Bar) bar = new Bar();
> > foo.x = 5; // Ok...?
> > bar.x = 5; // Compile-time error...?
> > 
> > Weird. I've always just used structs as stack-allocated classes without inheritence, constructors, or the overhead. Now there's another difference to worry about, I guess because they're value types...
> > 
> >> static if(is(invariant(S*) == invariant(S)*))
> >> does not pass, but
> >> invariant(S*) barptr2 = &bar;
> >> static if(is(typeof(barptr2) == invariant(S)*))
> >> passes... is there a logical explanation for that?
> > 
> > Now I'm even more confused... I'm from a Java background, so I don't really understand constness already, and the implementation here seems to be way weird...
> > 
> > Anyways, thanks all...
> > I guess I'll wait until the implementation matches the docs and there's a tutorial or something out there which explains this all without bringing up a lot of gray areas.
> 
> Does this help?  It was written before the release of D 2.0, but it does try to explain the difference between the different kinds of const: http://while-nan.blogspot.com/2007/06/you-cant-touch-this.html
> 
> As for the second thing, I'm not entirely sure either, but given that
> invariant(char)[] seems to have the behaviour one would expect from
> invariant(char[]), it could be that there's simply no difference and
> invariant(char)[] is the canonical form in the compiler.
> 
> 	-- Daniel