www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why can't we define re-assignable const reference variable?

reply none <z gg.com> writes:
Why can't we define re-assignable const reference variable?
i.e. the variable can be re-bind, but it's allowed to change the object it
points to:

This is such a pain to port D 1.0 code to 2.0, e.g. I have some code in D 1.0
like this:

========================
  B b1, b2;
  b1 = new B();
  b2 = new B();

  B b;
  b = b1;
  b.MemberFunc();         // later change to const member function
  b = b2;
  b.anotherMemberFunc();  // later change to const member function
========================

When porting to D 2.0, some of the method of class B is changed to const
member function, and I want to make my intention clear, so I changed 'B b', to
be 'const B b'.

Now the trouble is, b can only be init-ed once, but cannot be re-assigned.

I know I can use pointer to achieve what I want:

========================
  B b1, b2;
  b1 = new B();
  b2 = new B();

  const(B)* b;            // type change
  b = &b1;                // additional change
  b.constMemberFunc();
  b = &b2;                // additional change
  b.anotherConstMemberFunc();
========================

Then I have to change the code in multiple places; and the other problem is
that I will have to use pointers most of the time in my code.

So why can't we have both (just as in C++):

========================
const B b;  // b cannot be re-bind, and the object cannot be modified
B const b;  // b can    be re-bind, but the object cannot be modified
========================
Feb 16 2008
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 16/02/2008, none <z gg.com> wrote:
  So why can't we have both (just as in C++):

This has been covered so many times before, but in summary, allowing that would completely break the type system. But I just want to clear up one misconception. You /cannot/ do that in C++. References in C++ are /never/ rebindable.
Feb 16 2008
next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 16/02/2008, none <z gg.com> wrote:
  So why can't we have both (just as in C++):

This has been covered so many times before, but in summary, allowing that would completely break the type system.

What's the difference (save polymorphism) between a pointer to a struct and a reference to an object?
Feb 16 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 16/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 What's the difference (save polymorphism) between a pointer to a struct
  and a reference to an object?

I don't understand the question. What's the difference between chalk and cheese? What are you getting at?

Well, chalk has a significantly higher calcium content and has usually not gone through a cow. In C++, there's no difference between the two. In D, the difference appears to be that there is no syntax separate the reference from the object, besides an ugly cast. I've never heard any explanation of how reassignable const objects would break the type system, and it seems to me that, if they would, then you shouldn't be able to do: struct Foo{} const(Foo)* fptr;
Feb 16 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 16/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 What's the difference (save polymorphism) between a pointer to a struct
  and a reference to an object?



Yes there is. S * p = whatever; S & r = whatever; p can be rebound. r can't.

Hm. Perhaps I should learn c++ before talking about it.
 I've never heard any explanation of how reassignable const objects would
 break the type system,

Because you'd need a brand new syntax to do it, and whatever syntax you chose, it would break generic programming.

generic programming != type system I forgot about that argument, though. Thanks for reminding me. The solution, of course, is first-class references for everything, which Walter isn't willing to do. In almost every circumstance, it'd be duplicated functionality.
Feb 16 2008
parent reply Robert Fraser <fraserofthenight gmail.coim> writes:
Janice Caron Wrote:

 On 16/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 generic programming != type system

I can't argue with that! :-) Perhaps I should have said, it will break /either/ generic programming /or/ the type system. I suppose I was thinking that if you have new syntax, and you want to keep generic programming, then somehow you must force the new syntax to work for all types, and /that/ breaks the type system, but I guess I was probably thinking too far ahead there. :-)

D already has types that break generic programming. Static arrays, anyone? One thing I was arguing for before the const system was implemented as it is now is for structs to act as similarly to classes as possible, only stack allocated. So while there was still a question of "head-const" and "tail-const", a "tail-const" struct would be the same as a "tail-const" class: that is, the reference would be rebindable, but members would not be. For example: struct S { int x; } class C { int x; } tailconst(S) s1; tailconst(S) s2; tailconst(C) c1 = new C(); tailconst(C) c2 = new C(); s1 = s2; // Okay c1 = c2; // Okay s1.x = 5; // Not okay c1.x = 5; // Not okay When I proposed this, NOBODY agreed with me (that is, everybody who responded wanted structs/classes to act differently for generic programming purposes).
Feb 17 2008
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Robert Fraser wrote:
 Janice Caron Wrote:
 
 On 16/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 generic programming != type system

/either/ generic programming /or/ the type system. I suppose I was thinking that if you have new syntax, and you want to keep generic programming, then somehow you must force the new syntax to work for all types, and /that/ breaks the type system, but I guess I was probably thinking too far ahead there. :-)

D already has types that break generic programming. Static arrays, anyone?

Oh dear GOD yes. I'm doing an assertions library right now (cooked it up over the past couple days because I got tired of writing assert (something, "salient description here")) and strings are absolutely KILLING me. I want to support Tango collections for list constraints, but I can't instantiate it with a static array. I can write some ugly templates to make it go away, but it's just really annoying. And then I think sameAs constraints won't work right. If strings were dynamic arrays, all my troubles would disappear.
Feb 17 2008
prev sibling parent Robert Fraser <fraserofthenight gmail.coim> writes:
Janice Caron Wrote:

 On 17/02/2008, Robert Fraser <fraserofthenight gmail.coim> wrote:
 s1 = s2; // Okay
 c1 = c2; // Okay
 s1.x = 5; // Not okay
 c1.x = 5; // Not okay

 When I proposed this, NOBODY agreed with me (that is, everybody who responded
wanted structs/classes to act differently for generic programming purposes).

Isn't that rather because modifying s1.x modifies s1?

Technically, yes. But what I was (and still am) campaigning for is syntax changes where by "struct S" can be changed to "class X" and no client code will be broken at all. Structs/classes should behave differently from an implementation perspective (not like C++), so for example structs are data types/stack allocated and don't support inheritance, but they should be usable in roughly the same way by clients. This removes the decision from the API for most purposes.
Feb 17 2008
prev sibling parent reply none <z gg.com> writes:
== Quote from Janice Caron (caron800 googlemail.com)'s article
 On 16/02/2008, none <z gg.com> wrote:
  So why can't we have both (just as in C++):

that would completely break the type system.

I don't understand why it will break the type system; in any case, even without it (as we do now), I can always get around it by using D pointers: const(B)* b; // type change b = &b1; // rebind b = &b2; // rebind It's just so inconvenient, that's why I suggest allow define re-assignable const reference variable.
 But I just want to clear up one misconception. You /cannot/ do that in
 C++. References in C++ are /never/ rebindable.

That's a terminology issue, to make it clear: class C {} D C++ ========================= =========================== reference: C c = new C(); pointer: C *c = new C(); pointer: C* cp = &c; ptr-to-ptr: C **cp = &c; What I mean is in C++, you can define 2 different kinds of C++ pointers: const C* b; // b cannot be re-bind, and the object cannot be modified C* const b; // b can be re-bind, but the object cannot be modified So in D we should be able to define 2 different kinds of D reference: const C b; // b cannot be re-bind, and the object cannot be modified C const b; // b can be re-bind, but the object cannot be modified
Feb 16 2008
next sibling parent none <z gg.com> writes:
Sorry in C++, it should be

 What I mean is in C++, you can define 2 different kinds of C++ pointers:
 const C* b;  // b cannot be re-bind, and the object cannot be modified

 C* const b;  // b can    be re-bind, but the object cannot be modified

and: const C* const b; // both pointer, and data are const.
Feb 16 2008
prev sibling next sibling parent Christopher Wright <dhasenan gmail.com> writes:
none wrote:
 == Quote from Janice Caron (caron800 googlemail.com)'s article
 On 16/02/2008, none <z gg.com> wrote:
  So why can't we have both (just as in C++):

that would completely break the type system.

I don't understand why it will break the type system; in any case, even without it (as we do now), I can always get around it by using D pointers:

