digitalmars.D - Invariant doesn't apply to declared symbols
- Jason House (36/36) Nov 29 2007 This is the one thing that really bugs me about const...
- Janice Caron (11/16) Nov 30 2007 We've kind of been through all of this before. I haven't completely
- Janice Caron (21/26) Nov 30 2007 I suppose there is still an issue which hasn't gone away, which is
- Janice Caron (41/41) Nov 30 2007 Let's consider how this would be done in C++. To get a class on the
- Walter Bright (9/38) Nov 30 2007 I think this suggestion is over-engineered. It's much simpler to say
- Janice Caron (6/14) Nov 30 2007 I don't necessarily care if you use that particular syntax that I
- Walter Bright (14/58) Nov 30 2007 No. See my other reply to Janice, what we have here is a rebindable
- Janice Caron (2/5) Nov 30 2007 That's not simple. S and (S) should be the same thing.
- Jason House (22/29) Nov 30 2007 First of all, the docs don't explicitly talk about this. That really HA...
- Janice Caron (5/9) Nov 30 2007 Under D2.007 you would be correct. I don't know if "final" has
- Walter Bright (3/4) Nov 30 2007 But then you'd have to give up:
- Janice Caron (9/13) Nov 30 2007 ...unless you added an extra rule to re-introduce it. That's why I
- Jason House (8/13) Nov 30 2007 I've been thinking more and I don't really like the whole storage class
- Janice Caron (18/27) Nov 30 2007 Or...
- Janice Caron (75/76) Nov 30 2007 Scrap that last idea. I thought of a better one...
This is the one thing that really bugs me about const... The docs say that the following is legal invariant(S) s; s = ...; //legal This seems to add logical inconsistency... invaraint(int*) p; p = ...; //legal invariant(char*)** p; **p = ...; //illegal It seems that invariant(T) changes based on context! (All examples above come from http://www.digitalmars.com/d/const3.html) Unfortunately, it doesn't stop here for me. It seems that "const(char) *p;" and "const(char*) p;" have identical meanings even though the syntax is different. Also, "const(char) p;" and "const(S) s;" have different meanings. In the first, assignment to p is disallowed. In the second, assignment to s is allowed. When going into generic coding or upgrading to more complex data types in code, this type of thing *has* to bite someone eventually. Also, how I define a const point to const data? As I understand it, this is impossible. "const int* p;" and "const(int*) p;" both mean a non-const pointer to const data. While I don't explicitly see const(T) being the same as const T in the docs, Walter has said this on the NG. All of this leads me to ask wonder why this style of functionality is done. It seems like a whole lot of ambiguity just to allow "const(S) s;" to have constant members but a non-constant reference. I appreciate the argument that "const(S) s" is all that is syntactially available without any modification, but I encourage some thought into using a novel syntax for this. I have not come up with a good syntax for this. The best I've got is const(S) s; // can not assign to s or s.foo const(S)& s; // can assign to s, but not s.foo const(S)* s; // can assign to s, but not *s I think the last case may be the common case for working with foreign (C?) API's. I will admit to not understanding the argument of "in order to use structs as wrappers for builtin types, ...". I don't know of a common use case for this.
Nov 29 2007
On Nov 30, 2007 7:04 AM, Jason House <jason.james.house gmail.com> wrote:This is the one thing that really bugs me about const...We've kind of been through all of this before. I haven't completely grokked how things are changes since the last upheaval, but for what it's worth...invariant(S) s; s = ...; //legalI thought Walter said we'd got rid of tail-const, and now there's just const? I thought that const(T) now meant that T is fully const? But you're right. The docs say otherwise. What's the deal?It seems that "const(char) *p;" and "const(char*) p;" have identical meanings even though the syntax is different.That was one of things we all complained about. I really thought Walter was getting rid of that one, and that, from now on, const(T) would mean "T is fully const". Apparently not. Walter, could you clarify? Is this being sorted out?
Nov 30 2007
I suppose there is still an issue which hasn't gone away, which is this: Classes are passed by reference, so they are refence objects, just like pointers. However, /unlike/ pointers, there's no existing syntax to "dereference" them and refer directly to the bytes on the heap. So there's an ambiguity, in that when you write const(C) where C is a class, are you saying (a) the bytes on the heap are const, or (b) both the reference, and the bytes on the heap, are const? Clearly, my preference would be (b), since it seems so /intuitive/ that const(T) must mean that T is const. However, this does beg the question, is there a need for a syntax to descibe (a)? I suggest const(C.) observe the dot after the C. To my mind at least, that clearly denotes "members of", hence this means "members of C are const" (but the reference itself is not). That's not the only possible syntax, of course, and I don't want to get hung up on it. The point is, it's an issue that hasn't been properly addressed, and I believe it should be, if we are ever to "unconfuse" const.invariant(S) s; s = ...; //legalI thought Walter said we'd got rid of tail-const, and now there's just const? I thought that const(T) now meant that T is fully const? But you're right. The docs say otherwise. What's the deal?
Nov 30 2007
Let's consider how this would be done in C++. To get a class on the heap, you'd do C * c = new C(); To make the data only const, you'd do C const * c; (or "const C * c" but let's not go into C++'s confusing alternatives here). To make both the data and the pointer const, you'd do C const * const c; (or "const C * const c" but again, let's not go into C++'s confusing alternatives here). If classes in D used the syntax of pointers, it would be easy. We could say: const(C)* // just the data is const const(C*) // the data and the pointer are both const Unfortunately, that won't fly, because we /don't/ use pointer-syntax. The pointer is /implicit/. So the dereferencing asterisk is also implicit. It's a nasty problem for us, because of course we still want the declaration to read like we're declaring a variable c with type C (with some degree of constness applied somewhere). So any syntax which makes that NOT obvious is one we should be wary of. Suggestions like const(&C) c don't work for me, because it looks like we want c have type "address of C". Fortunately, there is an answer. Well, we may not have pointers, but C is a reference type, right? And we do have the "ref" keyword. Thus, we could distinguish const(C) // just the data is const const(ref C) // the data and the pointer are both const That one /kind of/ works - but it does violate the principle that const(T) x; x = y; should always be a compile error. (If x is fully const, how can it be assigned?). So here's my latest and greatest idea. What about: const(C)ref // just the data is const const(C) // the data and the pointer are both const This would give us const(C) c; const(C) ref d; c = new C: // Error; d = new C; // OK d.x = 100; // Error That one works for me. It's my favorite suggestion so far. It does what we need, /and/ it allows const to follow a general pattern.
Nov 30 2007
Janice Caron wrote:Fortunately, there is an answer. Well, we may not have pointers, but C is a reference type, right? And we do have the "ref" keyword. Thus, we could distinguish const(C) // just the data is const const(ref C) // the data and the pointer are both const That one /kind of/ works - but it does violate the principle that const(T) x; x = y; should always be a compile error. (If x is fully const, how can it be assigned?). So here's my latest and greatest idea. What about: const(C)ref // just the data is const const(C) // the data and the pointer are both const This would give us const(C) c; const(C) ref d; c = new C: // Error; d = new C; // OK d.x = 100; // Error That one works for me. It's my favorite suggestion so far. It does what we need, /and/ it allows const to follow a general pattern.I think this suggestion is over-engineered. It's much simpler to say that a declaration, even one with const type, is rebindable unless it has a storage class of const. const C c; const(C) d; c = new C: // Error; d = new C; // OK d.x = 100; // Error
Nov 30 2007
On Nov 30, 2007 11:06 AM, Walter Bright <newshound1 digitalmars.com> wrote:I think this suggestion is over-engineered. It's much simpler to say that a declaration, even one with const type, is rebindable unless it has a storage class of const. const C c; const(C) d; c = new C: // Error; d = new C; // OK d.x = 100; // ErrorI don't necessarily care if you use that particular syntax that I invented or not. Feel free to invent a better one (although obviously, I like mine). However, no disrespect intended, but yours is terrible! C should mean exactly the same thing as (C). "const C" and "const (C)" should be interchangable.
Nov 30 2007
Jason House wrote:This is the one thing that really bugs me about const... The docs say that the following is legal invariant(S) s; s = ...; //legalYes.This seems to add logical inconsistency... invaraint(int*) p; p = ...; //legal invariant(char*)** p; **p = ...; //illegal It seems that invariant(T) changes based on context! (All examples above come from http://www.digitalmars.com/d/const3.html)No. See my other reply to Janice, what we have here is a rebindable variable.Unfortunately, it doesn't stop here for me. It seems that "const(char) *p;" and "const(char*) p;" have identical meanings even though the syntax is different.No, the types are different. If you take the address of p, you'll see the type difference.Also, "const(char) p;" and "const(S) s;" have different meanings. In the first, assignment to p is disallowed. In the second, assignment to s is allowed.Not true, assignment to both is allowed. To disallow rebindable variables, give them const storage class.When going into generic coding or upgrading to more complex data types in code, this type of thing *has* to bite someone eventually. Also, how I define a const point to const data? As I understand it, this is impossible. "const int* p;" and "const(int*) p;" both mean a non-const pointer to const data. While I don't explicitly see const(T) being the same as const T in the docs, Walter has said this on the NG.No, the types are the same, but the storage class is different.All of this leads me to ask wonder why this style of functionality is done. It seems like a whole lot of ambiguity just to allow "const(S) s;" to have constant members but a non-constant reference.Consider const(C) where C is a class type. If declarations of type const(C) were not rebindable, they become impossible to use.I appreciate the argument that "const(S) s" is all that is syntactially available without any modification, but I encourage some thought into using a novel syntax for this. I have not come up with a good syntax for this. The best I've got is const(S) s; // can not assign to s or s.foo const(S)& s; // can assign to s, but not s.foo const(S)* s; // can assign to s, but not *s I think the last case may be the common case for working with foreign (C?) API's. I will admit to not understanding the argument of "in order to use structs as wrappers for builtin types, ...". I don't know of a common use case for this.It's much simpler: const(S) s; // can rebind s const S s; // cannot rebind s
Nov 30 2007
On Nov 30, 2007 10:58 AM, Walter Bright <newshound1 digitalmars.com> wrote:It's much simpler: const(S) s; // can rebind s const S s; // cannot rebind sThat's not simple. S and (S) should be the same thing.
Nov 30 2007
Walter Bright wrote:Jason House wrote:First of all, the docs don't explicitly talk about this. That really HAS to get fixed. Also, "const(char)*a" and "const(char*)a" having different reference types confuses me. In both cases, I can assign to a, but can't assign to *a. They really look the same. This should be documented too. Probably on a related note, "const(char) a" allows assignment to a (it's "rebindable"), but as far as I can tell, value types simply get overwritten when changed. I'd argue that such a definition is useless, confusing, error-prone, and should be a compile-time error. Maybe the problem is that the storage class specification is too easily confused with other const declarations. As much as it may draw tossing of rotten fruit at me, I think we need the final keyword. Would it be safe to say that "const int* x;" and "final const(int*) x;" are the same thing? Similarly that "invariant int* y;" and "final invariant(int*) y;" are the same thing? I'd argue that const and invariant should never be a storage class. At the risk of even more rotten fruit, this aligns well with the C++ interpretation of "const char*" as a function parameter. Of course, if a function has a return type of const(char*), it makes total sense that it should be assignable to a variable. I think it all works and should, in the end, be less confusing.This is the one thing that really bugs me about const... <snip>It's much simpler: const(S) s; // can rebind s const S s; // cannot rebind s
Nov 30 2007
On 11/30/07, Jason House <jason.james.house gmail.com> wrote:Would it be safe to say that "const int* x;" and "final const(int*) x;" are the same thing? Similarly that "invariant int* y;" and "final invariant(int*) y;" are the same thing?Under D2.007 you would be correct. I don't know if "final" has actually been dropped yet, but I'm assuming it has. It's certainly on its way out.I'd argue that const and invariant should never be a storage class.That's the conclusion I came to.
Nov 30 2007
Jason House wrote:I'd argue that const and invariant should never be a storage class.But then you'd have to give up: const x = 3;
Nov 30 2007
On 11/30/07, Walter Bright <newshound1 digitalmars.com> wrote:Jason House wrote:...unless you added an extra rule to re-introduce it. That's why I threw in rule (5) in my list of proposed changes. Let const x = y; be syntactic sugar for const(typeof(y)) x = y; without having to make const an attribute. See the full list of 5 on whatever thread it's on to see how it all might hang together if you went for it.I'd argue that const and invariant should never be a storage class.But then you'd have to give up: const x = 3;
Nov 30 2007
Walter Bright wrote:Jason House wrote:I've been thinking more and I don't really like the whole storage class thing anyway. I posted in the "const ideas" thread with my latest thoughts. In whatever situation, "const x = 3;" would be syntactically invalid under any scheme, so bolting it in to be a pure constant seems just fine. Personally, I'd never use it, but no language should ever be designed to be exactly what I'd use and include nothing else :)I'd argue that const and invariant should never be a storage class.But then you'd have to give up: const x = 3;
Nov 30 2007
So here's my latest and greatest idea. What about: const(C)ref // just the data is const const(C) // the data and the pointer are both const This would give us const(C) c; const(C) ref d; c = new C: // Error; d = new C; // OK d.x = 100; // ErrorOr... Since we like function-style type constructors in D: ref(const(C)) // just the data is const const(C) // the data and the pointer are both const In the interests of plain old fashioned common sense, the following four lines should all be exactly equivalent and interchangeable... ref const C c; ref const(C) c; ref(const C) c; ref(const(C)) c; In each case, we are declaring c to be of type C, and then saying that c is const - except for the actual reference, which isn't. Note that ref-as-a-type-constructor would naturally be forbidden for non-reference types, so ref(const(int)) n; would be a syntax error, because int isn't a reference type. (...at least, until we get references in D, at which point we will then be able to legalise it).
Nov 30 2007
On Nov 30, 2007 12:43 PM, Janice Caron <caron800 googlemail.com> wrote:Scrap that last idea. I thought of a better one... I started thinking ahead, to the time, however far off it is in the future, when D has C++-style references, and I started to think about what the syntax would be, in relation to const etc. The thing about references is, unlike pointers, they don't chain. You can chain pointers indefinitely - you can have a pointer to a pointer to a pointer to... ad infinitum, and each new level of indirection creates a new type. But it doesn't work like that with references. A reference to a reference is just a reference. T& is the same type as T&&, which is the same type as T&&&&&&&&&&&&&&&&&&&, and so on. It's all pretty simple and straightforward really. For any non-reference type T, T& is a new type: a reference-to-T. But for any reference type T, T& is the same thing as T. The rest follows logically. Now let's think ahead, and /imagine/ that we have C++-style references in D. Then... int x; // x is an int int* x; // x is a pointer to int int& x; // x is a reference to int And with const thrown in... const(int)* x; // x is a pointer to const int const(int)& x; // x is a reference to const int And then it occurred to me that this notation completely solves the problem of how to make existing reference types (e.g. classes) const. It is obvious that class C { int x; } const(C)& // c is a mutable reference to a const C const(C&) // c is a const reference to a const C but the cool bit is the final step. Since C& is the same thing as C (because C is a reference type), it follows that we can simplify the second one to const(C) // c is a const reference to a const C ...which of course, is the existing syntax, albeit with a different (and more obvious) meaning. So "C" is the same thing as "C&", and "const(C)" is the same thing as "const(C&)". What this means is that we now have a simple, and future-extendable syntax for distinguishing between const references and const data. const(C) c = new C; const(C)& d = new C; c = new C; // Error d = new C; // OK d.x = 100; // Error The syntax is "future-proof", in that, one day, we might get C++ references, and if and when we do, this syntax will cover it. struct S { int x; } const(S&) s; const(S)& t; s = new S; // Error; t = new S; // OK; t.x = 100; // Error; Observe that because a struct is not a reference type, S is a different type from S&. However, S&& is exactly the same type as S&. That's just the way that references work. I'm not pushing for references in D. I hope they'll come one day, but it's not a priority for me. The point of this post is that this /syntax/ for constness-of-classes would be consistent with future expansion. Compare this with the current syntax, which is: const(C) c; /* c is a reference to const C */ const(int) n; /* n is a mutable int */ const C c; /* c is a const reference to const C */ const int n; /* n is a const int */ It seems clear to me that the proposed new syntax would be better const(C) c; /* c is a const reference to const C */ const(C)& c; /* c is a mutable reference to const C */ const(int) n; /* n is a const int */ const C c; /* same as const(C) c; */ const int n; /* same as const(int) n; */So here's my latest and greatest idea. What about:
Nov 30 2007