From Walter's perspective, there are a few choices: - First-class references. A large language feature to solve a small problem. - Do nothing. - Have Andrei cook up a library solution. For that last, you can't simply put a struct around it and go on your merry way. The closest you can reasonably come is: struct Tailconst(T) { T t; void opAssign(T value) { t = value; } // insert method and operator forwarding here }
Feb 16 2008
prev sibling next sibling parent reply none <z gg.com> writes:
Actually we can rebind already: this is legal code accepted by the DMD compiler

for (i = n; i-- > 0; ) {
  const B b = getConstB(i);  // are we rebind here n-times already?
  b.doSomething();
}

// the trouble is I cannot use the last 'b' i.e. getConstB(0),
// anymore, it will be out of scope
b.doSomethingElse();


 (as we do now), I can always get around it by using D pointers:
   const(B)* b;            // type change
   b = &b1;                // rebind
   b = &b2;                // rebind
 It's just so inconvenient, that's why I suggest allow define re-assignable
const
 reference variable.

Feb 16 2008
parent Christopher Wright <dhasenan gmail.com> writes:
none wrote:
 Actually we can rebind already: this is legal code accepted by the DMD compiler
 
 for (i = n; i-- > 0; ) {
   const B b = getConstB(i);  // are we rebind here n-times already?
   b.doSomething();
 }

That doesn't really work. For purposes of const, it's as if you wrote: void loopBody (int i) { const B b = getConstB(i); b.doSomething(); } for (i = n; i > 0; i--) { loopBody(i); }
 // the trouble is I cannot use the last 'b' i.e. getConstB(0),
 // anymore, it will be out of scope
 b.doSomethingElse();
 
 
 (as we do now), I can always get around it by using D pointers:
   const(B)* b;            // type change
   b = &b1;                // rebind
   b = &b2;                // rebind
 It's just so inconvenient, that's why I suggest allow define re-assignable
const
 reference variable.


Then there's no way to say you don't want to be able to rebind it.
Feb 17 2008
prev sibling next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
none <z gg.com> wrote:
 == Quote from Janice Caron (caron800 googlemail.com)'s article
 On 16/02/2008, none <z gg.com> wrote:
  So why can't we have both (just as in C++):

that would completely break the type system.

I don't understand why it will break the type system; in any case, even without it (as we do now), I can always get around it by using D pointers: const(B)* b; // type change b = &b1; // rebind b = &b2; // rebind It's just so inconvenient, that's why I suggest allow define re-assignable const reference variable.
 But I just want to clear up one misconception. You /cannot/ do that in
 C++. References in C++ are /never/ rebindable.

That's a terminology issue, to make it clear: class C {} D C++ ========================= =========================== reference: C c = new C(); pointer: C *c = new C(); pointer: C* cp = &c; ptr-to-ptr: C **cp = &c;

This is not how the things currently are. Actually it's like this: class C {} struct S {} D C++ ========================= =========================== reference: C c = new C(); pointer: C *c = new C(); pointer: C* cp = &c; pointer: C *cp = c; value: S s = {}; value: S s = {}; pointer: S* sp = &s; pointer: S *sp = &s; no equiv. reference: C &cr = *c; no equiv. reference: C &sr = s; So it's really strange that you can't declare const(C) x; All the syntax is here already. -- SnakE
Feb 16 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 On 17/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 So it's really strange that you can't declare
 const(C) x;

??? You can!

But it wouldn't mean mutable reference to const data. This /looks/ like const only affecting C, but, in fact, constness is expanded over the whole declaration. I.e. the syntax for declaring reference to const is already in the language, the question is, why it's not in the type system. -- SnakE
Feb 17 2008
next sibling parent Sean Reque <seanthenewt yahoo.com> writes:
 
 Of course not. That would be impossible.
 
 You cannot have a mutable reference to const data in C++, so what's
 the big deal? The best you can achieve in C++ is a mutable /pointer/
 to const data, and that is also allowed in D.
 

This argument is completely pointless, because in C++ you don't have references at all, at least not in the same sense as references are in most every other high-level language, including D, Java, C# Perl, Python, Ruby, Smalltalk, Scheme, Javascript, etc. In most languages, including D, references are basically there to replace pointers as a much safer and more syntactically pleasing alternative when the full power of pointers isn't needed or desired (segmentation fault, anyone?). Therefore, references in D should have the same flexibility as a pointer would. But right now references behave like pointers in terms of assignment, but const references do NOT behave like const pointers in terms of assignment, and that simply should not be.
Feb 17 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 On 17/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 Janice Caron <caron800 googlemail.com> wrote:
 On 17/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 So it's really strange that you can't declare
 const(C) x;

You can!

But it wouldn't mean mutable reference to const data.

Of course not. That would be impossible. You cannot have a mutable reference to const data in C++, so what's the big deal? The best you can achieve in C++ is a mutable /pointer/ to const data, and that is also allowed in D.

You can't have a mutable reference to mutable data in C++, either. D allows that. Why allow one but deny the other, I don't get it.
 This /looks/ like
 const only affecting C

const is transitive, so it affects C, and everything referenced by C. It declares the four-byte reference on the stack that is C, to be const, and since const is transitive, that makes the instance of C's data on the heap const, and everything referenced therefrom.

Yes, the C and everything below is const. But why x ? It doesnt belong to C, it's in my function scope. It's not transitiviness that bothers me, but backward transitiviness.
 but, in fact, constness is expanded over the
 whole declaration.

Of course. Everything in the brackets is const. That's what's brackets are for - precedence. C is in the brackets; C is const.

But x is outside brackets ! Why is it const, too ?
 I.e. the syntax for declaring reference to const is
 already in the language,

You're going to have to clarify that, I'm afraid, as I don't understand. Are you referring to the function parameter syntax ref const(C) c ? In this case, c is indeed rebindable, but only because an extra level of indirection has been silently added.

When I write, "const(C) x;", I mean, "C is const but x is not." This is semantically correct because the assignment operator works with the reference directly when an object of the same type is assigned. It's in the language specs. There is no problem for the assignment to check at compile time if the x itself is const or not.
 the question is, why it's not in the type
 system.

I don't agree that that's even a reasonable question, because there is /not/ a syntax for "mutable reference to constant data" (not counting function parameters).

It could've been. You wouldn't need to change the language syntax if you wanted to add one. -- SnakE
Feb 17 2008
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 18 Feb 2008 07:12:08 +0000, Janice Caron wrote:

 I say again, there is no way to express "mutable reference to const
 data" in D

Of course you can. I do it all the time. But maybe I don't understand what you are saying ;-) import std.stdio; class Foo { const string _me; this(string name) { _me = name; writefln("Creating %s", _me); } } void main() { const(Foo) f; // A mutable reference to const data. const(Foo) a = new Foo("one"); const(Foo) b = new Foo("two"); const(Foo) c = new Foo("three"); f = a; f = b; f = c; writefln("%s", f._me); } What I can't get D to do easily is declare a const reference to mutable data. Yes, I know there are ways to trick D to get around this but its not as nice as just letting the programmer achieve their goals with simpler, more intuitive syntax. eg.. const nonconst(Foo) f; // 'f' is const but it's data is not. And I know the argument "But what purpose what that really serve?". The answer to that is "doing it another way is more complex". But anyhow, I know its not going to happen so my part in the discussion can end here. -- Derek (skype: derek.j.parnell) Melbourne, Australia 18/02/2008 7:04:36 PM
Feb 18 2008
parent reply Denton Cockburn <diboss hotmail.com> writes:
On Mon, 18 Feb 2008 19:36:31 +1100, Derek Parnell wrote:

 On Mon, 18 Feb 2008 07:12:08 +0000, Janice Caron wrote:
 
 I say again, there is no way to express "mutable reference to const
 data" in D

Of course you can. I do it all the time. But maybe I don't understand what you are saying ;-) import std.stdio; class Foo { const string _me; this(string name) { _me = name; writefln("Creating %s", _me); } } void main() { const(Foo) f; // A mutable reference to const data. const(Foo) a = new Foo("one"); const(Foo) b = new Foo("two"); const(Foo) c = new Foo("three"); f = a; f = b; f = c; writefln("%s", f._me); }

That doesn't compile in 2.010
Feb 18 2008
parent Derek Parnell <derek psych.ward> writes:
On Mon, 18 Feb 2008 08:39:58 -0500, Denton Cockburn wrote:

 On Mon, 18 Feb 2008 19:36:31 +1100, Derek Parnell wrote:

 import std.stdio;
 class Foo
 {
     const string _me;
     this(string name) { _me = name; writefln("Creating %s", _me); }    
 }
 void main()
 {
    const(Foo) f;  // A mutable reference to const data.
 
    const(Foo) a = new Foo("one");
    const(Foo) b = new Foo("two");
    const(Foo) c = new Foo("three");
    
    f = a;
    f = b;
    f = c;
    
    writefln("%s", f._me);
    
 }
 

That doesn't compile in 2.010

Damn! You're right. I was using an earlier version of D. However, if you replace 'const(Foo)' with 'const(char)[]' is does compile. Which means that a dynamic array, which is a reference type, can be mutable even though its data is not. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 18 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 I say again, there is no way to express "mutable reference to const
 data" in D, and any method you come up with to allow that would be (a)
 incredibly confusing, and (b) would break generic programming and/or
 the type system.

One can safely assume that a variable x of type int with a value 4 in it is a mutable reference to an object 4 of type invariant(int). That is, all variables of primitive types in D (as well as in other languages) are either mutable or immutable references to invariant objects. Class type variables in D are either mutable references to mutable objects, or immutable references to immutable objects. Pointers are either mutable or immutable references to either mutable or immutable objects, except head-const variant. Struct type variables are exactly like primitives if struct doesn't contain references somewhere. I like to call them PODs. Modifying a struct is generalised as replacing a reference to one immutable set of data with a reference to another immutable set of data, exactly as it is with primitive types. Non-POD structs cannot be generalised as references. Hence all the problems with them. So, here is a table summarizing what current D constructs can do: ref tgt Primitive/POD: var inv const inv inv inv Class: var var const const inv inv Pointer: var var var const var inv const const const inv inv inv The left column is a reference type, the right column is a type of referenced object. As you can see, the most generic type is pointer. That's why you suggest using it here and there. But it's not the D way. In D, the class reference should be the most generic. It should be safe to assume everything is reference when you write a template. The mutable references to const objects are not breaking generic programming, but aiding it. -- SnakE
Feb 18 2008
next sibling parent reply Robert Fraser <fraserofthenight gmail.coim> writes:
Janice Caron Wrote:

 On 18/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 One can safely assume that a variable x of type int with a value 4 in it
 is a mutable reference to an object 4 of type invariant(int).

Not unless you've redefined the word reference. At the ABI level, a reference is identical to a pointer. It occupies four bytes of space (eight on a 64 bit machine), and those four bytes contain an address. The difference between a reference and a pointer occurs at the syntax level, not the ABI level. What you've described there is not a reference at all - it's an int. A reference to an int would occupy eight bytes of memory (four for the reference, and four more for the int).

I _think_ he was trying to describe it from a logical, rather than implementation standpoint. Logically, x "refers" (using the English term, not the programming term, mind you) to 4. 4 can't be changed (you can't say 4 = 7), but x can be.
Feb 18 2008
parent Sergey Gromov <snake.scaly gmail.com> writes:
Robert Fraser <fraserofthenight gmail.coim> wrote:
 Janice Caron Wrote:
 
 On 18/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 One can safely assume that a variable x of type int with a value 4 in it
 is a mutable reference to an object 4 of type invariant(int).

Not unless you've redefined the word reference. At the ABI level, a reference is identical to a pointer. It occupies four bytes of space (eight on a 64 bit machine), and those four bytes contain an address. The difference between a reference and a pointer occurs at the syntax level, not the ABI level. What you've described there is not a reference at all - it's an int. A reference to an int would occupy eight bytes of memory (four for the reference, and four more for the int).

I _think_ he was trying to describe it from a logical, rather than implementation standpoint. Logically, x "refers" (using the English term, not the programming term, mind you) to 4. 4 can't be changed (you can't say 4 = 7), but x can be.

You're absolutely correct. -- SnakE
Feb 18 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 On 18/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 One can safely assume that a variable x of type int with a value 4 in it
 is a mutable reference to an object 4 of type invariant(int).

Not unless you've redefined the word reference. At the ABI level, a reference is identical to a pointer. It occupies four bytes of space (eight on a 64 bit machine), and those four bytes contain an address. The difference between a reference and a pointer occurs at the syntax level, not the ABI level. What you've described there is not a reference at all - it's an int. A reference to an int would occupy eight bytes of memory (four for the reference, and four more for the int).

I'm talking from the standpoint of generalization which you so much care about. When I generalize, I don't care how things are implemented. It's behaviour that matters. If I create an algorithm that uses references to invariant data, then it will work with anything that /behaves/ like such references. Even if they are actually ints passed by value. Imagine a language with only one class of objects, that is, objects passed by reference. Integer numbers are invariant objects of type int, and an integer variable is a reference to an invariant object of type int. Since the int objects are immutable, a compiler of this language can safely optimize the code and keep the int object values in place of references. This is what D does. It's an implementation detail. It's an optimization which just happened to end up in language specs. This optimization is so common that it's probably hard to believe that it's only an efficient implementation of a generic concept of a reference. -- SnakE
Feb 18 2008
next sibling parent reply Sean Reque <seanthenewt yahoo.com> writes:
 For what it's worth, I was arguing exactly what you are arguing now
 (that it should be allowable to modify the class reference of a const
 class), several weeks back. But Walter patiently explained why it was
 not a good idea, and his arguments were sound, so I got convinced. In
 other words, this isn't a new argument - you might want to check out
 some of that history (although I'm not sure I can advise on what to
 search for - "const" would probably yeild rather too many hits!)

Well if Walter wants people to use his language, he needs to do some better convincing. I looked around and couldn't find anything searching around for 15 minutes. I liked D when I first learned about it, but I keep finding out things that were "already discussed" that I'm still convinced are plain wrong, and still haven't seen a good reason as to why they are right, which convinces me more each time that D will never hit mainstream.
Feb 18 2008
parent reply Derek Parnell <derek nomail.afraid.org> writes:
 
 Well if Walter wants people to use his language, he needs to do some better
convincing.

I'm starting to suspect that in the effort to make the const system simple, it has become simplistic. -- Derek (skype: derek.j.parnell) Melbourne, Australia 19/02/2008 9:26:28 AM
Feb 18 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Derek Parnell wrote:
  
 Well if Walter wants people to use his language, he needs to do some better
convincing.

I'm starting to suspect that in the effort to make the const system simple, it has become simplistic.

I'm ok with simplistic as long as simple, common things remain simple. Don't know whether that's the case or not currently. --bb
Feb 18 2008
parent Derek Parnell <derek psych.ward> writes:
On Tue, 19 Feb 2008 07:29:18 +0000, Janice Caron wrote:

 Don't know whether that's the case or not currently.

I've been using it for a while now, and on the whole I find it very simple, elegant, and obvious.

So long as one isn't trying to do things that Walter disagrees with. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 19 2008
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sergey Gromov wrote:
 I'm talking from the standpoint of generalization which you so much care 
 about.  When I generalize, I don't care how things are implemented.  
 It's behaviour that matters.  If I create an algorithm that uses 
 references to invariant data, then it will work with anything that 
 /behaves/ like such references.  Even if they are actually ints passed 
 by value.

That's a good point, and a good reason why: invariant(C)[] c; invariant(int)[] i; should both have immutable array contents, i.e. if the contents of a reference is invariant then the reference itself should be invariant. The concept of having a mutable reference to immutable data is fundamentally flawed because it breaks the concept of what a reference is. A reference is not separable from what it refers to. If they were separable, they would be syntactically and semantically the same thing as pointers, and there would be no point whatsoever to even having a reference type.
Feb 19 2008
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 19 Feb 2008 12:49:09 -0800, Walter Bright wrote:

 invariant(C)[] c;
 invariant(int)[] i;
 
 should both have immutable array contents, i.e. if the contents of a 
 reference is invariant then the reference itself should be invariant.
 
 The concept of having a mutable reference to immutable data is 
 fundamentally flawed because it breaks the concept of what a reference 
 is. A reference is not separable from what it refers to. If they were 
 separable, they would be syntactically and semantically the same thing 
 as pointers, and there would be no point whatsoever to even having a 
 reference type.

Ok given this position and that 'string' is 'invariant(char)[]' then string s; makes 's' a reference to an immutable array, and thus 's' should also be immutable, right? If so, does that mean that I cannot bind 's' to another literal - that is change the bits in 's' such that the .ptr and maybe .length properties have different values? string s = "abc'; // Initialized ok. s = "def"; // Should this fail because // I'm now changing an immutable reference? If your position re immutable references to hold, and if our codes needs mutable 'references' to immutable data, and thus we need to use pointers, what is the 'pointer' syntax for dynamic arrays that preserves array bounds checking? -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 19 2008
parent Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 19 Feb 2008 22:18:29 +0000, Janice Caron wrote:

 On 19/02/2008, Derek Parnell <derek psych.ward> wrote:
 Ok given this position and that 'string' is 'invariant(char)[]' then

    string s;

 makes 's' a reference to an immutable array,

No, s is an array of immutable chars. Put another way, as I am sure you are well aware, s is a value type, ...

In that case, what did Walter really mean when he wrote:
That's a good point, and a good reason why:

invariant(C)[] c;
invariant(int)[] i;

should both have immutable array contents, i.e. if the contents of a 
reference is invariant then the reference itself should be invariant.



I took it that he was saying that 'c' and 'i' are references. -- Derek (skype: derek.j.parnell) Melbourne, Australia 20/02/2008 11:17:17 AM
Feb 19 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Sergey Gromov wrote:
 I'm talking from the standpoint of generalization which you so much care 
 about.  When I generalize, I don't care how things are implemented.  It's 
 behaviour that matters.  If I create an algorithm that uses references to 
 invariant data, then it will work with anything that /behaves/ like such 
 references.  Even if they are actually ints passed by value.

That's a good point, and a good reason why: invariant(C)[] c; invariant(int)[] i; should both have immutable array contents, i.e. if the contents of a reference is invariant then the reference itself should be invariant. The concept of having a mutable reference to immutable data is fundamentally flawed because it breaks the concept of what a reference is. A reference is not separable from what it refers to. If they were separable, they would be syntactically and semantically the same thing as pointers, and there would be no point whatsoever to even having a reference type.

So please explain this: import std.stdio; class C { this(int n) {x = n;} int x; } void assignit(ref int src, ref int value) { src = value; } void assignit(C src, C value) { src = value; } void main() { int n = 1; int m = 2; C c = new C(1); C d = new C(2); assignit(n, m); assignit(c, d); writefln("%s %s %s %s", n, m, c.x, d.x); } // outputs 2 2 1 2 In both cases, the assignit function is taking reference types as arguments. C is a reference type because it is a class. ref int is a reference type because the compiler magically creates a reference. Both do the same thing, but yet the class version doesn't result in the value changing, because we are rebinding the reference, not copying the class itself. This is why your argument fails. There are some cases in D where references are treated as non-rebindable pointers to data, where acting on the reference is equivalent to acting on the pointed at data, and then there are classes, where you can rebind the reference in some cases, but in other cases you are acting on the data pointed to. I'm not saying that references should always be non-rebindable (I like the way classes work) or always be rebindable, I'm just saying that your argument against allowing references to be acted upon separate from the data is against the way the language/compiler currently is implemented. You can make the case that class references are not the same as non-rebindable references, and also that they are not like pointers. I would say that they are exactly like pointers except you cannot dereference them or perform general arithmetic on them. This puts them in a category on their own, which I think is fair game to say that it is reasonable that we can ask for that category to be modified without affecting the consistency of the other categories. The specific modification being that you can const-ify the data pointed to by a class reference without modifying the reference constness itself. -Steve
Feb 19 2008
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 19/02/2008, Steven Schveighoffer wrote:
 So please explain this:

 <snip>
 // outputs 2 2 1 2

Simple. If you wanted the output to be 2 2 2 2 then the second function should have been declared void assignit(ref C src, ref C value) But you missed out the "ref"s, and so src was merely a local copy.
 In both cases, the assignit function is taking reference types as 
 arguments.

But in the second case, the reference is /local/. Function parameters declared without "ref" are just local variables. Assigning src will modify the local copy (of the reference), but you need the "ref" keyword if you want to modify the original (reference).
 Both do the same thing,

No, they don't. Passing a value by reference is not the same thing as passing a reference by value.

You missed the point completely. I'm not asking to find bugs in my code, I'm rebutting Walter's argument that "a reference is not separable from what it refers to". If this was true in the current compiler, then I would expect the output to be 2 2 2 2, because assigning one reference to another should be like copying the value, right? Walter's argument is like saying "If the sky wasn't green, then dragons wouldn't exist". I'm not arguing his point about dragons, I'm saying, "wait a minute, the sky isn't green." -Steve
Feb 19 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Walter Bright <newshound1 digitalmars.com> wrote:
 The concept of having a mutable reference to immutable data is 
 fundamentally flawed because it breaks the concept of what a reference 
 is. A reference is not separable from what it refers to. If they were 
 separable, they would be syntactically and semantically the same thing 
 as pointers, and there would be no point whatsoever to even having a 
 reference type.

A reference refers to data which is outside of the reference's body. This is the concept. A reference in a book, a C pointer, and a C++ reference are all realizations of this concept. The reason for C++ references to be immutable is obvious: the C++ designers wanted the C::operator=(C&) to work. D supports mutable references, so D references are not C++ references. D references are not the same as the data they refer to. They are much closer to pointers than in C++. The difference is in syntax. The point in having references in the language is the same as in C++: to use the same syntax for accessing objects, their members, and primitive types. This is required for proper generalization, and also simplifies program code. This is why references must support all possible type variants: they are instruments of generalization, so power of D templates depends directly on the power of references. -- SnakE
Feb 19 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sergey Gromov wrote:
 Walter Bright <newshound1 digitalmars.com> wrote:
 The concept of having a mutable reference to immutable data is 
 fundamentally flawed because it breaks the concept of what a reference 
 is. A reference is not separable from what it refers to. If they were 
 separable, they would be syntactically and semantically the same thing 
 as pointers, and there would be no point whatsoever to even having a 
 reference type.

A reference refers to data which is outside of the reference's body. This is the concept. A reference in a book, a C pointer, and a C++ reference are all realizations of this concept. The reason for C++ references to be immutable is obvious: the C++ designers wanted the C::operator=(C&) to work. D supports mutable references, so D references are not C++ references. D references are not the same as the data they refer to. They are much closer to pointers than in C++. The difference is in syntax. The point in having references in the language is the same as in C++: to use the same syntax for accessing objects, their members, and primitive types.

That is only a small part of why D classes are reference types. The major reason is that polymorphism doesn't work for value types, it cries out for reference types. Value and reference types have different uses.
 This is required for 
 proper generalization, and also simplifies program code.
 
 This is why references must support all possible type variants: they are 
 instruments of generalization, so power of D templates depends directly 
 on the power of references.
 

Feb 19 2008
parent Sergey Gromov <snake.scaly gmail.com> writes:
Walter Bright <newshound1 digitalmars.com> wrote:
 Sergey Gromov wrote:
 Walter Bright <newshound1 digitalmars.com> wrote:
 The concept of having a mutable reference to immutable data is 
 fundamentally flawed because it breaks the concept of what a reference 
 is. A reference is not separable from what it refers to. If they were 
 separable, they would be syntactically and semantically the same thing 
 as pointers, and there would be no point whatsoever to even having a 
 reference type.

A reference refers to data which is outside of the reference's body. This is the concept. A reference in a book, a C pointer, and a C++ reference are all realizations of this concept. The reason for C++ references to be immutable is obvious: the C++ designers wanted the C::operator=(C&) to work. D supports mutable references, so D references are not C++ references. D references are not the same as the data they refer to. They are much closer to pointers than in C++. The difference is in syntax. The point in having references in the language is the same as in C++: to use the same syntax for accessing objects, their members, and primitive types.

That is only a small part of why D classes are reference types. The major reason is that polymorphism doesn't work for value types, it cries out for reference types. Value and reference types have different uses.

My argument was not about references vs values but about references vs pointers. I'm arguing about this statement:
 If they (references---S.G.) were 
 separable, they would be syntactically and semantically the same thing 
 as pointers, and there would be no point whatsoever to even having a 
 reference type.



Pointers, being a flavour of references, support polymorphism, too. The only difference from pointers is in syntax, and it is very important. The value types are different, or more precisely, as I've written earlier, any value type not containing references may be abstracted as a mutable reference to immutable data.
 This is required for 
 proper generalization, and also simplifies program code.
 
 This is why references must support all possible type variants: they are 
 instruments of generalization, so power of D templates depends directly 
 on the power of references.


-- SnakE
Feb 20 2008
prev sibling parent reply none <z gg.com> writes:
 == Quote from Janice Caron (caron800 googlemail.com)'s article
 But I just want to clear up one misconception. You /cannot/ do that in
 C++. References in C++ are /never/ rebindable.

That's a terminology issue, to make it clear: class C {} D C++ ========================= =========================== reference: C c = new C(); pointer: C *c = new C(); pointer: C* cp = &c; ptr-to-ptr: C **cp = &c;

I just want to make it clear again, nobody has what you called 'misconception'. By using my table, I'm saying '_reference_ in D is similar to _pointer_ in C++'! I think you have a 'misconception' that '_reference_ in D is similar to _reference_ in C++'! Just as you said: "References in C++ are /never/ rebindable"; but '(non-const) reference in D' is rebindable class C {} C c; c = c1; c = c2; // rebind! So '_reference_ in D is similar to _pointer_ in C++', not reference in C++! Since 'in C++, const C* p; // non-const pointer, const data' is allowed, why cannot we have 'non-const reference, const data' in D?
Feb 17 2008
parent reply Matti Niemenmaa <see_signature for.real.address> writes:
Janice Caron wrote:
 On 17/02/2008, none <z gg.com> wrote:
 Since 'in C++, const C* p; // non-const pointer, const data' is allowed, why
 cannot we have 'non-const reference, const data' in D?

Why not just substitute your C++ pointer with a D pointer? If it's acceptable to use a pointer in C++, then use the same pointer in D.

The difference is that in C++, 'new C' results in a C*, whereas in D it results in a C. Assuming C is a class, that is. So the C++ code "C* a = new C();" converts to the D code "C a = new C;". Now what he wants is to convert the C++ "const C* b = a;" to D "??? b = a;" with the semantics that b can be reassigned but the data behind it is unchangeable. I.e.: class Foo { public: int x; }; int main() { Foo* a = new Foo(); const Foo* b = a; /* here, G++ gives "error: assignment of data-member 'Foo::x' in read-only structure" */ b->x = 1; delete a; } Would become what, exactly, in D? (Disclaimer: I honestly don't know, but that's what I think he's asking for.) -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Feb 17 2008
next sibling parent reply =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= writes:
Janice Caron schrieb:
 On 17/02/2008, Matti Niemenmaa <see_signature for.real.address> wrote:
 Now what he wants is to convert the C++ "const C* b = a;" to D "??? b = a;"
with
 the semantics that b can be reassigned but the data behind it is unchangeable.

OK, I see that. But the thing is, it is never actually /necessary/ to do that. It is always possible to just write the code differently. There is really no pressing /need/ for "const class with mutable reference", since nobody has ever demonstrated a real-life use case that couldn't just be done differently. Certainly, no need pressing enough to demand a major change to generic programming and the type system. Also, remember that a C++ class is a D struct, and there's really no equivalent to a D class in C++.

Show me how I can maintain a simple map of class instances, which I get as const(C) from somewhere else, i.e. I have to accept the const here. example: const(C)[string] map; <- completely useless - you can do nothing with this construct The only possibility to solve this is to either use a cast or to use a struct S { const(C) obj } and modify the map to S*[string] (or something similar) - at least I don't see an alternative. This, of course, would also apply to some self-made map-like container.
Feb 17 2008
next sibling parent reply =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= writes:
Janice Caron schrieb:
 On 17/02/2008, Sönke Ludwig <ludwig informatik_dot_uni-luebeck.de> wrote:
 Show me how I can maintain a simple map of class instances, which I get as
 const(C) from somewhere else, i.e. I have to accept the const here.

 example:

 const(C)[string] map;

 <- completely useless - you can do nothing with this construct

So change it to const(C)*[string] map; Now you've a map of mutable pointers, indexed by string. It's not hard.

Since C is a class, const(C)* will mean a mutable pointer to a const class reference, which is not even allocatable with a simple syntax (was brought up here a while ago), also this would be an additional allocation, just like with the struct workaround (see below). Generally, I think that it is very desirable to be able to handle the reference/pointer part of a class C separate from the actual data in this respect. After all they are completely distinct in the underlying implementation.
 
 
 or to use a struct S { const(C) obj }

That probably won't work in the future. You're basically exploiting a bug there, and I imagine that soon it will be fixed.

No, was talking about creating a struct where the const reference stored on the heap - a mutable pointer to this struct is then stored in the map. If a new object is to replace an array entry, a new struct is allocated and a pointer to this new struct replaces the old one. Sorry, forgot an example - this is what I was thinking about: struct S { const(C) inst; } S*[string] map; map["hello"] = new S(someinst); All of this is and should always be possible, it does not break any const principle or anything. It's just that it requires an additional heap allocation and a lot of additional typing which should really not be necessary for such a basic thing.
Feb 18 2008
parent =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= writes:
Sorry, the "new S(someinst)" syntax will not work, of course.
But you get the drift..
Feb 18 2008
prev sibling parent Robert Fraser <fraserofthenight gmail.coim> writes:
Janice Caron Wrote:
 So change it to
 
     const(C)*[string] map;
 
 Now you've a map of mutable pointers, indexed by string. It's not hard.
 

I don't feel good about sacrificing performance for something that I've lived without my whole programming career. The syntax is uglier, too. Your argument "we don't need it; there's always a workaround" is complete bollocks. We don't need high-level native-compiled languages at all, anything we can do in C or D we can in assembly language. We choose to use them because they encourage a more structured programming style with abstracted syntax. What you're basically encouraging is "workaround-oriented programming."
Feb 18 2008
prev sibling parent Sean Reque <seanthenewt yahoo.com> writes:
 On 17/02/2008, Matti Niemenmaa <see_signature for.real.address> wrote:
 Now what he wants is to convert the C++ "const C* b = a;" to D "??? b = a;"
with
 the semantics that b can be reassigned but the data behind it is unchangeable.

OK, I see that. But the thing is, it is never actually /necessary/ to do that. It is always possible to just write the code differently. There is really no pressing /need/ for "const class with mutable reference", since nobody has ever demonstrated a real-life use case that couldn't just be done differently. Certainly, no need pressing enough to demand a major change to generic programming and the type system.

Having a const class with mutable reference makes the language much much cleaner. The D docs themselves state that D is designed so that the programmer should rarely have to dip down to the level of pointers. Yet here we have a very important feature, const, that when used with an object reference cripples the reference so that it can no longer point to anything else, requiring us to use pointers when references should be able to suffice. D references are like Java references, and D const has similiarities with C++ const. I don't see why it should be so hard to mix Java-style references with C++ const, especially since Java style references are nothing more than syntactically pleasing pointers.
Feb 17 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 16/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 What's the difference (save polymorphism) between a pointer to a struct
  and a reference to an object?

I don't understand the question. What's the difference between chalk and cheese? What are you getting at?
Feb 16 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 16/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 What's the difference (save polymorphism) between a pointer to a struct
  and a reference to an object?


In C++, there's no difference between the two.

Yes there is. S * p = whatever; S & r = whatever; p can be rebound. r can't.
 In D, the difference
 appears to be that there is no syntax separate the reference from the
 object, besides an ugly cast.

And nor is there in C++. You cannot modify r. There is no way to get at r. You can't even take the address of r. C++ prohibits you, and no one's ever complained that that's a problem. If you want to rebind, you use a pointer.
 I've never heard any explanation of how reassignable const objects would
 break the type system,

Because you'd need a brand new syntax to do it, and whatever syntax you chose, it would break generic programming.
 and it seems to me that, if they would, then you
 shouldn't be able to do:
 struct Foo{}
 const(Foo)* fptr;

There's no problem with that. fptr is a pointer, not a reference.
Feb 16 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 16/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 generic programming != type system

I can't argue with that! :-) Perhaps I should have said, it will break /either/ generic programming /or/ the type system. I suppose I was thinking that if you have new syntax, and you want to keep generic programming, then somehow you must force the new syntax to work for all types, and /that/ breaks the type system, but I guess I was probably thinking too far ahead there. :-)
Feb 16 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 16/02/2008, none <z gg.com> wrote:
 What I mean is in C++, you can define 2 different kinds of C++ pointers:
 Sorry in C++, it should be

 (1) C* const b;  // b can be re-bind, but the object cannot be modified

 (2) const C* b and const C* const b;  // both pointer, and data are const.

I think you mistyped. (1) means "b is a const pointer to mutable data". In this case, therefore, b cannot be rebound. That would not be allowed in D because it breaks transitivity. On the other hand, the sentiment you expressed: "b can rebind, but the object cannot be modifed", can be expressed in both C++ and D as C const * b // C++ const C * b // C++, alternative syntax const(C)* b // D "Both pointer and data are const" can be written in both languages as C const * const b // C++ const C * const b // C++, alternative syntax const C* b; // D const(C*) b; // D, alternative syntax Both of my D examples assumed that the object C has been declared as a struct (in D), because otherwise you can't make an equivalence with C++. If C had been declared as a class in D, then things look a little different auto b = new C; // D There is no exact equivalent built into C++, although you can construct one artificially. C& b = *(new C()); // C++ This b will now behave just like a D reference. the C instance is on the heap, as required, and b is on the stack and four bytes big, as required. Now let's see what kind of constancy you can get: (1) C& b = ... (2) C& const b = ... (3) C const & b = ... (4) C const & const b = What may not be immediately obvious is that (1) and (2) are completely identical, because references in C++ are /always/ const. Likewise, (3) and (4) are completely identical, for the same reason. You absolutely /cannot/ do "mutable reference to const data" in C++. It's impossible. The best you can do is "mutable pointer to const data", and you /can/ do that in D. We're not missing out here!
Feb 16 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 17/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 So it's really strange that you can't declare
 const(C) x;

??? You can!
Feb 16 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 17/02/2008, Robert Fraser <fraserofthenight gmail.coim> wrote:
 s1 = s2; // Okay
 c1 = c2; // Okay
 s1.x = 5; // Not okay
 c1.x = 5; // Not okay

 When I proposed this, NOBODY agreed with me (that is, everybody who responded
wanted structs/classes to act differently for generic programming purposes).

Isn't that rather because modifying s1.x modifies s1?
Feb 17 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 17/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 Janice Caron <caron800 googlemail.com> wrote:
 On 17/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 So it's really strange that you can't declare
 const(C) x;

??? You can!

But it wouldn't mean mutable reference to const data.

Of course not. That would be impossible. You cannot have a mutable reference to const data in C++, so what's the big deal? The best you can achieve in C++ is a mutable /pointer/ to const data, and that is also allowed in D.
 This /looks/ like
 const only affecting C

const is transitive, so it affects C, and everything referenced by C. It declares the four-byte reference on the stack that is C, to be const, and since const is transitive, that makes the instance of C's data on the heap const, and everything referenced therefrom.
 but, in fact, constness is expanded over the
 whole declaration.

Of course. Everything in the brackets is const. That's what's brackets are for - precedence. C is in the brackets; C is const.
 I.e. the syntax for declaring reference to const is
 already in the language,

You're going to have to clarify that, I'm afraid, as I don't understand. Are you referring to the function parameter syntax ref const(C) c ? In this case, c is indeed rebindable, but only because an extra level of indirection has been silently added.
 the question is, why it's not in the type
 system.

I don't agree that that's even a reasonable question, because there is /not/ a syntax for "mutable reference to constant data" (not counting function parameters).
Feb 17 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 17/02/2008, none <z gg.com> wrote:
 So '_reference_ in D is similar to _pointer_ in C++', not reference in C++!

I disagree. I say a pointer is a pointer is a pointer, and that a pointer in C++ is similar (nay, identical) to a pointer in D.
 Since 'in C++, const C* p; // non-const pointer, const data' is allowed, why
 cannot we have 'non-const reference, const data' in D?

Why not just substitute your C++ pointer with a D pointer? If it's acceptable to use a pointer in C++, then use the same pointer in D.
Feb 17 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 17/02/2008, Matti Niemenmaa <see_signature for.real.address> wrote:
 Now what he wants is to convert the C++ "const C* b = a;" to D "??? b = a;"
with
 the semantics that b can be reassigned but the data behind it is unchangeable.

OK, I see that. But the thing is, it is never actually /necessary/ to do that. It is always possible to just write the code differently. There is really no pressing /need/ for "const class with mutable reference", since nobody has ever demonstrated a real-life use case that couldn't just be done differently. Certainly, no need pressing enough to demand a major change to generic programming and the type system. Also, remember that a C++ class is a D struct, and there's really no equivalent to a D class in C++.
Feb 17 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 18/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 When I write, "const(C) x;", I mean, "C is const but x is not."

That's just not getting it. x has type const(C), therefore x is const. I say again, there is no way to express "mutable reference to const data" in D, and any method you come up with to allow that would be (a) incredibly confusing, and (b) would break generic programming and/or the type system. And ... you never need to do that anyway. There's always a different way of writing the code.
Feb 17 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 17/02/2008, Sönke Ludwig <ludwig informatik_dot_uni-luebeck.de> wrote:
 Show me how I can maintain a simple map of class instances, which I get as
 const(C) from somewhere else, i.e. I have to accept the const here.

 example:

 const(C)[string] map;

 <- completely useless - you can do nothing with this construct

So change it to const(C)*[string] map; Now you've a map of mutable pointers, indexed by string. It's not hard.
 or to use a struct S { const(C) obj }

That probably won't work in the future. You're basically exploiting a bug there, and I imagine that soon it will be fixed.
Feb 17 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 18/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 One can safely assume that a variable x of type int with a value 4 in it
 is a mutable reference to an object 4 of type invariant(int).

Not unless you've redefined the word reference. At the ABI level, a reference is identical to a pointer. It occupies four bytes of space (eight on a 64 bit machine), and those four bytes contain an address. The difference between a reference and a pointer occurs at the syntax level, not the ABI level. What you've described there is not a reference at all - it's an int. A reference to an int would occupy eight bytes of memory (four for the reference, and four more for the int).
Feb 18 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 18/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 I'm talking from the standpoint of generalization which you so much care
 about.

Well sure, but if you use a word to mean one thing, and I understand it to mean something else, then we are not communicating. Communication has failed, and we are not understanding each other. Now, I'm no longer confident that I've got anything you've said, or that you've got anything I've said. We may have been talking at cross-purposes this whole thread. When I discuss generalization, I need to be able to say/think "this is how it's implemented for pointers; this is how it's implemented for references; this is how...", and so on. If you redefine all the words, then it just means we'll have to invent new ones in order to talk properly about the ABI. Ah well. No matter. :-) For what it's worth, I was arguing exactly what you are arguing now (that it should be allowable to modify the class reference of a const class), several weeks back. But Walter patiently explained why it was not a good idea, and his arguments were sound, so I got convinced. In other words, this isn't a new argument - you might want to check out some of that history (although I'm not sure I can advise on what to search for - "const" would probably yeild rather too many hits!)
Feb 18 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"none" wrote
 Why can't we define re-assignable const reference variable?
 i.e. the variable can be re-bind, but it's allowed to change the object it
 points to:

The problem here, none (is that your real name?), is that Walter has tried several times to achieve exactly that, and each time the syntax that he has created is either confusing or breaks something else. I think he has given up, claiming that there is no way to do it, but I think he is wrong in that respect. The problem stems from the fact that classes are inherently references and everything else is not. This in itself is not a problem, and is a distinction I happen to agree with, but the issue is that there is no easy way to const-modify the data of a class without also modifying the reference to the data. In order for this to work, there needs to be a syntax change. In all the responses to this thread, there is one point of view that I wish to rebut: "You can't have mutable references to const classes because any syntax change you have to make would break generic programming" Generic programming is already broken. That is, because classes *are* references and must be created from the heap using the new operator, you must treat them differently today than other types. For example, if I want to create a generic type that creates a new version of a type, or uses an array of the type, depending on whether the type is a class or a struct or something else, I need to use static if to determine this before creating the new type. I believe others have pointed out other examples, but it all comes down to the fact that yes, classes are inherently different than structs or other data types, and you have to treat them differently, so saying you cannot change the language because then you'd have to treat classes differently than other types is asinine. I have proposed two syntax changes that I believe would resolve the problem. One is to allow dereferencing a class type, such as: const(*C)* c; Which would translate to *C being the value of type C, and therefore const(*C)* being exactly what we want. This does not add any keywords, and also utilizes the * operator in familiar ways, and so it would be in my mind trivial to introduce and implement in the compiler. The only issue is that you must restrict the user from declaring a variable or return type of the dereferenced class type. i.e.: *C cvalue; would be illegal. The only drawback here is that code is going to get really ugly, but I can live with this because we can make templates that make the code look better. And in fact, this *fixes* to some degree the difference between classes and structs for generic programming, because if you want a reference type, you can do: valuetype(T) { static if(is(T == class)) alias *T valuetype; else alias T valuetype; } struct hasTailconstRef(T) { const(valuetype!(T)) * myref; } now, hasTailconstRef.myref is a tailconst reference to a type no matter if the type is a class, struct, int, or whatever. The other proposal I have made, which I like better but would be a more drastic syntax change, is to have a reference operator that "pulls out" the reference on a class. Other types would be treated like references to the type: struct S {} class C {} S & s; // equivalent to S * s C & c; // equivalent to C c int & i; // equivalent to int * i With this, you can then use the & operator to declare a tailconst reference to a class or struct equivalently: const(C) & c; const(S) & s; I have never read any arguments that correctly refute that these options would work. All I have ever heard is the generic programming argument, which is totally bogus, and "we've already tried tail-const, it doesn't work", which isn't an argument at all. I challenge anyone to tell me why either of these schemes would not work. -Steve
Feb 19 2008
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
here's my random thought on the subject (just wanted to throw those in
the mix...):
[warning: long post ahead]

the current (and all previous) const designs fail due to too narrow a
view on the issue.
when introducing a const system to a language than the language as a
whole should be examined and related concepts should be tweaked
accordingly. So here's my list of those related issues:

references -
in a high level language like D you should be able to just use refs
everywhere and pointer usage should be discouraged.  D as a systems
programming language, should however provide pointers for those
/advanced/low-level/potentially unsafe uses when you do systems
programming. that means that refs should be enhanced in D and pointers
should be used _only_ when truly you need them (for example for pointer
arithmetic).
for example: you should be able to use (and it should be the preferred
way!):
ref S s1 = new S(); // S is a struct
instead of the equivalent:
S* s2 = new S();
so you can eliminate pointers from almost all user code.
how does this affect const?
i suggest the following:
const(T) t = new T(); // this would be a mutable ref to const T
const(ref T) t = new T(); // this would be a const ref to const T
and you can also define "const T" as a shortcut for "const(ref T)"
i know that this means const(T) isn't the same as "const T" but I think
that this is a reasonable compromise (if it's consistent for all types)
another issue with refs is that i want to be able to return the lvalue
by specifying the return type to be ([const] ref T).

another  issue is a function call -
there are really only two modes for passing a parameter to a function:
1. you allow the function to change the outside(actual) parameter
2.  you do not allow the function to do that.
value vs. ref semantics is an implementation detail that user level code
shouldn't care about at all, and a special syntax should be provided and
used to distinguish the two only for advanced use cases.
so for example you could define:
void func_name(in T t);
in the above example the in keyword would represent the fact that you
cannot change the outside parameter.
how do you implement it? the compiler should have a very simple
heuristic that says that if T is a primitive value or a small struct
than just pass it by value and if it's a class instance than pass it as
a const ref. that is an optimization the compiler should provide.
now, what if i want to explicitly choose the parameter passing
semantics? that is also possible (but discouraged for user level code)
with a bit more verbose code and language rules. for example primitives
should be passed by value be default and you can change that with a ref
keyword. class instances are passed by ref so you can pass them by value
if they implement a dup method, and the user must call that explicitly.
so, other_func(someClassInstance.dup()); passes someClassInstance by value.
the second mode above is simply pass T by ref. also a bigger change
could be done so that the default mode when passing parameters is 1. so
you won't need to specify in and it would be implied.

for generic programming to work it needs to use the above "in" and "ref"
(i.e. modes 1 and 2) and not rely on a specific parameter passing
semantics unless this is what needed.

one last thing: i really liked oskar's orthogonal const proposal and
wish for it to be further explored.

anyway, that is just my shnekel (2 NIS) that i wanted to add to the
discussion.

--Yigal
Feb 19 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Yigal Chripun <yigal100 gmail.com> wrote:
 i suggest the following:
 const(T) t = new T(); // this would be a mutable ref to const T
 const(ref T) t = new T(); // this would be a const ref to const T
 and you can also define "const T" as a shortcut for "const(ref T)"
 i know that this means const(T) isn't the same as "const T" but I think
 that this is a reasonable compromise (if it's consistent for all types)
 another issue with refs is that i want to be able to return the lvalue
 by specifying the return type to be ([const] ref T).

Please, why invent new syntax ? const(T) t; // mutable ref to const T const{T t;} // const ref to const T, because of transitiviness This syntax even worked in one of the previous compiler versions! -- SnakE
Feb 19 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Sergey Gromov" wrote
 Please, why invent new syntax ?

 const(T) t; // mutable ref to const T
 const{T t;} // const ref to const T, because of transitiviness

 This syntax even worked in one of the previous compiler versions!

Yes, this is one of the trials of Walter to see if he could get tail-const to work. The problem is if T is not a reference/pointer (such as int). Now: const(T) t; T t; are equivalent, which is necessary to make that const system work. To have some variable declared as const(T), and then allow assignment to that variable is completely inconsistent with the fundamental usage of parentheses. If you have something defined as f(x), the entirety of x should be affected by f. This is why that system was dropped, as it should have been. -Steve
Feb 19 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Steven Schveighoffer <schveiguy yahoo.com> wrote:
 "Sergey Gromov" wrote
 Please, why invent new syntax ?

 const(T) t; // mutable ref to const T
 const{T t;} // const ref to const T, because of transitiviness

 This syntax even worked in one of the previous compiler versions!

Yes, this is one of the trials of Walter to see if he could get tail-const to work. The problem is if T is not a reference/pointer (such as int). Now: const(T) t; T t; are equivalent, which is necessary to make that const system work. To have some variable declared as const(T), and then allow assignment to that variable is completely inconsistent with the fundamental usage of parentheses. If you have something defined as f(x), the entirety of x should be affected by f. This is why that system was dropped, as it should have been.

If you create a generic algorithm which works with T, and declare something as const(T) t; then you effectively assert that you're going to replace t with different other Ts, but promise to keep integrity of a referenced T intact, if anything is referenced at all. This means that for int, which is not referencing anything, const(int) t; is completely and safely equivalent to int t; without breaking any contracts of your algorithm. Please provide an example where "const(int) x;" absolutely must mean "const int x;" for a generic algorithm to work properly. -- SnakE
Feb 20 2008
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Sergey Gromov wrote:
 Steven Schveighoffer <schveiguy yahoo.com> wrote:
 "Sergey Gromov" wrote
 Please, why invent new syntax ?

 const(T) t; // mutable ref to const T
 const{T t;} // const ref to const T, because of transitiviness

 This syntax even worked in one of the previous compiler versions!

to work. The problem is if T is not a reference/pointer (such as int). Now: const(T) t; T t; are equivalent, which is necessary to make that const system work. To have some variable declared as const(T), and then allow assignment to that variable is completely inconsistent with the fundamental usage of parentheses. If you have something defined as f(x), the entirety of x should be affected by f. This is why that system was dropped, as it should have been.

If you create a generic algorithm which works with T, and declare something as const(T) t; then you effectively assert that you're going to replace t with different other Ts, but promise to keep integrity of a referenced T intact, if anything is referenced at all. This means that for int, which is not referencing anything, const(int) t; is completely and safely equivalent to int t; without breaking any contracts of your algorithm. Please provide an example where "const(int) x;" absolutely must mean "const int x;" for a generic algorithm to work properly.

I wasn't aware of tat syntax. Please retract all comments I made in this thread, this is exactly what I was looking for.
Feb 20 2008
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Robert Fraser" wrote
 Sergey Gromov wrote:
 Steven Schveighoffer wrote:
 "Sergey Gromov" wrote
 Please, why invent new syntax ?

 const(T) t; // mutable ref to const T
 const{T t;} // const ref to const T, because of transitiviness

 This syntax even worked in one of the previous compiler versions!

tail-const to work. The problem is if T is not a reference/pointer (such as int). Now: const(T) t; T t; are equivalent, which is necessary to make that const system work. To have some variable declared as const(T), and then allow assignment to that variable is completely inconsistent with the fundamental usage of parentheses. If you have something defined as f(x), the entirety of x should be affected by f. This is why that system was dropped, as it should have been.

If you create a generic algorithm which works with T, and declare something as const(T) t; then you effectively assert that you're going to replace t with different other Ts, but promise to keep integrity of a referenced T intact, if anything is referenced at all. This means that for int, which is not referencing anything, const(int) t; is completely and safely equivalent to int t; without breaking any contracts of your algorithm. Please provide an example where "const(int) x;" absolutely must mean "const int x;" for a generic algorithm to work properly.

I wasn't aware of tat syntax. Please retract all comments I made in this thread, this is exactly what I was looking for.

Hm... maybe true, but this is not the supported syntax for the current or future compilers, so you will be stuck at D 2.004 or something (can't remember which one). Don't retract those comments just yet ;) -Steve
Feb 20 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Sergey Gromov" wrote
 Steven Schveighoffer wrote:
 "Sergey Gromov" wrote
 Please, why invent new syntax ?

 const(T) t; // mutable ref to const T
 const{T t;} // const ref to const T, because of transitiviness

 This syntax even worked in one of the previous compiler versions!

Yes, this is one of the trials of Walter to see if he could get tail-const to work. The problem is if T is not a reference/pointer (such as int). Now: const(T) t; T t; are equivalent, which is necessary to make that const system work. To have some variable declared as const(T), and then allow assignment to that variable is completely inconsistent with the fundamental usage of parentheses. If you have something defined as f(x), the entirety of x should be affected by f. This is why that system was dropped, as it should have been.

If you create a generic algorithm which works with T, and declare something as const(T) t; then you effectively assert that you're going to replace t with different other Ts, but promise to keep integrity of a referenced T intact, if anything is referenced at all. This means that for int, which is not referencing anything, const(int) t; is completely and safely equivalent to int t; without breaking any contracts of your algorithm. Please provide an example where "const(int) x;" absolutely must mean "const int x;" for a generic algorithm to work properly.

Sergey, I am not arguing that the functionality that you wish to have is invalid. In fact, I would have been ok with that syntax, and understood it once it was explained thoroughly. But I have to say that I agree with Janice and Walter on this one, it was very confusing. It just goes against the traditional usage of parentheses. The idea is correct, the syntax is confusing. Find another syntax. -Steve
Feb 20 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Steven Schveighoffer <schveiguy yahoo.com> wrote:
 "Sergey Gromov" wrote
 If you create a generic algorithm which works with T, and declare
 something as

 const(T) t;

 then you effectively assert that you're going to replace t with
 different other Ts, but promise to keep integrity of a referenced T
 intact, if anything is referenced at all.  This means that for int,
 which is not referencing anything,

 const(int) t;

 is completely and safely equivalent to

 int t;

 without breaking any contracts of your algorithm.  Please provide an
 example where "const(int) x;" absolutely must mean "const int x;" for a
 generic algorithm to work properly.

Sergey, I am not arguing that the functionality that you wish to have is invalid. In fact, I would have been ok with that syntax, and understood it once it was explained thoroughly. But I have to say that I agree with Janice and Walter on this one, it was very confusing. It just goes against the traditional usage of parentheses. The idea is correct, the syntax is confusing. Find another syntax.

The traditional usage of parentheses is this: foo(x). This means that a function foo is applied to its argument x, and not applied to everything else. You are probably talking about some different tradition. Please clarify. -- SnakE
Feb 20 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Sergey Gromov" wrote
 Steven Schveighoffer wrote:
 "Sergey Gromov" wrote
 If you create a generic algorithm which works with T, and declare
 something as

 const(T) t;

 then you effectively assert that you're going to replace t with
 different other Ts, but promise to keep integrity of a referenced T
 intact, if anything is referenced at all.  This means that for int,
 which is not referencing anything,

 const(int) t;

 is completely and safely equivalent to

 int t;

 without breaking any contracts of your algorithm.  Please provide an
 example where "const(int) x;" absolutely must mean "const int x;" for a
 generic algorithm to work properly.

Sergey, I am not arguing that the functionality that you wish to have is invalid. In fact, I would have been ok with that syntax, and understood it once it was explained thoroughly. But I have to say that I agree with Janice and Walter on this one, it was very confusing. It just goes against the traditional usage of parentheses. The idea is correct, the syntax is confusing. Find another syntax.

The traditional usage of parentheses is this: foo(x). This means that a function foo is applied to its argument x, and not applied to everything else. You are probably talking about some different tradition. Please clarify.

That is the usage I am referring to. I'll spell it out in English: class C means that C is equivalent to a "reference to a some data" that compromises the class that follows the given definition. If I interpret const(C) as constant C, or substituting for the english definition of C, a constant reference to some data, it is natural to assume that both the data AND the reference are const. It does not make intuitive sense that const(C) means reference to some constant data, where the const applies to the data, but not the reference, even though the reference is in C. It makes even less sense that const(int) means mutable int. X * means a pointer to X, so const(X) * means a pointer to a constant X, that makes sense. But since classes do not have the reference separated in syntax, there is no way to bring that piece of it outside the parentheses. This is why we need a new syntax, to make the definition clear and unambiguous. Your intuition may vary from 99% of us here, but that doesn't really matter. Your solution isn't intuitive, it is confusing, it would hinder the adoption of D 2. Walter is trying to attract as many coders as possible, not just you ;) -Steve
Feb 20 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Steven Schveighoffer <schveiguy yahoo.com> wrote:
 The traditional usage of parentheses is this: foo(x).  This means that a
 function foo is applied to its argument x, and not applied to everything
 else.  You are probably talking about some different tradition.  Please
 clarify.

That is the usage I am referring to. I'll spell it out in English: class C means that C is equivalent to a "reference to a some data" that compromises the class that follows the given definition.

At least, now I understand why we can't understand each other. For a declaration, C x; you imply reference in C, and I imply reference in x. const(C) x; // text written const(C) (ref x); // my understanding const(C ref) x; // your understanding No wonder we can't agree. Now I'm curious why the difference. To me, C is a class, and x is a reference. Why it's different for you ? -- SnakE
Feb 20 2008
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Sergey Gromov" wrote
 Steven Schveighoffer wrote:
 The traditional usage of parentheses is this: foo(x).  This means that 
 a
 function foo is applied to its argument x, and not applied to 
 everything
 else.  You are probably talking about some different tradition.  Please
 clarify.

That is the usage I am referring to. I'll spell it out in English: class C means that C is equivalent to a "reference to a some data" that compromises the class that follows the given definition.


Arghh! I can't type today! Compromises? :)
 At least, now I understand why we can't understand each other.

 For a declaration,

 C x;

 you imply reference in C, and I imply reference in x.

 const(C) x;   // text written
 const(C) (ref x); // my understanding
 const(C ref) x; // your understanding

 No wonder we can't agree.  Now I'm curious why the difference.  To me, C
 is a class, and x is a reference.  Why it's different for you ?

C is a class reference. There is no way to express the type of the class data, because D doesn't support value-type classes. I.e. there is no way to separate the reference from C. Whenever you see: class C {} in D, It's like the equivalent in C++ of class _C {}; typedef _C& C; But in D there is no way to get at the type _C. And D class references are rebindable. In D, the type never is in the variable, it's always in the type declaration. -Steve
Feb 20 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 The traditional usage of parentheses is this: foo(x).  This means that a
  function foo is applied to its argument x, and not applied to everything
  else.  You are probably talking about some different tradition.  Please
  clarify.

Happy to. It's the exact same tradition, except that the function in question is called "const", not "foo". It takes a type as it's input (e.g int), and yeilds a type as its output (in this case const int).

I see, thank you. It's really natural to think of const() as of a type- yielding function, sort of typeof(), which never came to my mind for some reason. But here is the caveat. As I understand from your posts, you state that "const(C) x;", as well as "const C x;", declares a variable of type (const C). But this is not true. It actually declares a variable of type "const ref to const C". Since declaration "C x;" declares x of type "ref to C", I seem to automatically assume that a declaration is actually C ref x; and therefore "const(C) x;" is actually "const(C) ref x;" which gives a variable x of type "ref to const C". A C++ habit, I presume. But it doesn't mean it's a bad approach. -- SnakE
Feb 20 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
what if we separate const into two keywords?
a) const would apply to data only. (transitively)
b) "bound" ( a new keyword ) would apply to references only  (transitively)

the idea is as follows:
Class c1 = new Class(); //all mutable
const Class c2 = new Class(); // mutable ref to const data
same as:
const(Class) c3 = new Class();
bound const Class c4 = new Class(); // bound (ie const) ref to const data
const int a; // a is const
bound int a; // illegal
bound Class c5 = new Class(); // const ref...
etc...
that's just a start of a start of an idea.
it could be developed much further... like where the "invariant" concept
would fit in with such a scheme.

-- Yigal

Janice Caron wrote:
 On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
   
  As I understand from your posts, you state that "const(C) x;", as well
  as "const C x;", declares a variable of type (const C).  But this is not
  true.  It actually declares a variable of type "const ref to const C".
     

No. Consider const(int) x; This does not declare a variable of type "const ref to const int", it declares a const int. I understand that you're thinking of classes, but even with classes, you're not quite getting what a class is. A class is /not/ the data on the heap. (If that were so, it would be possible to create an array of dereferenced class instances, and it isn't). No, a class /is the reference/ - and indirectly, what that reference points to. Thus, a const class /is/ a const reference. A "tailconst" class, on the other hand, would be a class (by which I mean, a reference) whose tail (the data on the heap) was const.

Feb 20 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 20/02/2008, Yigal Chripun <yigal100 gmail.com> wrote:
   
  bound const Class c4 = new Class(); // bound (ie const) ref to const data
     

Wouldn't the word "const" be superfluous in that example, since constancy is transitive? (If the reference is constant, the data /must/ be). It's an interesting idea, but I think, in general, if the compiler sees an array like const(T)[] x; then it has to mean that the array's contents are not modifiable. That rule shouldn't depend on T, it should simply be true always. Ditto all other collections.

As I've already said: it's only a start of an idea and should be further refined. that said, you are right that it's superfluous in the example above. I've mainly suggested a new keyword and the idea to seperate the two use cases of const. I'm not sure if the "bound" property should be transitive or not ( i suggested that it is but all options should be explored..) and it all depends on what semantics you want to achieve. maybe (probably) bound should also imply const on the referred data and const would imply all refs inside the data object to be bound. another tweak that can be done: intead of an error, if you apply bound to a value type (like a primitive) the compiler implicitly "casts" that to a const, that way you example above of an array would work for any type T. it could be used the same way in generic programming. --Yigal
Feb 20 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Yigal Chripun wrote:
 Janice Caron wrote:
   
 On 20/02/2008, Yigal Chripun <yigal100 gmail.com> wrote:
   
     
  bound const Class c4 = new Class(); // bound (ie const) ref to const data
     
       

constancy is transitive? (If the reference is constant, the data /must/ be). It's an interesting idea, but I think, in general, if the compiler sees an array like const(T)[] x; then it has to mean that the array's contents are not modifiable. That rule shouldn't depend on T, it should simply be true always. Ditto all other collections.

As I've already said: it's only a start of an idea and should be further refined. that said, you are right that it's superfluous in the example above. I've mainly suggested a new keyword and the idea to seperate the two use cases of const. I'm not sure if the "bound" property should be transitive or not ( i suggested that it is but all options should be explored..) and it all depends on what semantics you want to achieve. maybe (probably) bound should also imply const on the referred data and const would imply all refs inside the data object to be bound. another tweak that can be done: intead of an error, if you apply bound to a value type (like a primitive) the compiler implicitly "casts" that to a const, that way you example above of an array would work for any type T. it could be used the same way in generic programming. --Yigal

the problem is trying to apply a single rule to two very different concepts, so either way you get some type of a compromise. so why not just realize we have a duality in D and plan accordingly. thus instead of the current rule which states: for every type T, const(T) means that all of T is const and from it derived the rule that const T = const(T) and replace it with: if T has value semantics (primitive, union, struct, enum, ..) than the above rule applies. else, T is a reference type (a class) and const(T) means _only_ that the instance data is const, and its reference is _not_. what we get is that const(T)=const T only for value types. now, instead of my proposed "bound" keyword, let's just use final which is already a reserved keyword. final will always imply const too because of const transitivity (it'll be a superset of const). that way for value types the following are identical: const(S) s; final S s; final const S s; // could be reported by the compiler as redundant const S s; and for reference types (classes) we get the following behavior: const(C) c = new C(); // mutable ref to const data const C c = new C(); // same as above (compiler could issue a warning for lack of parens) final const(C) c = new C(); // const ref to const data final const C c = new C(); //same as above final C c = new C(); //same as above no issues with const(int) being mutable and other unintuitive syntaxes. generic programming - you can choose to use const(T)/const T for a weaker input condition or a final T for a stronger input condition that will enforce full constancy of T for all types. for function definitions: 'in' should be mapped to const as defined above, and as I suggested before the compiler should choose if he passes the parameter by value or by reference with optional control of that by the user (for system programming..) also it could be worthwhile to make in the default. D community - what do you think? am I totally wrong or is it a sane and more powerful solution to const? -- Yigal
Mar 06 2008
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Thu, 06 Mar 2008 20:21:32 +0100, Yigal Chripun <yigal100 gmail.com>  =

wrote:

 Yigal Chripun wrote:
 Janice Caron wrote:

 On 20/02/2008, Yigal Chripun <yigal100 gmail.com> wrote:


  bound const Class c4 =3D new Class(); // bound (ie const) ref to c=




 data

constancy is transitive? (If the reference is constant, the data /must/ be). It's an interesting idea, but I think, in general, if the compiler sees an array like const(T)[] x; then it has to mean that the array's contents are not modifiable. Th=



 rule shouldn't depend on T, it should simply be true always. Ditto a=



 other collections.

As I've already said: it's only a start of an idea and should be furt=


 refined.
 that said, you are right that it's superfluous in the example above.
 I've mainly suggested a new keyword and the idea to seperate the two =


 cases of const.
 I'm not sure if the "bound" property should be transitive or not ( i
 suggested that it is but all options should be explored..) and it all=


 depends on what semantics you want to achieve. maybe (probably) bound=


 should also imply const on the referred data and const would imply al=


 refs inside the data object to be bound.

 another tweak that can be done: intead of an error, if you apply boun=


 to a value type (like a primitive) the compiler implicitly "casts" th=


 to a const, that way you example above of an array would work for any=


 type T. it could be used the same way in generic programming.

 --Yigal

the problem is trying to apply a single rule to two very different concepts, so either way you get some type of a compromise. so why not just realize we have a duality in D and plan accordingly. thus instead=

 of the current rule which states:
 for every type T, const(T) means that all of T is const and from it
 derived the rule that const T =3D const(T)
 and replace it with:
 if T has value semantics (primitive, union, struct, enum, ..) than the=

 above rule applies.
 else, T is a reference type (a class)  and const(T) means _only_ that
 the instance data is const, and its reference is _not_.

 what we get is that const(T)=3Dconst T only for value types.
 now, instead of my proposed "bound" keyword, let's just use final whic=

 is already a reserved keyword.
 final will always imply const too because of const transitivity (it'll=

 be a superset of const). that way for value types the following are
 identical:
 const(S) s;
 final S s;
 final const S s; // could be reported by the compiler as redundant
 const S s;
 and for reference types (classes) we get the following behavior:
 const(C) c =3D new C(); // mutable ref to const data
 const C c =3D new C(); // same as above (compiler could issue a warnin=

 for lack of parens)
 final const(C) c =3D new C(); // const ref to const data
 final const C c =3D new C(); //same as above
 final C c =3D new C(); //same as above

 no issues with const(int) being mutable and other unintuitive syntaxes=

 generic programming - you can choose to use const(T)/const T for a
 weaker input condition or a final T for a stronger input condition tha=

 will enforce full constancy of T for all types.

 for function definitions: 'in' should be mapped to const as defined
 above, and as I suggested before the compiler should choose if he pass=

 the parameter by value or by reference with optional control of that b=

 the user (for system programming..)
 also it could be worthwhile to make in the default.

 D community - what do you think? am I totally wrong or is it a sane an=

 more powerful solution to const?

 -- Yigal

First problem I see is that it does not take into account D's two types = of const - const and invariant. -- Simen
Mar 08 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Simen Kjaeraas wrote:
 On Thu, 06 Mar 2008 20:21:32 +0100, Yigal Chripun <yigal100 gmail.com>
 wrote:

 Yigal Chripun wrote:
 Janice Caron wrote:

 On 20/02/2008, Yigal Chripun <yigal100 gmail.com> wrote:


  bound const Class c4 = new Class(); // bound (ie const) ref to
 const data

constancy is transitive? (If the reference is constant, the data /must/ be). It's an interesting idea, but I think, in general, if the compiler sees an array like const(T)[] x; then it has to mean that the array's contents are not modifiable. That rule shouldn't depend on T, it should simply be true always. Ditto all other collections.

As I've already said: it's only a start of an idea and should be further refined. that said, you are right that it's superfluous in the example above. I've mainly suggested a new keyword and the idea to seperate the two use cases of const. I'm not sure if the "bound" property should be transitive or not ( i suggested that it is but all options should be explored..) and it all depends on what semantics you want to achieve. maybe (probably) bound should also imply const on the referred data and const would imply all refs inside the data object to be bound. another tweak that can be done: intead of an error, if you apply bound to a value type (like a primitive) the compiler implicitly "casts" that to a const, that way you example above of an array would work for any type T. it could be used the same way in generic programming. --Yigal

the problem is trying to apply a single rule to two very different concepts, so either way you get some type of a compromise. so why not just realize we have a duality in D and plan accordingly. thus instead of the current rule which states: for every type T, const(T) means that all of T is const and from it derived the rule that const T = const(T) and replace it with: if T has value semantics (primitive, union, struct, enum, ..) than the above rule applies. else, T is a reference type (a class) and const(T) means _only_ that the instance data is const, and its reference is _not_. what we get is that const(T)=const T only for value types. now, instead of my proposed "bound" keyword, let's just use final which is already a reserved keyword. final will always imply const too because of const transitivity (it'll be a superset of const). that way for value types the following are identical: const(S) s; final S s; final const S s; // could be reported by the compiler as redundant const S s; and for reference types (classes) we get the following behavior: const(C) c = new C(); // mutable ref to const data const C c = new C(); // same as above (compiler could issue a warning for lack of parens) final const(C) c = new C(); // const ref to const data final const C c = new C(); //same as above final C c = new C(); //same as above no issues with const(int) being mutable and other unintuitive syntaxes. generic programming - you can choose to use const(T)/const T for a weaker input condition or a final T for a stronger input condition that will enforce full constancy of T for all types. for function definitions: 'in' should be mapped to const as defined above, and as I suggested before the compiler should choose if he passes the parameter by value or by reference with optional control of that by the user (for system programming..) also it could be worthwhile to make in the default. D community - what do you think? am I totally wrong or is it a sane and more powerful solution to const? -- Yigal

First problem I see is that it does not take into account D's two types of const - const and invariant. -- Simen

same rules. let's say that final defaults to const, BUT, if you can also use: --- final invariant(C) c= new C(); // a const ref to invariant data --- since both mutable and invariant implicitly cast to const, the above is sufficient. invariant data will contain invariant pointers only but they'll convert to const whenever necessary. also perhaps it's worth considering to remove the const T syntax from the language and always require parens. it will simplify usage and remove confusion for C++ programmers, because now both the syntax and the semantics are different so you can't confuse the meaning of const T. -- Yigal
Mar 08 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 09/03/2008, Yigal Chripun <yigal100 gmail.com> wrote:
   
  also perhaps it's worth considering to remove the const T syntax from
  the language and always require parens.
     

The bad news is, that would also disallow const x = 3; and const { int x = 3; int y = 4; } The problem here is that "const" is an "attribute" in the grammar, and therefore can be used anywhere "public", "static", "auto", "version(X)", and so on can be used. Changing that would be a major upheaval. The simplest route to re-assignable const reference is probably a template solution. In fact, if you look at the change log for D2.012, you'll see that it says: "std.typecons: added undocumented Rebindable in preparation for opImplicitCast." So, that sounds to me like as the language permits (as soon as we have opImplicitCast) we'll also have Rebindable!(T) - a rebindable class reference. We just need to hang on a little bit longer.

to emulate what ever you want. With that line of thought you don't need const as part of D either, as you can write template code to emulate that feature. yet, it is in the D language. the point is, that while Rebindable!(T) will solve the problem from an implementation point of view, it still remains a hack IMHO that shows a lacking type system. the fact that you can solve it with a template doesn't mean that it's a better, cleaner design, and while taste is a personal thing, for me that makes D look ugly and unpolished. Walter's enum solution to the manifest constant is a similar issue, while it does solve the problem, it remains IMHO an ugly hack. all I wanted to point out is that i want a more elegant solution, and that sometimes implementation driven design makes for bad decisions. while manifest constants and enums probably should have the same implementation (for example), that does not imply that the interface should be the same too. </rant> --Yigal
Mar 09 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 This means that for int,
 which is not referencing anything,

 const(int) t;

 is completely and safely equivalent to

 int t;

[...] One of the reasons that that syntax was ditched was because it was damnably confusing, and got many, many complaints from people who were using it. Not only did const(int) mean "mutable int", but even if you could get your head round that one, it turned out that const (int *)* p; and const (int **) p; were completely equivalent.

Pointers ruin my beautiful theory. :-) They are different. They do not generalize with references. So I propose to outlaw them completely: let them be an advanced, unsafe, low- level feature. So that pointers can never be const, and an explicit cast is /always/ required to convert from reference to pointer and back. To do this, references must be powerful enough to replace pointers for any high-level tasks. For instance, structs should always be passed by reference unless they are invariant and small.
 And I have absolutely no idea what the
 syntax for array of const ints was. Was it "const(int)[]"? Was it
 something else?

Easy. An array of const ints is "const(int[]) x;" You can't change ints, you can't change length nor ptr either. You can replace the whole thing though because x is not const (is outside the parentheses).
 I mean, it was just too confusing for words. No way
 can we /ever/ go back to that. For any type, T, const(T) absolutely
 /must/ mean that T is const.

My concept is very simple. Everything within the scope of const is const, everything outside is not. The confusion arises in two cases: a) trying to apply constness to primitive types. It really may be hard to understand why const(int) does nothing. But you don't need to use this syntax in everyday programming. What you really need is const(T) to behave consistently for different T. C++ allows to call a destructor for int for exactly the same reason, and b) when you use const for pointers and experience completely different behaviour from references. This is because pointers are too different, and you can't do without pointers in D at the moment. This should be fixed. -- SnakE
Feb 20 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
  > I mean, it was just too confusing for words. No way
  > can we /ever/ go back to that. For any type, T, const(T) absolutely
  > /must/ mean that T is const.

 My concept is very simple.  Everything within the scope of const is
  const, everything outside is not.

Not good enough. Everything inside the /brackets/ must be const. Otherwise, don't call it const, call it something else. Call it "tailconst" or "rebindable" or whatever, but don't call it const, because it isn't. I want to be able to introduce newbies to D and tell them how wonderful it is. I can't do that if const(T) doesn't mean const T. Really.

I am a newbie. I want consistency in the language. This is what attracts me.
 Back it D2.007 - the version of const that you like, but most people
 didn't, we had five different kinds of const. Count them. Five. They
 were:
 
     final T // head const
     const(T) // tail const
     const T // fully const
     invariant(T) // tail invariant
     invariant T // fully invariant

Can't see anything wrong here. Except that you omit the variable for some reason. Const only has meaning when applied to a reference. So: const(T) x; // tail const const{T x;} // full const Simple. Clean.
 ...and that's not counting the use of "in" in function declarations.
 It was just too silly for words.

As a newbie, I was really confused by a number of ways one can specify constness of an argument. This deserves a separate post. What I'm sure of, no "const," "const{}" or "const:" should be allowed there which all are different forms of a const attribute. Note that my confusion was about 2.010 which you probably consider 'fixed.'
 Right now, in D2.011, "invariant(char)[]" is a string. That makes
 absolutely perfect sense. Long may that remain.

Can't see any sense in making ptr and length variable. This makes strings 'somewhat const' to me. Not really const.
 What is your problem with just choosing a different word, or a
 different syntax? You'd get a lot more support for the idea that way.
 I certainly wouldn't be arguing with you in that case.

Actually, const() syntax is already different from const attribute. Probably if we had a tailconst attribute instead, so that it was possible to write tailconst { C c; int x; // either illegal, or not affected } this would be quite enough, and pretty consistent. This should of course deprecate the use of non-attribute const() form. But it would have a strange effect for structs anyway: with "tailconst S s;", which in my syntax is "const(S) s;", you won't be able to "s.whatever = newValue", but it would be legal to tailconst S s = initializer; S var = s; // contents copied var.whatever = newValue; s = var; // legal for tailconst otherwise generic algorithms won't work. I see your main concern is educational. This is resolved very simple, too: teach that /everything/ in D are objects, and everything is by reference. But compiler resolves many of the references at compile time. This is not strictly true because most references are resolved at language design time, but it doesn't invalidate the thesis itself. -- SnakE
Feb 20 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 Well then you really haven't understood it all. The characters of a
 string may be stored in a ROM segment, and for that reason, the
 characters of a string may not be modified.

Of course I understand that.
 But there is no reason why
 you can't modify slices which merely point into that ROM data.

Modifying a slice which points i-dont-know-where feels unsafe to me. I won't use this feature as long as I can.
 Actually, const() syntax is already different from const attribute.

Again, you're wrong. The following two lines const int x; const(int)x; are identical in meaning. And that remains true for any type, no matter how complicated, e.g.

I'm talking about syntax, not semantics. const is an attribute, const() is a function. I believe that the language lexer was specifically modified to allow for this functional const notation.
  Probably if we had a tailconst attribute instead, so that it was
  possible to write

  tailconst
  {
         C c;
         int x;  // either illegal, or not affected
  }

That is something which no one has ever suggested before! Oh, plenty of people have suggested tailconst, but it's generally expected to be more along the lines of tailconst(C) c;

This is strange. tailconst attribute merges smoothly with the language design. On the contrary, the functional attributes, const included, look strange.
  This should of
  course deprecate the use of non-attribute const() form.

You can't! How could you declare const(int[][][])[][][] x; without the brackets? (Not that I have a use for that particular type, but hopefully you see the point).

This use of "partial const", in addition to "full const" and "tail const", is specific to multi-dimentional arrays. No other types can make use of it. With arrays, you probably can do with aliasing: alias const int[][][] constArr; constArr[][][] x; -- SnakE
Feb 20 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Janice Caron <caron800 googlemail.com> wrote:
 On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 Modifying a slice which points i-dont-know-where feels unsafe to me.  I
  won't use this feature as long as I can.

string s = "hello world"; s = s[6..$]; In what way is this unsafe?

This is Ok. It's changing the .length directly which I don't like, and which is actually unpredictable.
 
  > How could you declare
  >
  >     const(int[][][])[][][] x;
  >
  > without the brackets?

 This use of "partial const", in addition to "full const" and "tail
  const", is specific to multi-dimentional arrays.  No other types can
  make use of it.

Sure they can const(int***)*** x; or, perhaps more usefully int[const(int)[]] map; There are an infinity of possible examples.

Ok I see. Rounding up our discussion, all the argument was because I misunderstood the type system. And, judging from this syntax is discussed often and even was supported in the compiler for some time, I'm not alone in my misunderstanding. Seems like an educational problem. -- SnakE
Feb 21 2008
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Janice Caron wrote:
     char[] s;
     s.length = 5;
     /* blah */
     s.length = 25;
     /* blah */
     s.length = 10;
 
 etc. In this case, the buffer will be reallocated somewhere else, and
 the original contents copied so you don't lose anything. It's exactly
 like calling realloc() in C or C++.

Well, there's one difference between what happens there and realloc(); the latter deallocates the original while D leaves the original in place in case there are other references to it. (This is more workable in D than C/C++ though, since if there aren't the GC will clean it up)
Feb 21 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Janice Caron <caron800 googlemail.com> wrote:
  If you want the array also to be invariant - no problemo! Just put the
  array inside the brackets too - "invariant(char)[]".

That was a typo. Allow me to correct myself. If you want the array also to be invariant - no problemo! Just put the array inside the brackets too - "invariant(char[])".
Feb 20 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 This means that for int,
 which is not referencing anything,

 const(int) t;

 is completely and safely equivalent to

 int t;

Sergey, there may well be merit in the idea of having some form of syntax which implies rebindable const classes. I'm not necessarily against the idea. However, I most wholeheartedly /am/ against allowing any kind of move which suggests that const(int) shall mean mutable int! One of the reasons that that syntax was ditched was because it was damnably confusing, and got many, many complaints from people who were using it. Not only did const(int) mean "mutable int", but even if you could get your head round that one, it turned out that const (int *)* p; and const (int **) p; were completely equivalent. And I have absolutely no idea what the syntax for array of const ints was. Was it "const(int)[]"? Was it something else? I mean, it was just too confusing for words. No way can we /ever/ go back to that. For any type, T, const(T) absolutely /must/ mean that T is const. So ... if you want widespread, across-the-board support for rebindable class references, please, please, please choose a different syntax to campaign for. Several syntaxes have been suggested in the past, including const(T)& /* by me */ ref const(T) const(*T)* tailconst(T) TailConst!(T) and so on, any or all of which could work, if Walter finds a way to make it possible. But not const(T). Anything else, just not const(T).
Feb 20 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
  > I mean, it was just too confusing for words. No way
  > can we /ever/ go back to that. For any type, T, const(T) absolutely
  > /must/ mean that T is const.

 My concept is very simple.  Everything within the scope of const is
  const, everything outside is not.

Not good enough. Everything inside the /brackets/ must be const. Otherwise, don't call it const, call it something else. Call it "tailconst" or "rebindable" or whatever, but don't call it const, because it isn't. I want to be able to introduce newbies to D and tell them how wonderful it is. I can't do that if const(T) doesn't mean const T. Really. Back it D2.007 - the version of const that you like, but most people didn't, we had five different kinds of const. Count them. Five. They were: final T // head const const(T) // tail const const T // fully const invariant(T) // tail invariant invariant T // fully invariant ...and that's not counting the use of "in" in function declarations. It was just too silly for words. Right now, in D2.011, "invariant(char)[]" is a string. That makes absolutely perfect sense. Long may that remain. What is your problem with just choosing a different word, or a different syntax? You'd get a lot more support for the idea that way. I certainly wouldn't be arguing with you in that case.
Feb 20 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 The traditional usage of parentheses is this: foo(x).  This means that a
  function foo is applied to its argument x, and not applied to everything
  else.  You are probably talking about some different tradition.  Please
  clarify.

Happy to. It's the exact same tradition, except that the function in question is called "const", not "foo". It takes a type as it's input (e.g int), and yeilds a type as its output (in this case const int).
Feb 20 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
  As I understand from your posts, you state that "const(C) x;", as well
  as "const C x;", declares a variable of type (const C).  But this is not
  true.  It actually declares a variable of type "const ref to const C".

No. Consider const(int) x; This does not declare a variable of type "const ref to const int", it declares a const int. I understand that you're thinking of classes, but even with classes, you're not quite getting what a class is. A class is /not/ the data on the heap. (If that were so, it would be possible to create an array of dereferenced class instances, and it isn't). No, a class /is the reference/ - and indirectly, what that reference points to. Thus, a const class /is/ a const reference. A "tailconst" class, on the other hand, would be a class (by which I mean, a reference) whose tail (the data on the heap) was const.
Feb 20 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 Const only has meaning when applied to a reference.

Hardly. Const is perfectly meaningful when applied to a value!
 So:

         const(T) x;     // tail const
         const{T x;}     // full const

  Simple.  Clean.

Yuk! Fortunately, I just don't believe Walter will go for that.
  > Right now, in D2.011, "invariant(char)[]" is a string. That makes
  > absolutely perfect sense. Long may that remain.

 Can't see any sense in making ptr and length variable.  This makes
  strings 'somewhat const' to me.  Not really const.

Well then you really haven't understood it all. The characters of a string may be stored in a ROM segment, and for that reason, the characters of a string may not be modified. But there is no reason why you can't modify slices which merely point into that ROM data. Don't you see that "invariant(char)" means exactly that!? Chars that are invariant - it doesn't get much simpler. And "[]" means "array of". Hence, "invariant(char)[]" = "array of invariant chars". If you want the array also to be invariant - no problemo! Just put the array inside the brackets too - "invariant(char)[]". I'm not describing anything exotic here - I'm describing the system as it is now - simple, elegant, and obvious.
 Actually, const() syntax is already different from const attribute.

Again, you're wrong. The following two lines const int x; const(int)x; are identical in meaning. And that remains true for any type, no matter how complicated, e.g. const int[]* p; const(int[]*)p; Again, completely identical. You are partly correct in that "const" /when applied to member functions/ means something different, and that is something which bugs me a little. But member functions aside, the const attribute just makes things const, which is exactly what you'd expect.
  Probably if we had a tailconst attribute instead, so that it was
  possible to write

  tailconst
  {
         C c;
         int x;  // either illegal, or not affected
  }

That is something which no one has ever suggested before! Oh, plenty of people have suggested tailconst, but it's generally expected to be more along the lines of tailconst(C) c;
  This should of
  course deprecate the use of non-attribute const() form.

You can't! How could you declare const(int[][][])[][][] x; without the brackets? (Not that I have a use for that particular type, but hopefully you see the point).
 "tailconst S s;", which
  in my syntax is "const(S) s;"

If you stop calling it const(S), I'll stop arguing with you. :-). I'm still on the fence as to its utility, and Walter still says he can't implement it, but if people want it, and Walter turns out to be wrong, I certainly won't complain.
  I see your main concern is educational.   This is resolved very simple,
  too: teach that /everything/ in D are objects, and everything is by
  reference.

Teaching lies is just plain wrong. D has both value type and reference types.
Feb 20 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
 Don't know whether that's the case or not currently.

I've been using it for a while now, and on the whole I find it very simple, elegant, and obvious.
Feb 18 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 19/02/2008, Derek Parnell <derek psych.ward> wrote:
 Ok given this position and that 'string' is 'invariant(char)[]' then

    string s;

 makes 's' a reference to an immutable array,

No, s is an array of immutable chars. Put another way, as I am sure you are well aware, s is a value type, whose layout is struct { invariant(char)* ptr; uint length; }
 If so, does that mean that I cannot bind 's' to another
 literal

s isn't a reference, and so the concept of "binding" doesn't make sense for it. What you can do is reassign it with another value. Basically, everything works, exactly as is it should.
Feb 19 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 19/02/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 So please explain this:

 <snip>
 // outputs 2 2 1 2

Simple. If you wanted the output to be 2 2 2 2 then the second function should have been declared void assignit(ref C src, ref C value) But you missed out the "ref"s, and so src was merely a local copy.
 In both cases, the assignit function is taking reference types as arguments.

But in the second case, the reference is /local/. Function parameters declared without "ref" are just local variables. Assigning src will modify the local copy (of the reference), but you need the "ref" keyword if you want to modify the original (reference).
 Both do the same thing,

No, they don't. Passing a value by reference is not the same thing as passing a reference by value.
Feb 19 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  bound const Class c4 = new Class(); // bound (ie const) ref to const data

Wouldn't the word "const" be superfluous in that example, since constancy is transitive? (If the reference is constant, the data /must/ be). It's an interesting idea, but I think, in general, if the compiler sees an array like const(T)[] x; then it has to mean that the array's contents are not modifiable. That rule shouldn't depend on T, it should simply be true always. Ditto all other collections.
Feb 20 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 20/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 Modifying a slice which points i-dont-know-where feels unsafe to me.  I
  won't use this feature as long as I can.

string s = "hello world"; s = s[6..$]; In what way is this unsafe?
  > How could you declare
  >
  >     const(int[][][])[][][] x;
  >
  > without the brackets?

 This use of "partial const", in addition to "full const" and "tail
  const", is specific to multi-dimentional arrays.  No other types can
  make use of it.

Sure they can const(int***)*** x; or, perhaps more usefully int[const(int)[]] map; There are an infinity of possible examples.
  With arrays, you probably can do with aliasing:

         alias const int[][][] constArr;
         constArr[][][] x;

And this to you would be a good thing?
Feb 20 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 21/02/2008, Sergey Gromov <snake.scaly gmail.com> wrote:
 Janice Caron <caron800 googlemail.com> wrote:
 This is Ok.  It's changing the .length directly which I don't like, and
  which is actually unpredictable.

It's always safe, and it's always predictable. It's just not very useful. string s = "hello"; s.length = 10; s will now contain "hello" followed by five default-value chars. Given that string contents are immutable, that's completely pointless. But it makes a lot more sense with non-constant arrays. For example: char[] s; s.length = 5; /* blah */ s.length = 25; /* blah */ s.length = 10; etc. In this case, the buffer will be reallocated somewhere else, and the original contents copied so you don't lose anything. It's exactly like calling realloc() in C or C++.
Feb 21 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 09/03/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  also perhaps it's worth considering to remove the const T syntax from
  the language and always require parens.

The bad news is, that would also disallow const x = 3; and const { int x = 3; int y = 4; } The problem here is that "const" is an "attribute" in the grammar, and therefore can be used anywhere "public", "static", "auto", "version(X)", and so on can be used. Changing that would be a major upheaval. The simplest route to re-assignable const reference is probably a template solution. In fact, if you look at the change log for D2.012, you'll see that it says: "std.typecons: added undocumented Rebindable in preparation for opImplicitCast." So, that sounds to me like as the language permits (as soon as we have opImplicitCast) we'll also have Rebindable!(T) - a rebindable class reference. We just need to hang on a little bit longer.
Mar 08 2008