www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D const design rationale

reply Walter Bright <newshound1 digitalmars.com> writes:
http://www.digitalmars.com/d/const.html
Jun 20 2007
next sibling parent eao197 <eao197 intervale.ru> writes:
On Thu, 21 Jun 2007 10:36:12 +0400, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 http://www.digitalmars.com/d/const.html

Thanks for the article! Small typo in C++ example: const_cast needs type in angle brackets: void foo(const int *p) { int *q =3D const_cast< int * >(p); *q =3D 4; } -- = Regards, Yauheni Akhotnikau
Jun 21 2007
prev sibling next sibling parent BLS <nanali nospam-wanadoo.fr> writes:
Walter Bright schrieb:
 http://www.digitalmars.com/d/const.html

THANK YOU ! Just speaking for myself : I have learned a lot.The article is very clear and understandable. (should be nominated for the "Art of technical writing award") Well done folks. Bjoern
Jun 21 2007
prev sibling next sibling parent reply Daniel919 <Daniel919 web.de> writes:
Great article !
Shows the weaknesses of C++'s const implementation and
points out D's improvements and advantages.
Very attractive and easy to understand !

Also the need for having 3 different keywords should be clear now.


PS:
http://www.digitalmars.com/d/const.html
Articles has a link for it (at the top)

http://www.digitalmars.com/d/index.html
There the link to it under Articles is missing.
Jun 21 2007
parent torhu <fake address.dude> writes:
Daniel919 wrote:
 
 PS:
 http://www.digitalmars.com/d/const.html
 Articles has a link for it (at the top)
 
 http://www.digitalmars.com/d/index.html
 There the link to it under Articles is missing.

The link is there now. If you don't see it, press F5.
Jun 21 2007
prev sibling next sibling parent Myron Alexander <someone somewhere.com> writes:
Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

Hello Walter. Thanks, a very interesting read. I think the only missing element is a discussion, and example, of the invariant keyword, and how it differs from const. I know you have it explained in this page (http://www.digitalmars.com/d/final-const-invariant.html) but since you mention final, and in the C++ portion you talk about the problems with C++ const that are solved by invariant but not const, then I believe it should be added to show how the "constness" hole is close. Regards, Myron.
Jun 21 2007
prev sibling next sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
An excellent article Mr. Bright. It certainly cleared some misunderstandings
regarding new const/invariants in DMD 2.0.
Jun 21 2007
prev sibling next sibling parent "Craig Black" <cblack ara.com> writes:
Good article!  The link to the article currently doesn't appear on the main 
D page http://www.digitalmars.com/d/  I've browsed some of the other pages 
and it doesn't appear there either.

-Craig 
Jun 21 2007
prev sibling next sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribi:
 http://www.digitalmars.com/d/const.html

Thanks! -- Carlos Santander Bernal
Jun 21 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Carlos Santander wrote:
 Walter Bright escribi:
 http://www.digitalmars.com/d/const.html

Thanks!

Welcs! Just for fun, I posted it on http://programming.reddit.com/
Jun 21 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

So in short, 'const' protects data and 'final' freezes references. How do these two apply to an int declaration? const final int x = 5; Is either a compiler error? are they synonyms in this case? This aspect of the design seems very straightforward, aside from the question above. What bothers me, however, is the use of 'invariant'. Adding a third keyword simply to represent data that's "really really const" just confuses things to me, and I haven't been able to get past this. Given that adding a third keyword doubles the number of permutations for describing const behavior, I think the addition of 'invariant' should be very carefully considered. Is there any way we could get along without it? I realize that 'invariant' would be rarely used in practice, but that doesn't change the impact an additional attribute has on the complexity of this design. Frankly, I think we could almost get away with one keyword, but for the fact that D doesn't use a reference qualifier for class references. About the only workaround I could think of to describe a const reference to mutable data would be something like this: const (ref MyClass) x; And inserting the 'ref' seems even more confusing than simply having 'final' as in the current design. Sean
Jun 21 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

So in short, 'const' protects data and 'final' freezes references. How do these two apply to an int declaration? const final int x = 5; Is either a compiler error? are they synonyms in this case?

It's not an error, it's just redundant.
 This aspect of the design seems very straightforward, aside from the 
 question above.  What bothers me, however, is the use of 'invariant'. 
 Adding a third keyword simply to represent data that's "really really 
 const" just confuses things to me, and I haven't been able to get past 
 this.

invariant = the data doesn't change const = the data cannot be changed through this reference to it
 Given that adding a third keyword doubles the number of 
 permutations for describing const behavior, I think the addition of 
 'invariant' should be very carefully considered.  Is there any way we 
 could get along without it?  I realize that 'invariant' would be rarely 
 used in practice, but that doesn't change the impact an additional 
 attribute has on the complexity of this design.

In C++, sometimes const means invariant, and sometimes it means readonly view. I've found even C++ experts who don't know how it works.
 Frankly, I think we could almost get away with one keyword, but for the 
 fact that D doesn't use a reference qualifier for class references. 
 About the only workaround I could think of to describe a const reference 
 to mutable data would be something like this:
 
     const (ref MyClass) x;
 
 And inserting the 'ref' seems even more confusing than simply having 
 'final' as in the current design.

C++ tries too hard to use one keyword to do it all. The result is, as the article showed, serious difficulties.
Jun 22 2007
next sibling parent reply "Martin Howe" <martinhowe myprivacy.ca> writes:
After reading the articles by Walter referred to recently, I'm less confused 
that I was, but would still appreciate some further explanation.

So should "const foo bar = baz" be an error if foo is a value type, on the 
basis that one should be using "invariant" or maybe final? It it safe to 
have two things const/invariant that become identical depending on type?

And does "final" exist *solely* so that you can approximate "invariant" for 
objects as well as for structs and scalars? I can see its use when you want 
something to be invariant but with the stipulation that it must exist in 
memory and have an address. In which case, why have final vs invariant at 
all, rather than having final be, for example, "weak invariant" or "stored 
invariant" or something like that? From here, final just looks 
*semantically* like "a poor man's invariant".
Jun 22 2007
next sibling parent BCS <ao pathlink.com> writes:
Reply to Martin,

 After reading the articles by Walter referred to recently, I'm less
 confused that I was, but would still appreciate some further
 explanation.
 
 So should "const foo bar = baz" be an error if foo is a value type, on
 the basis that one should be using "invariant" or maybe final? It it
 safe to have two things const/invariant that become identical
 depending on type?
 
 And does "final" exist *solely* so that you can approximate
 "invariant" for objects as well as for structs and scalars? I can see
 its use when you want something to be invariant but with the
 stipulation that it must exist in memory and have an address. In which
 case, why have final vs invariant at all, rather than having final be,
 for example, "weak invariant" or "stored invariant" or something like
 that? From here, final just looks *semantically* like "a poor man's
 invariant".
 

One use of final that I can think of is this (assuming I understand correctly) int foo(final int* bar) { } bar now acts like a c++ reference (can't be pointed to something else) but with the syntax of a pointer.
Jun 22 2007
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Martin Howe wrote:
 After reading the articles by Walter referred to recently, I'm less confused 
 that I was, but would still appreciate some further explanation.
 
 So should "const foo bar = baz" be an error if foo is a value type, on the 
 basis that one should be using "invariant" or maybe final? It it safe to 
 have two things const/invariant that become identical depending on type?

For value types, there's no difference between const, final, and invariant. So it's not an error. This is probably the most confusing part of the whole deal, and there's no way around it except by making all value types into reference types (ugh) or vice versa (but we've progressed beyond C).
 And does "final" exist *solely* so that you can approximate "invariant" for 
 objects as well as for structs and scalars? I can see its use when you want 
 something to be invariant but with the stipulation that it must exist in 
 memory and have an address. In which case, why have final vs invariant at 
 all, rather than having final be, for example, "weak invariant" or "stored 
 invariant" or something like that? From here, final just looks 
 *semantically* like "a poor man's invariant".
 
 

Not at all. A final variable cannot be reassigned; the reference is const, but the data is mutable. So: --- class Foo { int _bar; public void bar(int value) { _bar = value; } } final Foo foo = new Foo(); foo.bar = 5; // legal -- I'm not touching the reference foo = new Foo(); // fails -- foo was already assigned ---
Jun 23 2007
parent reply "Martin Howe" <martinhowe myprivacy.ca> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:f5kjll$92s$1 digitalmars.com...
 Not at all. A final variable cannot be reassigned; the reference is const, 
 but the data is mutable.

Thanks - this has been confusing the heck out of me. The problems seem to all stem from (a) coming from other languages and (c) overloading of keywords. The *concepts* are straightforward, it's knowing which keyword or combination of keywords means what. A picture speaks a thousand words, so we REALLY REALLY REALLY need a TABLE here. I have started one below (you need a fixed-pitch font to view it, courier, courier new or fixedsys). I'd be very grateful if somebody (pref. Walter, since he likely knows more than anybody about all this) would correct/complete the table and repost it here. LOCAL/GLOBAL VARIABLE +---------------------------+-----------------------------------------+-----------------------------------------+ | KEYWORD(S) | VALUE TYPE | REFERENCE TYPE | | +-------+------+-------+------------------+-------+------+-------+------------------+ | | CAN | CAN | CAN | INVARIANCE | CAN | CAN | CAN | INVARIANCE | | | MOD | MOD | MOD | BEGINS | MOD | MOD | MOD | BEGINS | | | DECL | DATA | DATA | | DECL | DATA | DATA | | | | REF | VIA | VIA | | REF | VIA | VIA | | | | | DECLR | OTHER | | | DECLR | OTHER | | | | | REF | REF | | | REF | REF | | +---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+ | <none> | NO(1) | YES | YES | N/A | YES | YES | YES | N/A | | invariant | NO(2) | NO | NO(3) | AT COMPILE TIME | NO | NO | NO(3) | COMPILE TIME(5) | | const | NO(2) | NO | NO(3) | AT COMPILE TIME | NO | NO | NO(3) | COMPILE TIME(5) | | const invariant (4) | NO(2) | NO | NO(3) | AT COMPILE TIME | NO | NO | NO(3) | COMPILE TIME(5) | | final | NO(2) | NO | YES | AT COMPILE TIME | NO | YES | YES | RUN TIME | | final const | NO(2) | NO | NO | AT COMPILE TIME | NO | NO | YES | RUN TIME | | final invariant | NO(2) | NO | NO | AT COMPILE TIME | NO | NO | NO | RUN TIME | | final const invariant (4) | NO(2) | NO | NO | AT COMPILE TIME | NO | NO | NO | RUN TIME | +---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+ FUNCTION ARGUMENT +---------------------------+-----------------------------------------+-----------------------------------------+ | KEYWORD(S) | VALUE TYPE | REFERENCE TYPE | | +-------+------+-------+------------------+-------+------+-------+------------------+ | | CAN | CAN | CAN | INVARIANCE | CAN | CAN | CAN | INVARIANCE | | | MOD | MOD | MOD | BEGINS | MOD | MOD | MOD | BEGINS | | | DECL | DATA | DATA | | DECL | DATA | DATA | | | | REF | VIA | VIA | | REF | VIA | VIA | | | | | DECLR | OTHER | | | DECLR | OTHER | | | | | REF | REF | | | REF | REF | | +---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+ | <none> | NO(1) | YES(6)| NO | FUNC PROLOGUE | | | | | | invariant | NO(1) | NO | NO(3) | FUNC PROLOGUE | | | | | | const | NO(1) | NO | NO(3) | FUNC PROLOGUE | | | | | | const invariant (4) | NO(1) | NO | NO(3) | FUNC PROLOGUE | | | | | | final | NO(1) | NO | YES(7)| FUNC PROLOGUE | | | | | | final const | NO(1) | NO | NO | FUNC PROLOGUE | | | | | | final invariant | NO(1) | NO | NO | FUNC PROLOGUE | | | | | | final const invariant (4) | NO(1) | NO | NO | FUNC PROLOGUE | | | | | +---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+ (1) - NOT VISIBLE TO PROGRAMMER; EXISTS IN SYMBOL-TABLE/STACK/LOAD-FROM-MEMORY-INSTRUCTIONS ONLY (2) - NOT VISIBLE TO PROGRAMMER; EXISTS IN SYMBOL-TABLE/LOAD-FROM-MEMORY-INSTRUCTIONS ONLY AND MAY EVEN HAVE BEEN REPLACED BY IMMEDIATE LOAD, DEPENDING ON IMPLEMENTATION AND TYPE. (3) - CREATING OTHER REFERENCES IS NOT ALLOWED (4) - SHOULD THIS BE LEGAL FOR VALUE TYPES? CERTAINLY REDUNDANT FOR THEM... (5) - IMPOSSIBLE FOR REFERENCE TYPES THAT NEED CONSTRUCTORS/DESTRUCTORS; USE "FINAL INVARIANT" INSTEAD (6) - USE OF A BY-VALUE ARGUMENT AS A LOCAL VARIABLE (ARGUABLY A "BAD THING [TM]") (7) - POSSIBLE WITHIN THE FUNCTION, BUT WHY WOULD YOU WANT TO, ITS ONLY A **COPY OF** THE ORIGINAL ARGUMENT!!
Jun 24 2007
parent "Martin Howe" <martinhowe myprivacy.ca> writes:
"Martin Howe" <martinhowe myprivacy.ca> wrote in message 
news:f5leib$1rp4$1 digitalmars.com...
 ...

Damn line wrapping!!! Ok, try it as an attachment...
Jun 24 2007
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Walter Bright, el 22 de junio a las 01:07 me escribiste:
 Sean Kelly wrote:
Walter Bright wrote:
http://www.digitalmars.com/d/const.html

const final int x = 5; Is either a compiler error? are they synonyms in this case?

It's not an error, it's just redundant.

Shouldn't be better to be an error? So it's more clear that final makes sense only for reference types. Even more, aren't: const int x = 5; final int x = 5; invariant int x = 5; all the same? -- LUCA - Leandro Lucarella - Usando Debian GNU/Linux Sid - GNU Generation ------------------------------------------------------------------------ E-Mail / JID: luca lugmen.org.ar GPG Fingerprint: D9E1 4545 0F4B 7928 E82C 375D 4B02 0FE0 B08B 4FB2 GPG Key: gpg --keyserver pks.lugmen.org.ar --recv-keys B08B4FB2 ------------------------------------------------------------------------ No existe nada más intenso que un reloj, ni nada más flaco que una bicicleta. No intenso como el café, ni flaco como escopeta. -- Ricardo Vaporeso
Jun 22 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Leandro Lucarella wrote:
 Walter Bright, el 22 de junio a las 01:07 me escribiste:
 Sean Kelly wrote:
 Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

const final int x = 5; Is either a compiler error? are they synonyms in this case?


Shouldn't be better to be an error? So it's more clear that final makes sense only for reference types.

Allowing it allows cleaner generic code, otherwise templates would often have to check whether parameters were value or reference types.
Jun 22 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Frits van Bommel wrote:
 Leandro Lucarella wrote:
 Walter Bright, el 22 de junio a las 01:07 me escribiste:
 Sean Kelly wrote:
 Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

How do these two apply to an int declaration? const final int x = 5; Is either a compiler error? are they synonyms in this case?


Shouldn't be better to be an error? So it's more clear that final makes sense only for reference types.

Allowing it allows cleaner generic code, otherwise templates would often have to check whether parameters were value or reference types.

Indeed; we already need to special-case functions that return void. "It can't be *that* bad!" In some cases, it means an entire template has to be duplicated *just* for the void case. I had to do this for a coroutine implementation, and was not happy about it. -- Daniel
Jun 22 2007
parent reply Sean Kelly <sean f4.ca> writes:
Daniel Keep wrote:
 
 Frits van Bommel wrote:
 Leandro Lucarella wrote:
 Walter Bright, el 22 de junio a las 01:07 me escribiste:
 Sean Kelly wrote:
 Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

How do these two apply to an int declaration? const final int x = 5; Is either a compiler error? are they synonyms in this case?


sense only for reference types.

have to check whether parameters were value or reference types.

Indeed; we already need to special-case functions that return void.

How so? I thought "return void" was a legal statement? Sean
Jun 22 2007
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Sean Kelly wrote:
 Daniel Keep wrote:
 Frits van Bommel wrote:
 Allowing it allows cleaner generic code, otherwise templates would often
 have to check whether parameters were value or reference types.

Indeed; we already need to special-case functions that return void.

How so? I thought "return void" was a legal statement? Sean

Indeed it is. But what if you need to actually *store* the return value? I believe the problems I had with the coroutine stuff specifically were storing the return value and having functions that passed *in* a value of the return type (which obviously had to be written separately for functions with void since you can't have void parameters, either). -- Daniel
Jun 22 2007
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Leandro Lucarella wrote:
 Walter Bright, el 22 de junio a las 01:07 me escribiste:
 Sean Kelly wrote:
 Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

const final int x = 5; Is either a compiler error? are they synonyms in this case?


Shouldn't be better to be an error? So it's more clear that final makes sense only for reference types.

It shouldn't be an error. With template functions, you often aren't sure what may be passed in, and may want to do something like this just to be safe: void func(T)( const final T val ) { ... } Sean
Jun 22 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Leandro Lucarella wrote:
 Even more, aren't:
 
 const int x = 5;
 final int x = 5;
 invariant int x = 5;
 
 all the same?

No, because finals exist in memory, while const/invariants do not. Const/invariant declarations also require their initializers to be evaluated at compile time, whereas final can do them at run time. The const & invariant cases are the same, as they are the degenerate cases. It's sort of like: int x = 1; uint y = 1; both represent 1.
Jun 22 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

So in short, 'const' protects data and 'final' freezes references. How do these two apply to an int declaration? const final int x = 5; Is either a compiler error? are they synonyms in this case?

It's not an error, it's just redundant.

Okay, thanks.
 This aspect of the design seems very straightforward, aside from the 
 question above.  What bothers me, however, is the use of 'invariant'. 
 Adding a third keyword simply to represent data that's "really really 
 const" just confuses things to me, and I haven't been able to get past 
 this.

invariant = the data doesn't change const = the data cannot be changed through this reference to it

And a 'const' at the declaration point implicitly means 'invariant'.
 Given that adding a third keyword doubles the number of permutations 
 for describing const behavior, I think the addition of 'invariant' 
 should be very carefully considered.  Is there any way we could get 
 along without it?  I realize that 'invariant' would be rarely used in 
 practice, but that doesn't change the impact an additional attribute 
 has on the complexity of this design.

In C++, sometimes const means invariant, and sometimes it means readonly view. I've found even C++ experts who don't know how it works.

Odd. The C++ system always seemed extremely simple to me.
 Frankly, I think we could almost get away with one keyword, but for 
 the fact that D doesn't use a reference qualifier for class 
 references. About the only workaround I could think of to describe a 
 const reference to mutable data would be something like this:

     const (ref MyClass) x;

 And inserting the 'ref' seems even more confusing than simply having 
 'final' as in the current design.

C++ tries too hard to use one keyword to do it all. The result is, as the article showed, serious difficulties.

I personally find the use of three keywords to represent three overlapping facets of const behavior to be very confusing, and am concerned about trying to explain it to novice programmers. With three keywords, there are six possible combinations: final const invariant final const final invariant const invariant final const invariant That some of these may be redundant just serves to further confuse the issue in my opinion. So I wondered whether one of the keywords could be done away with. Previously, you said 'invariant' may only apply to data whose value can be determined at compile-time, thus I imagine it can only apply to concrete/data types (ie. not classes). Assuming this is true, I wonder whether there is truly a point in having 'invariant' at all. Assuming it were done away with, the system becomes much simpler to me: final const final const And that's it. 'final' means a reference cannot be rebound, 'const' means the data cannot be altered (through the reference), and 'final const' means that both the reference is frozen and the data cannot be changed. And that's it. That the existence of invariant required the addition of parenthesis on class invariants just serves to strengthen the argument in my mind. So in short, I'm just not convinced that 'invariant' provides enough utility to warrant the cost in complexity and broken consistency (unittest doesn't require parens, so why does invariant?). Sean
Jun 22 2007
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Sean Kelly wrote:
 ...
 
 final
 const
 final const
 
 And that's it.  'final' means a reference cannot be rebound, 'const'
 means the data cannot be altered (through the reference), and 'final
 const' means that both the reference is frozen and the data cannot be
 changed.  And that's it.  That the existence of invariant required the
 addition of parenthesis on class invariants just serves to strengthen
 the argument in my mind.  So in short, I'm just not convinced that
 'invariant' provides enough utility to warrant the cost in complexity
 and broken consistency (unittest doesn't require parens, so why does
 invariant?).

What makes me uneasy about the above is that final isn't a type constructor: it's a storage class. You're turning it into sorta-kinda-but-not-quite both. So, you end up with: final int x; typeof(x) == int; const int y; typeof(y) == const int; final const int z; typeof(z) == final const int; "Wait; why is final part of the last type, but not the first?" And what does this mean if you want a class member with final-style binding, but (final const) type semantics? final (final const(int*)) foo; As opposed to final invariant(int*) foo; I think the thing here is that you're shifting the complexity from invariant into final; instead of invariant meaning two different things with two very different appearances, you've got final meaning two slightly different things with almost identical looks. -- Daniel
Jun 22 2007
next sibling parent Lars Ivar Igesund <larsivar igesund.net> writes:
Daniel Keep wrote:

 
 
 Sean Kelly wrote:
 ...
 
 final
 const
 final const
 
 And that's it.  'final' means a reference cannot be rebound, 'const'
 means the data cannot be altered (through the reference), and 'final
 const' means that both the reference is frozen and the data cannot be
 changed.  And that's it.  That the existence of invariant required the
 addition of parenthesis on class invariants just serves to strengthen
 the argument in my mind.  So in short, I'm just not convinced that
 'invariant' provides enough utility to warrant the cost in complexity
 and broken consistency (unittest doesn't require parens, so why does
 invariant?).

What makes me uneasy about the above is that final isn't a type constructor: it's a storage class. You're turning it into sorta-kinda-but-not-quite both. So, you end up with: final int x; typeof(x) == int; const int y; typeof(y) == const int; final const int z; typeof(z) == final const int; "Wait; why is final part of the last type, but not the first?" And what does this mean if you want a class member with final-style binding, but (final const) type semantics? final (final const(int*)) foo; As opposed to final invariant(int*) foo; I think the thing here is that you're shifting the complexity from invariant into final; instead of invariant meaning two different things with two very different appearances, you've got final meaning two slightly different things with almost identical looks. -- Daniel

I agree with Sean on the proposed system looking unnecessarily complex. I don't have a problem seeing your points regarding Sean's suggestion, but I still think he has shown that there still may be room for improvement, and since many (including me) think the full mechanics of this solution is somewhat hard to grasp, and many even think it is unnecessary to add to the language in the first place (I'm not one of those), we should strive even more than normal to find a solution that works and that is easy to use. I find it weird if such a great change should be solidified after the first alpha release in a new branch. Not everyone have the opportunity to test it the first few days after a release to give feedback. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Jun 22 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Daniel Keep wrote:
 
 Sean Kelly wrote:
 ...

 final
 const
 final const

 And that's it.  'final' means a reference cannot be rebound, 'const'
 means the data cannot be altered (through the reference), and 'final
 const' means that both the reference is frozen and the data cannot be
 changed.  And that's it.  That the existence of invariant required the
 addition of parenthesis on class invariants just serves to strengthen
 the argument in my mind.  So in short, I'm just not convinced that
 'invariant' provides enough utility to warrant the cost in complexity
 and broken consistency (unittest doesn't require parens, so why does
 invariant?).

What makes me uneasy about the above is that final isn't a type constructor: it's a storage class. You're turning it into sorta-kinda-but-not-quite both. So, you end up with: final int x; typeof(x) == int; const int y; typeof(y) == const int; final const int z; typeof(z) == final const int;

Hm. Just to clarify, we both agree that the value of a final integer (ie. case 1 above) is effectively constant, correct?
 "Wait; why is final part of the last type, but not the first?"  And what
 does this mean if you want a class member with final-style binding, but
 (final const) type semantics?
 
 final (final const(int*)) foo;
 
 As opposed to
 
 final invariant(int*) foo;

Perhaps I'm missing something, but I would rewrite this as: final const int* foo; Thus foo cannot be reassigned once set and the data foo refers to may not be changed through foo. This is a slightly weaker guarantee than: final invariant int* foo; Which says that the data foo refers to is immutable, but I am skeptical that this guarantee actually matters much to users. Or am I completely misunderstanding? And why the parenthesis in the second declaration?
 I think the thing here is that you're shifting the complexity from
 invariant into final; instead of invariant meaning two different things
 with two very different appearances, you've got final meaning two
 slightly different things with almost identical looks.

I only see final meaning one thing: that the associated value may not be reassigned. For concrete types like integers this is effectively the same as const, but as Walter said, the integer would be addressable when final but not when const. Perhaps this is the source of confusion? Sean
Jun 22 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Sean Kelly wrote:
 Daniel Keep wrote:
 final int x;        typeof(x) == int;
 const int y;        typeof(y) == const int;
 final const int z;    typeof(z) == final const int;

Hm. Just to clarify, we both agree that the value of a final integer (ie. case 1 above) is effectively constant, correct?

Indeed.
 "Wait; why is final part of the last type, but not the first?"  And what
 does this mean if you want a class member with final-style binding, but
 (final const) type semantics?

 final (final const(int*)) foo;

 As opposed to

 final invariant(int*) foo;

Perhaps I'm missing something, but I would rewrite this as: final const int* foo; Thus foo cannot be reassigned once set and the data foo refers to may not be changed through foo. This is a slightly weaker guarantee than: final invariant int* foo; Which says that the data foo refers to is immutable, but I am skeptical that this guarantee actually matters much to users. Or am I completely misunderstanding? And why the parenthesis in the second declaration?

(When I wrote the below, I missed precisely what you were saying: the problem with writing it at "final const int*" is that you've got both final and const as a storage class; I assumed you were using them as a type constructor.) Ok, let's try this instead: in the current system, we have class Foo { final invariant(FunkyType) funky; this(bool superFunky) { if( superFunky ) funky = cast(invariant) new FunkyType; else funky = some_global_invariant_funky; } } In this case, we have a final storage (so we can assign the value during the ctor), and an invariant(FunkyType) value type. Under your proposal, invariant is replaced with (final const); if we want the above, it'd become: final final const(FunkyType) funky; But how does the compiler tell that second final is storage class or part of the type constructor? Remember, we could be dealing with a template that's just shoving "final" out the front of things, so we can't assume two finals ==> one is a type constructor. So we'd probably have to do this: final (final const(FunkyType)) funky; We can't use *just* "final const FunkyType funky" because then we'd have an "invariant" storage class: which means the initialiser would have to be a compile-time constant. Incidentally, the above is also probably equivalent to: final(final const(FunkyType)) funky; Which really doesn't make any sense anyway...
 I think the thing here is that you're shifting the complexity from
 invariant into final; instead of invariant meaning two different things
 with two very different appearances, you've got final meaning two
 slightly different things with almost identical looks.

I only see final meaning one thing: that the associated value may not be reassigned. For concrete types like integers this is effectively the same as const, but as Walter said, the integer would be addressable when final but not when const. Perhaps this is the source of confusion? Sean

Yes, but it *also* means "can assign to in a ctor". Like Lars said, I do think that if there's a way to simplify or consolidate this, then we should take it. That said, I can see a use for each of the various cases the new system allows for, and I don't want to see any of them cut off in the name of "well *I'm* not going to use it, and I don't like that keyword, so it has to go!"[1]. :) -- Daniel [1] I'm not saying that's what you're doing, but I've heard that sort of argument from a lot of people, so I've become somewhat defensive of the new stuff: don't take away my new shinies!
Jun 22 2007
parent Sean Kelly <sean f4.ca> writes:
Daniel Keep wrote:
 
 Sean Kelly wrote:
 Daniel Keep wrote:
 final int x;        typeof(x) == int;
 const int y;        typeof(y) == const int;
 final const int z;    typeof(z) == final const int;

(ie. case 1 above) is effectively constant, correct?

Indeed.
 "Wait; why is final part of the last type, but not the first?"  And what
 does this mean if you want a class member with final-style binding, but
 (final const) type semantics?

 final (final const(int*)) foo;

 As opposed to

 final invariant(int*) foo;

final const int* foo; Thus foo cannot be reassigned once set and the data foo refers to may not be changed through foo. This is a slightly weaker guarantee than: final invariant int* foo; Which says that the data foo refers to is immutable, but I am skeptical that this guarantee actually matters much to users. Or am I completely misunderstanding? And why the parenthesis in the second declaration?

(When I wrote the below, I missed precisely what you were saying: the problem with writing it at "final const int*" is that you've got both final and const as a storage class; I assumed you were using them as a type constructor.) Ok, let's try this instead: in the current system, we have class Foo { final invariant(FunkyType) funky; this(bool superFunky) { if( superFunky ) funky = cast(invariant) new FunkyType; else funky = some_global_invariant_funky; } } In this case, we have a final storage (so we can assign the value during the ctor), and an invariant(FunkyType) value type. Under your proposal, invariant is replaced with (final const); if we want the above, it'd become: final final const(FunkyType) funky;

Under my proposal, this would be: final const FunkyType funky;
 But how does the compiler tell that second final is storage class or
 part of the type constructor?  Remember, we could be dealing with a
 template that's just shoving "final" out the front of things, so we
 can't assume two finals ==> one is a type constructor.  So we'd probably
 have to do this:
 
     final (final const(FunkyType)) funky;

Hm. I'm afraid I don't understand all this talk about type constructor vs. storage class. What I get from the above is that: final const FunkyType funky; represents a fixed reference to an instance of FunkyType, which cannot be changed through the reference.
 We can't use *just* "final const FunkyType funky" because then we'd have
 an "invariant" storage class: which means the initialiser would have to
 be a compile-time constant.

Why would it have to be an "invariant" storage class? It seems I'm missing something fundamental here.
 Incidentally, the above is also probably
 equivalent to:
 
     final(final const(FunkyType)) funky;
 
 Which really doesn't make any sense anyway...

 I think the thing here is that you're shifting the complexity from
 invariant into final; instead of invariant meaning two different things
 with two very different appearances, you've got final meaning two
 slightly different things with almost identical looks.

reassigned. For concrete types like integers this is effectively the same as const, but as Walter said, the integer would be addressable when final but not when const. Perhaps this is the source of confusion?

Yes, but it *also* means "can assign to in a ctor".

Right. That's compatible with what I said. It can be assigned in a ctor but not *reassigned* once assigned. Just like 'const' in D 1.0.
 Like Lars said, I do think that if there's a way to simplify or
 consolidate this, then we should take it.  That said, I can see a use
 for each of the various cases the new system allows for, and I don't
 want to see any of them cut off in the name of "well *I'm* not going to
 use it, and I don't like that keyword, so it has to go!"[1]. :)

I agree. Sean
Jun 23 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Daniel Keep wrote:
 
 Sean Kelly wrote:
 ...

 final
 const
 final const

 And that's it.  'final' means a reference cannot be rebound, 'const'
 means the data cannot be altered (through the reference), and 'final
 const' means that both the reference is frozen and the data cannot be
 changed.  And that's it.  That the existence of invariant required the
 addition of parenthesis on class invariants just serves to strengthen
 the argument in my mind.  So in short, I'm just not convinced that
 'invariant' provides enough utility to warrant the cost in complexity
 and broken consistency (unittest doesn't require parens, so why does
 invariant?).

What makes me uneasy about the above is that final isn't a type constructor: it's a storage class. You're turning it into sorta-kinda-but-not-quite both. So, you end up with: final int x; typeof(x) == int; const int y; typeof(y) == const int; final const int z; typeof(z) == final const int;

And in what are you based to say that those typeofs are like that? The typeof of each of those 3 vars is int (as reported by DMD). And in terms of semantics y and z are exactly the same, only x differs from those other two (y and z, are compile-time values, with all that that implies). -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 23 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 In C++, sometimes const means invariant, and sometimes it means 
 readonly view. I've found even C++ experts who don't know how it works.


It isn't. I run into people all the time who are amazed to discover that const references can change. Few understand when const is invariant and when it isn't. I've never even seen anyone mention the problem where the non-transitive const destroys any hope of having FP like capabilities in C++.
 I personally find the use of three keywords to represent three 
 overlapping facets of const behavior to be very confusing, and am 
 concerned about trying to explain it to novice programmers.  With three 
 keywords, there are six possible combinations:
 
 final
 const invariant
 final const
 final invariant
 const invariant
 final const invariant

Probably the thing to do is simply outlaw using more than one.
 That some of these may be redundant just serves to further confuse the 
 issue in my opinion.  So I wondered whether one of the keywords could be 
 done away with.  Previously, you said 'invariant' may only apply to data 
 whose value can be determined at compile-time, thus I imagine it can 
 only apply to concrete/data types (ie. not classes).  Assuming this is 
 true, I wonder whether there is truly a point in having 'invariant' at 
 all.  Assuming it were done away with, the system becomes much simpler 
 to me:
 
 final
 const
 final const
 
 And that's it.  'final' means a reference cannot be rebound, 'const' 
 means the data cannot be altered (through the reference), and 'final 
 const' means that both the reference is frozen and the data cannot be 
 changed.  And that's it.

It's missing the transitive nature of invariant.
Jun 22 2007
next sibling parent Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribi:
 Sean Kelly wrote:
 I personally find the use of three keywords to represent three 
 overlapping facets of const behavior to be very confusing, and am 
 concerned about trying to explain it to novice programmers.  With 
 three keywords, there are six possible combinations:

 final
 const invariant
 final const
 final invariant
 const invariant
 final const invariant

Probably the thing to do is simply outlaw using more than one.

How does that fit with "in" meaning "final const scope"? Will it change? -- Carlos Santander Bernal
Jun 22 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 In C++, sometimes const means invariant, and sometimes it means 
 readonly view. I've found even C++ experts who don't know how it works.


It isn't. I run into people all the time who are amazed to discover that const references can change. Few understand when const is invariant and when it isn't. I've never even seen anyone mention the problem where the non-transitive const destroys any hope of having FP like capabilities in C++.

Matter of opinion, I suppose. The C++ design was immediately clear to me, though it obviously wasn't for others. I grant that the aliasing problem can be confusing, but I feel that it is a peripheral issue.
 I personally find the use of three keywords to represent three 
 overlapping facets of const behavior to be very confusing, and am 
 concerned about trying to explain it to novice programmers.  With 
 three keywords, there are six possible combinations:

 final
 const invariant
 final const
 final invariant
 const invariant
 final const invariant

Probably the thing to do is simply outlaw using more than one.

If nothing else, I imagine "final const" will be a necessary combination.
 That some of these may be redundant just serves to further confuse the 
 issue in my opinion.  So I wondered whether one of the keywords could 
 be done away with.  Previously, you said 'invariant' may only apply to 
 data whose value can be determined at compile-time, thus I imagine it 
 can only apply to concrete/data types (ie. not classes).  Assuming 
 this is true, I wonder whether there is truly a point in having 
 'invariant' at all.  Assuming it were done away with, the system 
 becomes much simpler to me:

 final
 const
 final const

 And that's it.  'final' means a reference cannot be rebound, 'const' 
 means the data cannot be altered (through the reference), and 'final 
 const' means that both the reference is frozen and the data cannot be 
 changed.  And that's it.

It's missing the transitive nature of invariant.

How so? Given the above, I would consider 'const' to apply to a declaration left-to-right and 'final' to apply to a declaration right-to-left. If there are parenthesis, the rightmost (ie. closing) paren would effectively be the barrier between const and final. Thus, from your example: const (int**)* x; Represents a mutable pointer to a const**, and: final const (int**)* x; Represents an immutable (ie. final) pointer to a const int**. By default, both qualifiers would be fully transitive, so: final int** x; Would be an immutable pointer to an immutable pointer to a mutable int. Or am I missing something? Sean
Jun 22 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 In C++, sometimes const means invariant, and sometimes it means 
 readonly view. I've found even C++ experts who don't know how it works.


It isn't. I run into people all the time who are amazed to discover that const references can change.


I was surprised to find that p->q[0]=5 is always ok for a const C*p. Learned that from reading the const doc just now.
 Matter of opinion, I suppose.  The C++ design was immediately clear to 
 me, though it obviously wasn't for others.  I grant that the aliasing 
 problem can be confusing, but I feel that it is a peripheral issue.

I vaguely remember C++'s const taking some time for me to internalize. But it seems natural now. So I'm not too worried about it. I also have a feeling that invariant won't show up very often except at the module-level scope. So I'm not so worried about how to grok the odd combinations of const/invariant and what they mean. I have trouble with groking "int * const * const **foo" in C++ but that's never caused me any problem in an actual programming task. In the end there are just a handful of common usages. It might be nice to have these collected and documented somewhere. --bb
Jun 22 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Matter of opinion, I suppose.  The C++ design was immediately clear to 
 me, though it obviously wasn't for others.  I grant that the aliasing 
 problem can be confusing, but I feel that it is a peripheral issue.

I don't think it is a peripheral issue. It completely screws up optimization, is useless for threading support, and has spawned endless angst about why C++ code is slower than Fortran code.
 Or am I missing something?

Yes, you've missed the distinction between a readonly view and a truly immutable value. That's not surprising, since my experience is that very few who are used to C++ const see the difference, and it took me a while to figure it out.
Jun 22 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Matter of opinion, I suppose.  The C++ design was immediately clear to 
 me, though it obviously wasn't for others.  I grant that the aliasing 
 problem can be confusing, but I feel that it is a peripheral issue.

I don't think it is a peripheral issue. It completely screws up optimization, is useless for threading support, and has spawned endless angst about why C++ code is slower than Fortran code.

As a programmer, I consider the optimization problem to be a non-issue. Optimization is just magic that happens to make my program run faster. As for multithreading... I'll address that below.
 Or am I missing something?

Yes, you've missed the distinction between a readonly view and a truly immutable value. That's not surprising, since my experience is that very few who are used to C++ const see the difference, and it took me a while to figure it out.

I didn't miss it, but I'm afraid I don't see how D fares much better than C++ in this respect. In a typical program, I see two basic situations for manipulating external data: first, it may be a reference parameter passed into a function call, or second, it may be a global variable of some sort. In the first case I assert that it is impossible to make any assumptions about the inherent immutability of the data. D may offer 'invariant' for this purpose, but practicality dictates that I would never apply 'invariant' to function parameters. 'invariant' by itself is too restrictive for general use, so overloads must be provided, and maintaining 2-3 instances of the same routine with different parameter qualifiers invites more trouble than it prevents. C++ has no equivalent, so there is no direct comparison there--mutability must always be assumed. In the second case, it is generally inherently obvious whether the data is mutable. Either it is a simple const-qualified global declaration, a reference is returned from a global routine of some sort that has documented guarantees about its mutability, etc. These are the cases most likely to eschew mutexes in a multi-threaded program. I will grant that 'invariant' may well be useful for writing self-documenting code at the highest level of an application, but I continue to wonder whether this offers a benefit sufficient to outweigh the complexity it adds to the design. This is the real crux of my argument, and what I am hoping will come clear through discussion. My initial reaction to the const design for D was "what the heck is this? well, I guess I'll figure it out when it's explained better," and though I feel I am gaining a better understanding now, my initial reaction greatly tempers my enthusiasm for the design. In my opinion, if something is not inherently obvious then it is probably over-complicated, and I remain hopeful that a some simplification can be found that does not sacrifice much of the expressiveness of the current design. Sean
Jun 22 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 Matter of opinion, I suppose.  The C++ design was immediately clear 
 to me, though it obviously wasn't for others.  I grant that the 
 aliasing problem can be confusing, but I feel that it is a peripheral 
 issue.

I don't think it is a peripheral issue. It completely screws up optimization, is useless for threading support, and has spawned endless angst about why C++ code is slower than Fortran code.

As a programmer, I consider the optimization problem to be a non-issue. Optimization is just magic that happens to make my program run faster.

Optimization often makes the difference between a successful project and a failure. C++ has failed to supplant FORTRAN, because although in every respect but one C++ is better, that one - optimization of arrays - matters a whole lot. It drives people using C++ to use inline assembler. They spend a lot of time on the issue. Various proposals to fix it, like 'noalias' and 'restrict', consume vast amounts of programmer time. And time is money. Optimization issues often drive the choice of language. That really makes it an issue!
 Yes, you've missed the distinction between a readonly view and a truly 
 immutable value. That's not surprising, since my experience is that 
 very few who are used to C++ const see the difference, and it took me 
 a while to figure it out.

I didn't miss it, but I'm afraid I don't see how D fares much better than C++ in this respect. In a typical program, I see two basic situations for manipulating external data: first, it may be a reference parameter passed into a function call, or second, it may be a global variable of some sort. In the first case I assert that it is impossible to make any assumptions about the inherent immutability of the data.

Right, hence the need for invariant.
 D may offer 'invariant' 
 for this purpose, but practicality dictates that I would never apply 
 'invariant' to function parameters.

I'm less sure about that. I think we're all so used to C++ and its mushy concept of const that we don't know yet what will emerge from the use of invariant. I do know, however, that those who want to do advanced array optimizations are going to want to be using invariant function parameters.
 'invariant' by itself is too 
 restrictive for general use, so overloads must be provided, and 
 maintaining 2-3 instances of the same routine with different parameter 
 qualifiers invites more trouble than it prevents.

For a function that wants to accept invariant and mutable parameters, declare them to be 'const'.
 C++ has no 
 equivalent, so there is no direct comparison there--mutability must 
 always be assumed.
 
 In the second case, it is generally inherently obvious whether the data 
 is mutable.  Either it is a simple const-qualified global declaration, a 
 reference is returned from a global routine of some sort that has 
 documented guarantees about its mutability, etc.  These are the cases 
 most likely to eschew mutexes in a multi-threaded program.

The problems with documentation are legion. It's inevitably wrong, out of date, incomplete, or missing. Furthermore, the compiler cannot make any use of documented characteristics, nor can it check them.
 I will grant that 'invariant' may well be useful for writing 
 self-documenting code at the highest level of an application, but I 
 continue to wonder whether this offers a benefit sufficient to outweigh 
 the complexity it adds to the design.  This is the real crux of my 
 argument, and what I am hoping will come clear through discussion.  My 
 initial reaction to the const design for D was "what the heck is this? 
 well, I guess I'll figure it out when it's explained better," and though 
 I feel I am gaining a better understanding now, my initial reaction 
 greatly tempers my enthusiasm for the design.  In my opinion, if 
 something is not inherently obvious then it is probably 
 over-complicated, and I remain hopeful that a some simplification can be 
 found that does not sacrifice much of the expressiveness of the current 
 design.

I share your concern, there, but we tried about everything for months, and nothing else worked.
Jun 22 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 Matter of opinion, I suppose.  The C++ design was immediately clear 
 to me, though it obviously wasn't for others.  I grant that the 
 aliasing problem can be confusing, but I feel that it is a 
 peripheral issue.

I don't think it is a peripheral issue. It completely screws up optimization, is useless for threading support, and has spawned endless angst about why C++ code is slower than Fortran code.

As a programmer, I consider the optimization problem to be a non-issue. Optimization is just magic that happens to make my program run faster.

Optimization often makes the difference between a successful project and a failure. C++ has failed to supplant FORTRAN, because although in every respect but one C++ is better, that one - optimization of arrays - matters a whole lot. It drives people using C++ to use inline assembler. They spend a lot of time on the issue. Various proposals to fix it, like 'noalias' and 'restrict', consume vast amounts of programmer time. And time is money. Optimization issues often drive the choice of language. That really makes it an issue!

You're right of course. However, my point was that to a programmer, optimization should be invisible. I can appreciate that 'invariant' may be of tremendous use to the compiler, but I balk at the notion of adding language features that seem largely intended as compiler "hints." Rather, it would be preferable if the language were structured in such a way as to make such hints unnecessary. To that end, and speaking as someone who isn't primarily involved in numerics programming, my impression of FORTRAN is that the language is syntactically suited to numerics programming, while C++ is not. Even if C++ performed on par with FORTRAN for similar work (and Bjarne suggested last year that it could), I would likely still choose FORTRAN over C++ because the syntax seems so much more appealing for that kind of work.
 Yes, you've missed the distinction between a readonly view and a 
 truly immutable value. That's not surprising, since my experience is 
 that very few who are used to C++ const see the difference, and it 
 took me a while to figure it out.

I didn't miss it, but I'm afraid I don't see how D fares much better than C++ in this respect. In a typical program, I see two basic situations for manipulating external data: first, it may be a reference parameter passed into a function call, or second, it may be a global variable of some sort. In the first case I assert that it is impossible to make any assumptions about the inherent immutability of the data.

Right, hence the need for invariant.
 D may offer 'invariant' for this purpose, but practicality dictates 
 that I would never apply 'invariant' to function parameters.

I'm less sure about that. I think we're all so used to C++ and its mushy concept of const that we don't know yet what will emerge from the use of invariant. I do know, however, that those who want to do advanced array optimizations are going to want to be using invariant function parameters.

You may be right, and I'm certainly willing to give it a try. This is simply my initial reaction to the new design, and I wanted to voice it before becoming placated by experience. My gut feeling is that a better design is possible, and I'm not yet ready to close the door on alternatives.
 'invariant' by itself is too restrictive for general use, so overloads 
 must be provided, and maintaining 2-3 instances of the same routine 
 with different parameter qualifiers invites more trouble than it 
 prevents.

For a function that wants to accept invariant and mutable parameters, declare them to be 'const'.

Well sure. But that merely supports my point that 'invariant' may be too specialized to justify the conceptual complexity it adds.
 C++ has no equivalent, so there is no direct comparison 
 there--mutability must always be assumed.

 In the second case, it is generally inherently obvious whether the 
 data is mutable.  Either it is a simple const-qualified global 
 declaration, a reference is returned from a global routine of some 
 sort that has documented guarantees about its mutability, etc.  These 
 are the cases most likely to eschew mutexes in a multi-threaded program.

The problems with documentation are legion. It's inevitably wrong, out of date, incomplete, or missing. Furthermore, the compiler cannot make any use of documented characteristics, nor can it check them.

The compiler can inspect the code however, and a global const is as good as an invariant for optimization (as far as I know). As for the rest, I think the majority of remaining cases aren't ones where 'invariant' would apply anyway: dynamic buffers whose contents are guaranteed not to change either in word or by design, etc.
 I will grant that 'invariant' may well be useful for writing 
 self-documenting code at the highest level of an application, but I 
 continue to wonder whether this offers a benefit sufficient to 
 outweigh the complexity it adds to the design.  This is the real crux 
 of my argument, and what I am hoping will come clear through 
 discussion.  My initial reaction to the const design for D was "what 
 the heck is this? well, I guess I'll figure it out when it's explained 
 better," and though I feel I am gaining a better understanding now, my 
 initial reaction greatly tempers my enthusiasm for the design.  In my 
 opinion, if something is not inherently obvious then it is probably 
 over-complicated, and I remain hopeful that a some simplification can 
 be found that does not sacrifice much of the expressiveness of the 
 current design.

I share your concern, there, but we tried about everything for months, and nothing else worked.

Perhaps I'm just a few months worth of discussion behind then. I'll admit that what I'm mostly doing here is poking the haystack to see if anyone tumbles out. Sean
Jun 22 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 However, my point was that to a programmer, 
 optimization should be invisible.

I can't agree with that. The compiler and the programmer need to cooperate with each other in order to produce fast programms. If a programmer just throws the completed program "over the wall" to the optimizer and expects good results, he'll be sadly disappointed.
 I can appreciate that 'invariant' may 
 be of tremendous use to the compiler, but I balk at the notion of adding 
 language features that seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.
 Rather, it would be preferable if the language were structured in such a 
 way as to make such hints unnecessary.

That would be preferable, but experience with such languages is that they are complete failures at producing fast code. Fast code comes from a language that enables the programmer to work with the optimizer to produce better code. Lots of people think that just 'cuz they write in C++, which has good optimizers available, they'll get fast code. That's not even close to being true.
 To that end, and speaking as 
 someone who isn't primarily involved in numerics programming, my 
 impression of FORTRAN is that the language is syntactically suited to 
 numerics programming, while C++ is not.  Even if C++ performed on par 
 with FORTRAN for similar work (and Bjarne suggested last year that it 
 could), I would likely still choose FORTRAN over C++ because the syntax 
 seems so much more appealing for that kind of work.

I programmed for years in FORTRAN. The syntax is not appealing, in fact, it sucks. The reason it is suited for numerics programming is because FORTRAN arrays, by definition, cannot be aliased. This means that optimizers can go to town parallelizing array operations, which is a big, big deal for speed. You can't do that with C/C++ because arrays can be aliased. I have a stack of papers 6" deep in the basement on trying to make C more suitable for numerics, and it's mostly about, you guessed it, fixing the alias problem. They failed. There are some template libraries for C++ which parallelize array operations and can finally approach FORTRAN. But they are horrifically kludgy and complicated. No thanks. But the effort that has gone into them is astonishing, and is indicative of how severe the problem is.
 I'm less sure about that. I think we're all so used to C++ and its 
 mushy concept of const that we don't know yet what will emerge from 
 the use of invariant. I do know, however, that those who want to do 
 advanced array optimizations are going to want to be using invariant 
 function parameters.

You may be right, and I'm certainly willing to give it a try. This is simply my initial reaction to the new design, and I wanted to voice it before becoming placated by experience. My gut feeling is that a better design is possible, and I'm not yet ready to close the door on alternatives.

Andrei, I and Bartosz have each expended probably a hundred hours trying to figure this out, and we've tried a lot of designs. If there is a better design, it's not like we haven't tried. I wish to reiterate that const designs in other languages like C++ (and to some extent Java) utterly fail at the objectives we set for const in D. Furthermore, Andrei is familiar with the many research papers on the topic, which were of invaluable help. D's const system is more ambitious than any of them.
 The compiler can inspect the code however, and a global const is as good 
 as an invariant for optimization (as far as I know).  As for the rest, I 
 think the majority of remaining cases aren't ones where 'invariant' 
 would apply anyway: dynamic buffers whose contents are guaranteed not to 
 change either in word or by design, etc.

But they do apply - that's the whole array optimization thing. You're not just going to have global arrays.
Jun 23 2007
next sibling parent reply Reiner Pope <some address.com> writes:
Walter Bright wrote:
 
 I can appreciate that 'invariant' may be of tremendous use to the 
 compiler, but I balk at the notion of adding language features that 
 seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.

which is nice to hear. But nothing in the current const system allows you to declare a verifiably 'pure' function; can we expect some annotation for functions which says 'this function doesn't read/write any global variables?' int b int foo() { b++; return b * 2; } pure int square(int x) { return x * x; } pure int baz() { return foo(); // fails: foo is not pure }
Jun 23 2007
next sibling parent reply "Martin Howe" <martinhowe myprivacy.ca> writes:
"Reiner Pope" <some address.com> wrote in message 
news:f5ipqd$2m6j$1 digitalmars.com...

 can we expect some annotation for functions which says 'this function 
 doesn't read/write any global variables?'

Personally, I like the PHP way; each global variable must be declared as such before use; the syntax is similar to a local variable. Any function including any "global" declarations is thus "not pure". ....int b .... ....int foo() ....{ ........global int b; ........ ........b++; ........return b * 2; ....} .... ....int square(int x) ....{ ........return x * x; ....} .... ....pure int baz() ....{ ........return foo(); // fails: foo is impure (it has "global" declarations) ....} .... ....pure int bar(int x) ....{ ........global int b; // fails: global conflicts with pure ........ ........return x*(b++); // would fail ....}
Jun 23 2007
parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Martin Howe wrote:
 "Reiner Pope" <some address.com> wrote in message 
 news:f5ipqd$2m6j$1 digitalmars.com...
 
 can we expect some annotation for functions which says 'this function 
 doesn't read/write any global variables?'

Personally, I like the PHP way; each global variable must be declared as such before use; the syntax is similar to a local variable. Any function including any "global" declarations is thus "not pure".

That might actually work in D: in PHP, it was, for me, the source of countless bugs, as you don't have to declare local variables before use. Thus I'd happily use something like "return $b++" without "global $b" and wonder why the function always returned zero. A good plus about such a requirement is that it discourages you from writing functions that rely on many globals: when you start thinking about something like http://www.php.net/manual/en/language.variables.scope.php#18748 you should consider refactoring instead. However, it can still make code unwieldy when you're writing a lot of one-liners, each of which need to refer to the same global, and you need to repeat the "global foo bar" in each one. I'd say "pure" wins here, though "global" isn't without its merits. -- Remove ".doesnotlike.spam" from the mail address.
Jun 23 2007
prev sibling next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Reiner Pope wrote:
 You mention functional programming a fair bit with respect to const, 
 which is nice to hear. But nothing in the current const system allows 
 you to declare a verifiably 'pure' function; can we expect some 
 annotation for functions which says 'this function doesn't read/write 
 any global variables?'
 
     int b
 
     int foo()
     {
         b++;
         return b * 2;
     }
 
     pure int square(int x)
     {
         return x * x;
     }
 
     pure int baz()
     {
         return foo(); // fails: foo is not pure
     }

I have two questions which relate to this. 1). Can invariant member functions change globals and / or static members? According to the doc I would think so, but the compiler crashes with this assert in that case: ito->isInvariant() on line 335 in file mtype.c. If invariant member functions only guarantee invariance on the object / struct they are member of, is this the desired semantics? When looked at from the pov of an ADT, invariant member functions do not guarantee immutability of an object / struct, since their evaluation can rely on outside mutable state. I'm not sure I can oversee the consequences, but what are the obstacles to defining the semantics of invariant member functions as pure functions? 2) What are the semantics of invariant free functions, or do they not have any special meaning? Again, can invariant be used to mean pure here? It would seem logical to me to apply invariance to the evaluation of functions as well as what they possibly refer to.
Jun 23 2007
parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Lutger wrote:
 1). Can invariant member functions change globals and / or static
 members? According to the doc I would think so, but the compiler crashes
 with this assert in that case: ito->isInvariant() on line 335 in file
 mtype.c.

File a bug! -- Remove ".doesnotlike.spam" from the mail address.
Jun 23 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Reiner Pope wrote:
 You mention functional programming a fair bit with respect to const, 
 which is nice to hear. But nothing in the current const system allows 
 you to declare a verifiably 'pure' function; can we expect some 
 annotation for functions which says 'this function doesn't read/write 
 any global variables?'

You're right, we're moving in the right direction, but we're not there yet.
Jun 23 2007
parent reply eao197 <eao197 intervale.ru> writes:
On Sat, 23 Jun 2007 20:44:41 +0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Reiner Pope wrote:
 You mention functional programming a fair bit with respect to const,  
 which is nice to hear. But nothing in the current const system allows  
 you to declare a verifiably 'pure' function; can we expect some  
 annotation for functions which says 'this function doesn't read/write  
 any global variables?'

You're right, we're moving in the right direction, but we're not there yet.

Here is an interesting quote from Joe Armstrong dissertation "Making reliable distributed systems in the presence of software errors" [1] (I highlight most important fragments by **): <quote> In the above table the code has been subject to a simple analysis which superficially classifies each module or function as “clean” or “dirty.” A module is considered dirty if any function in the module is dirty, otherwise it is clean. To simplify matters I say that a function is dirty if it sends or receives a message or if it calls one of the following Erlang BIFs: ... The reason for this classification is that any code fragment which calls one of these BIFs is potentially dangerous. **Notice that I have chosen a particularly simple definition of “dirty.”** At first sight it might appear that it would be better to recursively define a module as being dirty if any function in the module calls a “dangerous” BIF or a dirty function in another module. Unfortunately with such a definition virtually every module in the system would be classified as dirty. The reason for this is that if you compute the transitive closure of all functions calls exported from a particular module, **the transitive closure will include virtually every module in the system**. The reason why the transitive closure is so large is due to “leakage” which occurs from many of the modules in the Erlang libraries. **We take the simplifying view** that all modules are well-written and tested, and that if they do contain side-effects, that the module has been written in such a way so that the side effects do not leak out from the module to adversely affect code which calls the module. </quote> $8.3, p.171. Armstrong said that about the AX301 software -- more the 1.1 millions of Erlang code (this is one of the biggest FP software in the world). But even in pure functional Erlang there isn't special marks for 'pure/clean' or 'impure/dirty' functions. Because it can lead to that almost all modules in a system would be 'dirty'. Adding 'pure' modificator to D can lead, IMHO, to the following alternatives: 1) nobody would use 'pure' at top level of modules/libraries/frameworks, because any top-level function/method would use some 'impure' function inside, or 2) it is necessary to introduce some constructs into language to define 'logical pureness' -- function will be marked as pure, but can use impure calls inside. Like 'mutable' keyword in C++ used for 'logical constantness'. [1] http://www.sics.se/~joe/thesis/armstrong_thesis_2003.pdf -- Regards, Yauheni Akhotnikau
Jun 24 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
eao197 wrote:
 Adding 'pure' modificator to D can lead, IMHO, to the following 
 alternatives:
 1) nobody would use 'pure' at top level of modules/libraries/frameworks, 
 because any top-level function/method would use some 'impure' function 
 inside, or
 2) it is necessary to introduce some constructs into language to define 
 'logical pureness' -- function will be marked as pure, but can use 
 impure calls inside. Like 'mutable' keyword in C++ used for 'logical 
 constantness'.

I think you're probably right.
Jun 25 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 However, my point was that to a programmer, optimization should be 
 invisible.

I can't agree with that. The compiler and the programmer need to cooperate with each other in order to produce fast programms. If a programmer just throws the completed program "over the wall" to the optimizer and expects good results, he'll be sadly disappointed.

I suppose the issue is where the line should be drawn, and that line is constantly shifting. Previously, "register" was actually a valuable tool in C/C++, now it's a pointless anachronism. Techniques for producing optimal loops varied across compiler and platform--in some cases using pointers was better, in some cases array indexing was better--and now those are largely pointless as well. But I think there are a few different factors to be considered here. A significant one is that compilers have simply gotten better. Another is that hardware has improved tremendously. But it's interesting that you mentioned FORTRAN as being the language to beat for numerics performance because it's positively ancient and, to my knowledge, does not require the programmer to make any extra effort to produce such optimal code. It's just a side-effect of the language design. I'm sure you recognize this because D has 'foreach', which is as much an optimization tool as it is a programming aid. The thing is, 'foreach' would be great from a programmer perspective even if it were the bane of compiler optimization. It clarifies loop syntax, reduces errors, and adds a great deal of flexibility for iteration. These are sort of features I want to see in D: those that make code more elegant and maintainable, and which produce optimal code simply as a side-effect of their design. I suppose this is one reason I'm not quite ready to give up on "const by default" yet. Converting code may be more difficult than with the current design, but the result seems to have the potential to be both cleaner and more suited to deep compiler optimization.
 I can appreciate that 'invariant' may be of tremendous use to the 
 compiler, but I balk at the notion of adding language features that 
 seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.

Could you explain? I've been trying to determine how I would write a library using these new features, and so far, 'invariant' simply seems more an obstacle to maintainability (because of the necessary code duplication), than it is an aid to programming.
 Rather, it would be preferable if the language were structured in such 
 a way as to make such hints unnecessary.

That would be preferable, but experience with such languages is that they are complete failures at producing fast code. Fast code comes from a language that enables the programmer to work with the optimizer to produce better code.

Really? I thought that many functional programming languages were quite fast. And FORTRAN seems a suitable example for an imperative language which is quite fast as well. To my knowledge, none of these contain features specifically intended for optimizing code.
 Lots of people think that just 'cuz they write in C++, which has good 
 optimizers available, they'll get fast code. That's not even close to 
 being true.

Of course not. Garbage in, garbage out.
 To that end, and speaking as someone who isn't primarily involved in 
 numerics programming, my impression of FORTRAN is that the language is 
 syntactically suited to numerics programming, while C++ is not.  Even 
 if C++ performed on par with FORTRAN for similar work (and Bjarne 
 suggested last year that it could), I would likely still choose 
 FORTRAN over C++ because the syntax seems so much more appealing for 
 that kind of work.

I programmed for years in FORTRAN. The syntax is not appealing, in fact, it sucks. The reason it is suited for numerics programming is because FORTRAN arrays, by definition, cannot be aliased. This means that optimizers can go to town parallelizing array operations, which is a big, big deal for speed. You can't do that with C/C++ because arrays can be aliased. I have a stack of papers 6" deep in the basement on trying to make C more suitable for numerics, and it's mostly about, you guessed it, fixing the alias problem. They failed.

That's the big secret? Weird. For some reason I assumed it was a bit less specific.
 There are some template libraries for C++ which parallelize array 
 operations and can finally approach FORTRAN. But they are horrifically 
 kludgy and complicated. No thanks. But the effort that has gone into 
 them is astonishing, and is indicative of how severe the problem is.

Yup. And I agree that this isn't the proper route to follow. Heck, it's one reason I've lost interest in C++.
 I'm less sure about that. I think we're all so used to C++ and its 
 mushy concept of const that we don't know yet what will emerge from 
 the use of invariant. I do know, however, that those who want to do 
 advanced array optimizations are going to want to be using invariant 
 function parameters.

You may be right, and I'm certainly willing to give it a try. This is simply my initial reaction to the new design, and I wanted to voice it before becoming placated by experience. My gut feeling is that a better design is possible, and I'm not yet ready to close the door on alternatives.

Andrei, I and Bartosz have each expended probably a hundred hours trying to figure this out, and we've tried a lot of designs. If there is a better design, it's not like we haven't tried. I wish to reiterate that const designs in other languages like C++ (and to some extent Java) utterly fail at the objectives we set for const in D. Furthermore, Andrei is familiar with the many research papers on the topic, which were of invaluable help. D's const system is more ambitious than any of them.

I really do appreciate the effort which you all have made to find a solid design, and perhaps I simply don't have enough experience with it to feel comfortable with it yet. As you've no doubt noticed, my issue is with 'invariant' from a conceptual and a code maintenance standpoint. On the one hand we have 'const' and on the other we have 'really really const'. It just sticks in my craw that we need two separate keywords for what seem to be nearly identical properties, and that I may have to write separate code to suit each, etc, not to mention trying to explain all of it to a new programmer.
 The compiler can inspect the code however, and a global const is as 
 good as an invariant for optimization (as far as I know).  As for the 
 rest, I think the majority of remaining cases aren't ones where 
 'invariant' would apply anyway: dynamic buffers whose contents are 
 guaranteed not to change either in word or by design, etc.

But they do apply - that's the whole array optimization thing. You're not just going to have global arrays.

Perhaps I misunderstood the "must be known at compile-time" clause. Can 'invariant' apply to dynamic arrays that will remain unchanged once initialized? In my work I use almost no static data--it's all generated on the fly or loaded from some data source. Will 'invariant' help to make my code more optimal? Sean
Jun 23 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 However, my point was that to a programmer, optimization should be 
 invisible.

I can't agree with that. The compiler and the programmer need to cooperate with each other in order to produce fast programms. If a programmer just throws the completed program "over the wall" to the optimizer and expects good results, he'll be sadly disappointed.

I suppose the issue is where the line should be drawn, and that line is constantly shifting. Previously, "register" was actually a valuable tool in C/C++, now it's a pointless anachronism. Techniques for producing optimal loops varied across compiler and platform--in some cases using pointers was better, in some cases array indexing was better--and now those are largely pointless as well. But I think there are a few different factors to be considered here. A significant one is that compilers have simply gotten better. Another is that hardware has improved tremendously. But it's interesting that you mentioned FORTRAN as being the language to beat for numerics performance because it's positively ancient and, to my knowledge, does not require the programmer to make any extra effort to produce such optimal code. It's just a side-effect of the language design.

It's a side effect of: 1) no pointers 2) no aliasing It's not a result of genius on the part of FORTRAN's designers. And it's only more efficient for certain types of code - for everything else, it sucks.
 I'm sure you recognize this because D has 'foreach', which is as much an 
 optimization tool as it is a programming aid.  The thing is, 'foreach' 
 would be great from a programmer perspective even if it were the bane of 
 compiler optimization.  It clarifies loop syntax, reduces errors, and 
 adds a great deal of flexibility for iteration.  These are sort of 
 features I want to see in D: those that make code more elegant and 
 maintainable, and which produce optimal code simply as a side-effect of 
 their design.  I suppose this is one reason I'm not quite ready to give 
 up on "const by default" yet.  Converting code may be more difficult 
 than with the current design, but the result seems to have the potential 
 to be both cleaner and more suited to deep compiler optimization.
 
 I can appreciate that 'invariant' may be of tremendous use to the 
 compiler, but I balk at the notion of adding language features that 
 seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.

Could you explain? I've been trying to determine how I would write a library using these new features, and so far, 'invariant' simply seems more an obstacle to maintainability (because of the necessary code duplication), than it is an aid to programming.

For one thing, if you have a reference to an invariant in a multithreaded program, you won't have to worry about putting a lock around it in order to read it. For the FP aspect, it means that if a function only depends on its parameters, and its parameters are passed by value, the compiler has enough information about it to reorder when the function is executed, which means it can parallelize it. If the compiler cannot determine the side effects, it is forced to execute the functions serially.
 That would be preferable, but experience with such languages is that 
 they are complete failures at producing fast code. Fast code comes 
 from a language that enables the programmer to work with the optimizer 
 to produce better code.

Really? I thought that many functional programming languages were quite fast.

I didn't mean FP languages, I meant languages where one is supposed to ignore optimization and let the compiler do it all.
 And FORTRAN seems a suitable example for an imperative language 
 which is quite fast as well.  To my knowledge, none of these contain 
 features specifically intended for optimizing code.

It's an unintended side effect of FORTRAN's primitive semantics.
 I programmed for years in FORTRAN. The syntax is not appealing, in 
 fact, it sucks. The reason it is suited for numerics programming is 
 because FORTRAN arrays, by definition, cannot be aliased. This means 
 that optimizers can go to town parallelizing array operations, which 
 is a big, big deal for speed.

 You can't do that with C/C++ because arrays can be aliased. I have a 
 stack of papers 6" deep in the basement on trying to make C more 
 suitable for numerics, and it's mostly about, you guessed it, fixing 
 the alias problem. They failed.

That's the big secret? Weird. For some reason I assumed it was a bit less specific.

That's the big secret.
 I really do appreciate the effort which you all have made to find a 
 solid design, and perhaps I simply don't have enough experience with it 
 to feel comfortable with it yet.  As you've no doubt noticed, my issue 
 is with 'invariant' from a conceptual and a code maintenance standpoint. 
  On the one hand we have 'const' and on the other we have 'really really 
 const'.  It just sticks in my craw that we need two separate keywords 
 for what seem to be nearly identical properties, and that I may have to 
 write separate code to suit each, etc, not to mention trying to explain 
 all of it to a new programmer.

It's like int and uint - most of the time, they behave the same. But when they're different, they're very different.
 Perhaps I misunderstood the "must be known at compile-time" clause.

That's only when you use invariant as a storage class. It doesn't apply when you use it as a type constructor.
  Can 
 'invariant' apply to dynamic arrays that will remain unchanged once 
 initialized?

Yes. In my work I use almost no static data--it's all generated
 on the fly or loaded from some data source.  Will 'invariant' help to 
 make my code more optimal?

Not with the current compiler, because the back end hasn't been modified to take advantage of it. But the potential is there.
Jun 23 2007
parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 
 Perhaps I misunderstood the "must be known at compile-time" clause.

That's only when you use invariant as a storage class. It doesn't apply when you use it as a type constructor.
  Can 'invariant' apply to dynamic arrays that will remain unchanged 
 once initialized?

Yes.

Ah, now it's coming clear. For some reason I thought 'invariant' only worked as a storage class. I guess my only real issue with the design now is the use of the word 'invariant' -- it's rather long :-) In some respects I'd almost prefer using 'const' for 'invariant' and 'view' for 'const'. But I think this suggestion has come up before and 'view' was shot down because it's both a noun and a verb?
   In my work I use almost no static data--it's all generated
 on the fly or loaded from some data source.  Will 'invariant' help to 
 make my code more optimal?

Not with the current compiler, because the back end hasn't been modified to take advantage of it. But the potential is there.

Yeah, I don't care if the compiler does it now, only that the potential is there. After all, this discussion is about the design, not the implementation. Thanks for explaining things. I'm coming to understand the reasoning behind the design you've chosen. Sean
Jun 23 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Yeah, I don't care if the compiler does it now, only that the potential 
 is there.  After all, this discussion is about the design, not the 
 implementation.

There's quite a bit of unrealized potential for better code right now.
 Thanks for explaining things.  I'm coming to understand the reasoning 
 behind the design you've chosen.

Only time will tell if we screwed it up or not, but there is reasoning behind it <g>.
Jun 25 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Sean Kelly wrote:
 
 I can appreciate that 'invariant' may be of tremendous use to the 
 compiler, but I balk at the notion of adding language features that 
 seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.

Could you explain? I've been trying to determine how I would write a library using these new features, and so far, 'invariant' simply seems more an obstacle to maintainability (because of the necessary code duplication), than it is an aid to programming.

In which situation does having 'invariant' lead to code duplication? I can see the common situation where you have class getter methods, or other similiar methods, but even without 'invariant' and only with const and non-const you have that problem.
 
 The compiler can inspect the code however, and a global const is as 
 good as an invariant for optimization (as far as I know).  As for the 
 rest, I think the majority of remaining cases aren't ones where 
 'invariant' would apply anyway: dynamic buffers whose contents are 
 guaranteed not to change either in word or by design, etc.

But they do apply - that's the whole array optimization thing. You're not just going to have global arrays.

Perhaps I misunderstood the "must be known at compile-time" clause. Can 'invariant' apply to dynamic arrays that will remain unchanged once initialized? In my work I use almost no static data--it's all generated on the fly or loaded from some data source. Will 'invariant' help to make my code more optimal? Sean

Of course, that's the whole point of invariant. The 'invariant' keyword is being used for two things, compile-time values, and invariant data. You obviously can't do what you mention with compile-time values, but you can with invariant data (which is created at runtime). (or have I misunderstood you, and you already knew that?) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 23 2007
next sibling parent Derek Parnell <derek psych.ward> writes:
On Sat, 23 Jun 2007 18:22:41 +0100, Bruno Medeiros wrote:

 Of course, that's the whole point of invariant. The 'invariant' keyword 
 is being used for two things  ...

And thus a source of confusion. Overloading keywords is not a smart move. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Jun 23 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Bruno Medeiros wrote:
 Sean Kelly wrote:
 I can appreciate that 'invariant' may be of tremendous use to the 
 compiler, but I balk at the notion of adding language features that 
 seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.

Could you explain? I've been trying to determine how I would write a library using these new features, and so far, 'invariant' simply seems more an obstacle to maintainability (because of the necessary code duplication), than it is an aid to programming.

In which situation does having 'invariant' lead to code duplication? I can see the common situation where you have class getter methods, or other similiar methods, but even without 'invariant' and only with const and non-const you have that problem.

Let's say I am writing a linear search routine for arrays. The most obvious implementation would accept that array as 'const', but it may be more optimal to use 'invariant'. Since not all searchable arrays will be invariant however, both implementations must exist. That said, a template function could likely handle both situations with the same code, and hopefully it will be uncommon to want virtual class routines that take both types of parameters. Though I guess the same could be done there with a single template implementation and aliases.
 The compiler can inspect the code however, and a global const is as 
 good as an invariant for optimization (as far as I know).  As for 
 the rest, I think the majority of remaining cases aren't ones where 
 'invariant' would apply anyway: dynamic buffers whose contents are 
 guaranteed not to change either in word or by design, etc.

But they do apply - that's the whole array optimization thing. You're not just going to have global arrays.

Perhaps I misunderstood the "must be known at compile-time" clause. Can 'invariant' apply to dynamic arrays that will remain unchanged once initialized? In my work I use almost no static data--it's all generated on the fly or loaded from some data source. Will 'invariant' help to make my code more optimal?

Of course, that's the whole point of invariant. The 'invariant' keyword is being used for two things, compile-time values, and invariant data. You obviously can't do what you mention with compile-time values, but you can with invariant data (which is created at runtime). (or have I misunderstood you, and you already knew that?)

I didn't know that, and that was the source of my confusion. I had thought that 'invariant' only applied to data which was initialized at compile-time. That it can apply to run-time initialized data makes a world of difference. Sean
Jun 23 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Sean Kelly wrote:
 Bruno Medeiros wrote:
 Sean Kelly wrote:
 I can appreciate that 'invariant' may be of tremendous use to the 
 compiler, but I balk at the notion of adding language features that 
 seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.

Could you explain? I've been trying to determine how I would write a library using these new features, and so far, 'invariant' simply seems more an obstacle to maintainability (because of the necessary code duplication), than it is an aid to programming.

In which situation does having 'invariant' lead to code duplication? I can see the common situation where you have class getter methods, or other similiar methods, but even without 'invariant' and only with const and non-const you have that problem.

Let's say I am writing a linear search routine for arrays. The most obvious implementation would accept that array as 'const', but it may be more optimal to use 'invariant'. Since not all searchable arrays will be invariant however, both implementations must exist. That said, a template function could likely handle both situations with the same code, and hopefully it will be uncommon to want virtual class routines that take both types of parameters. Though I guess the same could be done there with a single template implementation and aliases.

Well, but that code duplication (even if simplified by the use of templates), is optional, because the invariant version of that function is just an optimization that can be made for some kinds of arrays. With only const in the language, but no invariant, you don't even have that option, so you are not any worse by having invariant in the language (in terms of the aformentioned code duplication). I think it has now been clearly established the usefulness of having both const and invariant type modifiers (which Walter has explained about a lot), and I hope everyone understands that now. We should focus now on the implementation details of our current proposal, for which there are several issues: * Having invariant/const being used differently as a storage-class and type-modifier. Is this a good thing? (IMO, no, it is a major unnecessary source of confusion) * The fact that the top-level type is never declared const/invariant when using const/invariant as type modifiers. Is this a horrible breach in consistency, or is actually a key necessary aspect of the design? (IMO, it's either one or the other, but I'm still not sure which) * How to deal with code duplication, especially in class getter methods. where the only thing that changes is the type modifier of the implicit 'this' parameter and some other input or output. * How will meta-programming, specialization, IFTI, etc. work with these type modifiers? * ... and other issues that there may be. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 24 2007
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sat, 23 Jun 2007 08:32:36 -0700, Sean Kelly wrote:

 Will 'invariant' help to make my code more optimal?

Yeah ... what he said. I'm with Sean on these points. There seems to be a false dichotomy being peddled here; that optimal run-time performance is mutually exclusive with source code that is cheap to write and maintain. D is so close to given us both that I constantly scream at the ugly warts that is being instilled into this wonderful language. I agree that the concepts behind 'const' and 'invariant' represent two different and valid concepts that need to be catered for. The problem is in their proposed implementation. The keywords chosen are a hinderence to D source maintenance because they both appear to mean either of these two concepts. Sure, they will be learned and distingushed in one's mind over time, I've no doubt of that, but that is the problem in itself. Instead, why not use different key words that actually reflect the differences in the two concepts. A second issue I have is that by forcing 'const' to be explicitly coded in function parameter lists, you are reducing the ability of the compiler to help detect bugs. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Jun 23 2007
parent reply Sean Kelly <sean f4.ca> writes:
Derek Parnell wrote:
 On Sat, 23 Jun 2007 08:32:36 -0700, Sean Kelly wrote:
 
 Will 'invariant' help to make my code more optimal?

Yeah ... what he said. I'm with Sean on these points. There seems to be a false dichotomy being peddled here; that optimal run-time performance is mutually exclusive with source code that is cheap to write and maintain.

I am coming to feel that Walter's argument may just have been poorly presented, and that this is actually not the case. But I'll admit some lingering trepidation about the apparent complexity of the design.
 D is so close to given us both that I constantly scream at the ugly warts
 that is being instilled into this wonderful language.

Same here.
 I agree that the concepts behind 'const' and 'invariant' represent two
 different and valid concepts that need to be catered for. The problem is in
 their proposed implementation. The keywords chosen are a hinderence to D
 source maintenance because they both appear to mean either of these two
 concepts. Sure, they will be learned and distingushed in one's mind over
 time, I've no doubt of that, but that is the problem in itself. Instead,
 why not use different key words that actually reflect the differences in
 the two concepts.

Exactly. I think a lot of the confusion (now and in the future) may stem from the semantic overlap of these two terms. The best alternative I've been able to come up with, though, is 'view' in place of 'const' and 'const' in place of 'invariant'. And I know this suggestion has been shot down in the past. 'final' seems to fit the use to which it has been put, however.
 A second issue I have is that by forcing 'const' to be explicitly coded in
 function parameter lists, you are reducing the ability of the compiler to
 help detect bugs.

Isn't this necessary though? Say the function declaration is available to the compiler but the implementation is not. The compiler needs to know what kind of data can legally be passed as parameters. Sean
Jun 23 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Derek Parnell wrote:

 A second issue I have is that by forcing 'const' to be explicitly 
 coded in
 function parameter lists, you are reducing the ability of the compiler to
 help detect bugs.

Isn't this necessary though? Say the function declaration is available to the compiler but the implementation is not. The compiler needs to know what kind of data can legally be passed as parameters.

I took that to mean he wanted to make const the default. I too am disappointed that the only rebuttal from Walter on const by default has been "some C++ guys I talked to thought it was weird". --bb
Jun 23 2007
parent reply Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 Derek Parnell wrote:

 A second issue I have is that by forcing 'const' to be explicitly 
 coded in
 function parameter lists, you are reducing the ability of the 
 compiler to
 help detect bugs.

Isn't this necessary though? Say the function declaration is available to the compiler but the implementation is not. The compiler needs to know what kind of data can legally be passed as parameters.

I took that to mean he wanted to make const the default. I too am disappointed that the only rebuttal from Walter on const by default has been "some C++ guys I talked to thought it was weird".

Same here. It's an elegant concept and I'd really like to give it a try. I think only experience could tell whether it's a practical solution. Sean
Jun 23 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Bill Baxter wrote:
 Sean Kelly wrote:
 Derek Parnell wrote:

 A second issue I have is that by forcing 'const' to be explicitly 
 coded in
 function parameter lists, you are reducing the ability of the 
 compiler to
 help detect bugs.

Isn't this necessary though? Say the function declaration is available to the compiler but the implementation is not. The compiler needs to know what kind of data can legally be passed as parameters.

I took that to mean he wanted to make const the default. I too am disappointed that the only rebuttal from Walter on const by default has been "some C++ guys I talked to thought it was weird".

Same here. It's an elegant concept and I'd really like to give it a try. I think only experience could tell whether it's a practical solution.

Oh I forgot -- he did give two reasons, actually: 1) some c++ guys he talked to think it's weird. 2) didn't want to have to have a mutable keyword. (And in some other message later he said something about not wanting 'mutable' in D because it left a bad taste in the mouth of some C++ guys.) I still think anti-const keywords would be useful even *without* const by default. So #2 seems kind of odd to me. Seems to me it would be very handy to be able to say something like: mutable T foo(T)(T input) { mutable T mungeable; // munge the mungeable based in the input return mungeable } Sure there's other ways to accomplish that, but that seems like a particularly clear way to express "same type as T but not const". You could also make templates to strip it like they do in C++. then you could have Mutable!(T) foo(T)(T input) { } Either way I don't see any real insurmountable or even troublesome issue there with #2. --bb
Jun 23 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:
 I too am disappointed that the only rebuttal from Walter on const by
 default has been "some C++ guys I talked to thought it was weird".

Same here. It's an elegant concept and I'd really like to give it a try. I think only experience could tell whether it's a practical solution.

Oh I forgot -- he did give two reasons, actually: 1) some c++ guys he talked to think it's weird. 2) didn't want to have to have a mutable keyword. (And in some other message later he said something about not wanting 'mutable' in D because it left a bad taste in the mouth of some C++ guys.)

I thought we had established that "some C++ guys" will always find something wrong with any non-C++ language? That's not a good reason for anything. -- Remove ".doesnotlike.spam" from the mail address.
Jun 24 2007
parent Lars Ivar Igesund <larsivar igesund.net> writes:
Deewiant wrote:

 Bill Baxter wrote:
 Sean Kelly wrote:
 Bill Baxter wrote:
 I too am disappointed that the only rebuttal from Walter on const by
 default has been "some C++ guys I talked to thought it was weird".

Same here. It's an elegant concept and I'd really like to give it a try. I think only experience could tell whether it's a practical solution.

Oh I forgot -- he did give two reasons, actually: 1) some c++ guys he talked to think it's weird. 2) didn't want to have to have a mutable keyword. (And in some other message later he said something about not wanting 'mutable' in D because it left a bad taste in the mouth of some C++ guys.)

I thought we had established that "some C++ guys" will always find something wrong with any non-C++ language? That's not a good reason for anything.

Yep, it's probably the biggest non-reason there is in language design. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Jun 24 2007
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 However, my point was that to a programmer, optimization should be 
 invisible.

I can't agree with that. The compiler and the programmer need to cooperate with each other in order to produce fast programms. If a programmer just throws the completed program "over the wall" to the optimizer and expects good results, he'll be sadly disappointed.
 I can appreciate that 'invariant' may be of tremendous use to the 
 compiler, but I balk at the notion of adding language features that 
 seem largely intended as compiler "hints."

It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.
 Rather, it would be preferable if the language were structured in such 
 a way as to make such hints unnecessary.

That would be preferable, but experience with such languages is that they are complete failures at producing fast code. Fast code comes from a language that enables the programmer to work with the optimizer to produce better code. Lots of people think that just 'cuz they write in C++, which has good optimizers available, they'll get fast code. That's not even close to being true.
 To that end, and speaking as someone who isn't primarily involved in 
 numerics programming, my impression of FORTRAN is that the language is 
 syntactically suited to numerics programming, while C++ is not.  Even 
 if C++ performed on par with FORTRAN for similar work (and Bjarne 
 suggested last year that it could), I would likely still choose 
 FORTRAN over C++ because the syntax seems so much more appealing for 
 that kind of work.

I programmed for years in FORTRAN. The syntax is not appealing, in fact, it sucks. The reason it is suited for numerics programming is because FORTRAN arrays, by definition, cannot be aliased. This means that optimizers can go to town parallelizing array operations, which is a big, big deal for speed. You can't do that with C/C++ because arrays can be aliased. I have a stack of papers 6" deep in the basement on trying to make C more suitable for numerics, and it's mostly about, you guessed it, fixing the alias problem. They failed.

How much of this will actually be solved by invariant? It seems to me that typically, arrays are built up element-by-element, even if they never change again. I guess in D2.0, such code would initialize a temporary array, and finally idup it? The aliasing problem will be far from gone, though. Consider an in-place operation on a matrix. Frequently you modify one only row at a time, based on the remaining rows. I think you'd have to cast(invariant) those other rows, to avoid aliasing.
 There are some template libraries for C++ which parallelize array 
 operations and can finally approach FORTRAN. But they are horrifically 
 kludgy and complicated. No thanks. But the effort that has gone into 
 them is astonishing, and is indicative of how severe the problem is.

Actually in C++ I think there's a problem with operator overloading, not just a problem with const. Op overloads destroy far too much valuable information. I'll discuss that a bit in my conference presentation. Interesting thing is, with poor-man's-macros (mixin + ctfe), you can detect a great many cases of aliasing (since two static arrays are guaranteed not to alias each other). And you can also assert that there's no overlap between arrays, then generate code assuming no aliasing. Invariant would move that to a compile-time check, but if you had array operations built-in, I'm not sure how much extra benefit it would give (D can do a *much* better job than C++ already).
Jun 23 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Don Clugston wrote:
 Walter Bright wrote:

 You can't do that with C/C++ because arrays can be aliased. I have a 
 stack of papers 6" deep in the basement on trying to make C more 
 suitable for numerics, and it's mostly about, you guessed it, fixing 
 the alias problem. They failed.

How much of this will actually be solved by invariant? It seems to me that typically, arrays are built up element-by-element, even if they never change again. I guess in D2.0, such code would initialize a temporary array, and finally idup it? The aliasing problem will be far from gone, though. Consider an in-place operation on a matrix. Frequently you modify one only row at a time, based on the remaining rows. I think you'd have to cast(invariant) those other rows, to avoid aliasing.

Thanks for asking this. It's been eating at me too. From what I understand of Fortran code, you declare an array of a given size, set its values however you like, then pass it to a function. But if we make an invariant D array we can't set its values after initialization. So it doesn't seem very useful. Or is the use pattern going to be, as you say, Don, casting to invariant every time you need to call fast_math_array_function()? If that's the case I suspect it won't take long before this becomes an idiom: /* x += y array-style */ add_accum(double[] x, const double[] y) { _add_accum_impl(cast(invariant)x,cast(invariant)y); } Is that really what we want? That code can't even be right though, because x is *supposed* to get modified there. So how do you denote lack of aliasing on something like that using invariant? In this respect C99's 'restrict' seems to be a lot more understandable. My naive understanding of it being that you just slap it on any argument that you want to tell the compiler it can assume to be non-aliased. --bb
Jun 23 2007
next sibling parent reply "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Sun, 24 Jun 2007 07:44:33 +0300, Bill Baxter  =

<dnewsgroup billbaxter.com> wrote:
 Don Clugston wrote:
 Walter Bright wrote:

 You can't do that with C/C++ because arrays can be aliased. I have a=



 stack of papers 6" deep in the basement on trying to make C more  =



 suitable for numerics, and it's mostly about, you guessed it, fixing=



 the alias problem. They failed.

It seems to me that typically, arrays are built up element-by-element=


 even if they never change again. I guess in D2.0, such code would  =


 initialize a temporary array, and finally idup it?
  The aliasing problem will be far from gone, though. Consider an  =


 in-place operation on a matrix. Frequently you modify one only row at=


 time, based on the remaining rows. I think you'd have to  =


 cast(invariant) those other rows, to avoid aliasing.

Thanks for asking this. It's been eating at me too. From what I =

 understand of Fortran code, you declare an array of a given size, set =

 its values however you like, then pass it to a function.  But if we ma=

 an invariant D array we can't set its values after initialization.  So=

 it doesn't seem very useful.  Or is the use pattern going to be, as yo=

 say, Don, casting to invariant every time you need to call  =

 fast_math_array_function()?  If that's the case I suspect it won't tak=

 long before this becomes an idiom:

     /* x +=3D y array-style */
     add_accum(double[] x, const double[] y) {
        _add_accum_impl(cast(invariant)x,cast(invariant)y);
     }

 Is that really what we want?
 That code can't even be right though, because x is *supposed* to get  =

 modified there. So how do you denote lack of aliasing on something lik=

 that using invariant?  In this respect C99's 'restrict' seems to be a =

 lot more understandable.  My naive understanding of it being that you =

 just slap it on any argument that you want to tell the compiler it can=

 assume to be non-aliased.

 --bb

I've been asking the same questions myself too. In serial coding, will 'invariant' only be used (in practice) to solve t= he = aliasing problem? You have a mutable data structure, and you're passing it as an invariant= = to functions. Lots of 'cast(invariant)' will occur. In parallel coding, 'invariant' will be more useful. However, truly = 'invariant' data cannot ever be changed. What I would want is a way to tell the compiler that the dat= a = will not be changed *during the execution* of a function. Hm, let there be the following function: void foo(invariant Bar bar); Then I could use mutexes and casting as follows: MUTEX_LOCK(a) foo(cast(invariant)a); MUTEX_UNLOCK(a) So 'cast(invariant)' will occur again. The *programmer* needs to be sure that data passed to functions is truly= = invariant. What if we drop out the 'invariant' keyword, and simply define that = aliasing is illegal (by default) and result in undefined behaviour? That is, 'const' would mean a read-only view to immutable data (that = cannot be changed during the execution of a function).
Jun 24 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Kristian Kilpi wrote:
 
 What if we drop out the 'invariant' keyword, and simply define that 
 aliasing
 is illegal (by default) and result in undefined behaviour?

That would pretty much invalidate the use of slicing, which is one of the most powerful features of the language. Sean
Jun 24 2007
parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Sun, 24 Jun 2007 19:03:37 +0300, Sean Kelly <sean f4.ca> wrote:

 Kristian Kilpi wrote:
  What if we drop out the 'invariant' keyword, and simply define that  
 aliasing
 is illegal (by default) and result in undefined behaviour?

That would pretty much invalidate the use of slicing, which is one of the most powerful features of the language. Sean

On Sun, 24 Jun 2007 19:03:37 +0300, Sean Kelly <sean f4.ca> wrote:
 Kristian Kilpi wrote:
  What if we drop out the 'invariant' keyword, and simply define that  
 aliasing
 is illegal (by default) and result in undefined behaviour?

That would pretty much invalidate the use of slicing, which is one of the most powerful features of the language. Sean

You mean that a programmer has been slicing arrays around so much that (s)he cannot know (be sure) if two arrays/slices overlap? ;) But, if the slices do overlap, and one of them is modified, wouldn't that (very likely) be a bug anyway (that is, when you do that 'by accident')? -> The same aliasing problem? For instance, 'memcpy()' does not work correctly if the destination and source overlap. You have to use 'memmove()'. (Constant aliasing would be ok of course. E.g. "void f(const int *a, const int *b); f(&v, &v);")
Jun 24 2007
prev sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Kristian Kilpi wrote:
 What I would want is a way to tell the compiler that the 
 data will not be changed
 *during the execution* of a function.

scope invariant. "invariant" means the data referenced won't be changed, "scope" means the reference will be invalid after the function exits (and thus the "invariantness" no longer needs to be guaranteed from that point on since any access to the data is an error anyway).
Jun 24 2007
prev sibling parent Reiner Pope <some address.com> writes:
Bill Baxter wrote:
 Don Clugston wrote:
 Walter Bright wrote:

 You can't do that with C/C++ because arrays can be aliased. I have a 
 stack of papers 6" deep in the basement on trying to make C more 
 suitable for numerics, and it's mostly about, you guessed it, fixing 
 the alias problem. They failed.

How much of this will actually be solved by invariant? It seems to me that typically, arrays are built up element-by-element, even if they never change again. I guess in D2.0, such code would initialize a temporary array, and finally idup it? The aliasing problem will be far from gone, though. Consider an in-place operation on a matrix. Frequently you modify one only row at a time, based on the remaining rows. I think you'd have to cast(invariant) those other rows, to avoid aliasing.

Thanks for asking this. It's been eating at me too. From what I understand of Fortran code, you declare an array of a given size, set its values however you like, then pass it to a function. But if we make an invariant D array we can't set its values after initialization. So it doesn't seem very useful. Or is the use pattern going to be, as you say, Don, casting to invariant every time you need to call fast_math_array_function()? If that's the case I suspect it won't take long before this becomes an idiom: /* x += y array-style */ add_accum(double[] x, const double[] y) { _add_accum_impl(cast(invariant)x,cast(invariant)y); } Is that really what we want? That code can't even be right though, because x is *supposed* to get modified there. So how do you denote lack of aliasing on something like that using invariant? In this respect C99's 'restrict' seems to be a lot more understandable. My naive understanding of it being that you just slap it on any argument that you want to tell the compiler it can assume to be non-aliased. --bb

keeping everything immutable means arrays would need to be copied all over the place. Clean has uniqueness types, which allow the type system to specify that your pointer is the only one to that data. I don't know how they work, but maybe they could be an appropriate solution? (Not to mention that they've been proposed before to avoid the cast-to-invariant after creating new data) Much of the enforcement work, I think, could be done by scope (because it tells you that functions you call won't keep a pointer to your data). After that, it might give you 'restrict', but actually type-checked, and basically for free. (But that's only a guess) Reiner
Jun 24 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 Matter of opinion, I suppose.  The C++ design was immediately clear 
 to me, though it obviously wasn't for others.  I grant that the 
 aliasing problem can be confusing, but I feel that it is a 
 peripheral issue.

I don't think it is a peripheral issue. It completely screws up optimization, is useless for threading support, and has spawned endless angst about why C++ code is slower than Fortran code.

As a programmer, I consider the optimization problem to be a non-issue. Optimization is just magic that happens to make my program run faster.

Optimization often makes the difference between a successful project and a failure. C++ has failed to supplant FORTRAN, because although in every respect but one C++ is better, that one - optimization of arrays - matters a whole lot. It drives people using C++ to use inline assembler. They spend a lot of time on the issue. Various proposals to fix it, like 'noalias' and 'restrict', consume vast amounts of programmer time. And time is money.

FORTRAN is also helped by having a fairly standardized ABI that can be called easily from lots of languages, which C++ lacks. But C has that, and it has also failed to supplant FORTRAN for numeric code. But I think Sean's right. A lot of that is just that the language supports things like actual multi-dimensional arrays (by 'actual' I mean contiguous memory rather than pointers to pointers), and mathematical operations on them right out of the box. Telling a numerics person that C/C++ will give them much better IO and GUI support, but take them a step back in terms of core numerics is like trying to sell a hunter a fancy new gun with a fantastic scope that will let you pinpoint a mouse at 500 yards but -- oh, I should mention it only shoots bb's. On the other hand, I suspect there's lots of code that's written in FORTRAN supposedly for performance reasons that doesn't really need to be. Just as there's lots of code written in C++ that would perform fine in a scripting language. But people will still swear up and down that {whatever} is the only language fast enough. A lot of numerics folks do realize this, however. They just go from Fortran straight to Matlab, and skip the other compiled languages altogether. I guess what I'd like to say in summary is that I'm skeptical about the claim that optimization "often" makes the difference between success and failure. "occasionally" I could believe. Ill-advised premature optimization has probably led to the demise of many more a project than actual optimization problems in the end product. We'll all gladly take a free 20% speed improvement if the compiler can give it to us, but I don't believe there are that many projects that will fail simply for lack of that 20%. --bb
Jun 22 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Optimization often makes the difference between a successful project 
 and a failure. C++ has failed to supplant FORTRAN, because although in 
 every respect but one C++ is better, that one - optimization of arrays 
 - matters a whole lot. It drives people using C++ to use inline 
 assembler. They spend a lot of time on the issue. Various proposals to 
 fix it, like 'noalias' and 'restrict', consume vast amounts of 
 programmer time. And time is money.

FORTRAN is also helped by having a fairly standardized ABI that can be called easily from lots of languages, which C++ lacks. But C has that, and it has also failed to supplant FORTRAN for numeric code. But I think Sean's right. A lot of that is just that the language supports things like actual multi-dimensional arrays (by 'actual' I mean contiguous memory rather than pointers to pointers),

C has them too: int array[3][5]; is not an array of pointers to arrays.
 and mathematical 
 operations on them right out of the box.

No, FORTRAN does not have array operations out of the box. It has no more mathematical operations than C does (in fact, it has fewer).
 Telling a numerics person that 
 C/C++ will give them much better IO and GUI support, but take them a 
 step back in terms of core numerics is like trying to sell a hunter a 
 fancy new gun with a fantastic scope that will let you pinpoint a mouse 
 at 500 yards but -- oh, I should mention it only shoots bb's.

I've read the papers on it. It's very clear that the only technical reason FORTRAN is better than C at numerics is it doesn't have array aliasing. That's it.
 I guess what I'd like to say in summary is that I'm skeptical about the 
 claim that optimization "often" makes the difference between success and 
 failure.  "occasionally" I could believe.  Ill-advised premature 
 optimization has probably led to the demise of many more a project than 
 actual optimization problems in the end product.  We'll all gladly take 
 a free 20% speed improvement if the compiler can give it to us, but I 
 don't believe there are that many projects that will fail simply for 
 lack of that 20%.

When you're paying by the minute for supercomputer time, 20% is a big deal. When you're predicting the weather, a 20% slowdown means you're producing history rather than predictions. If Google could get 20% more speed out of their servers, they could cut the size of their server farm by 20%. That's hundreds of millions of dollars. When you're writing a game, numerics performance is what makes your game's graphics better than the competition. When you're writing code for embedded systems, faster code means you might be able to use a slower, cheaper processor, which can translate into millions of dollars in cost savings when you're shipping millions of units. I don't want D to be fundamentally locked out of these potential markets. If D compilers can produce fundamentally better code than C++, that's a big selling point for D into companies like Google. And when they use it for their critical server farm apps, they'll naturally tend to use it for much more.
Jun 23 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 Walter Bright wrote:
 Optimization often makes the difference between a successful project 
 and a failure. C++ has failed to supplant FORTRAN, because although 
 in every respect but one C++ is better, that one - optimization of 
 arrays - matters a whole lot. It drives people using C++ to use 
 inline assembler. They spend a lot of time on the issue. Various 
 proposals to fix it, like 'noalias' and 'restrict', consume vast 
 amounts of programmer time. And time is money.

FORTRAN is also helped by having a fairly standardized ABI that can be called easily from lots of languages, which C++ lacks. But C has that, and it has also failed to supplant FORTRAN for numeric code. But I think Sean's right. A lot of that is just that the language supports things like actual multi-dimensional arrays (by 'actual' I mean contiguous memory rather than pointers to pointers),

C has them too: int array[3][5]; is not an array of pointers to arrays.
 and mathematical operations on them right out of the box.

No, FORTRAN does not have array operations out of the box. It has no more mathematical operations than C does (in fact, it has fewer).

Hmm, maybe I'm thinking of Fortran 90 then. This doc seems to be saying that F90 has array ops http://www.ibiblio.org/pub/languages/fortran/ch1-2.html "Fortran 90 supports an array notation that allows operations on array sections, and using vector indices." And from that page, Fortran has an efficient built-in exponentiation operator, which sounds to me like one more operation than C has. But I guess C has all the += type things, which I don't think Fortran has. So I guess you could argue that C has more than F77 at least. Anyway, that page there pretty much sums up all the arguments for why to use Fortran for numerical computing, straight from a Fortran guy's mouth, so I can't really add much more than that.
 I guess what I'd like to say in summary is that I'm skeptical about 
 the claim that optimization "often" makes the difference between 
 success and failure.  "occasionally" I could believe.  Ill-advised 
 premature optimization has probably led to the demise of many more a 
 project than actual optimization problems in the end product.  We'll 
 all gladly take a free 20% speed improvement if the compiler can give 
 it to us, but I don't believe there are that many projects that will 
 fail simply for lack of that 20%.

When you're paying by the minute for supercomputer time, 20% is a big deal. When you're predicting the weather, a 20% slowdown means you're producing history rather than predictions. If Google could get 20% more speed out of their servers, they could cut the size of their server farm by 20%. That's hundreds of millions of dollars. When you're writing a game, numerics performance is what makes your game's graphics better than the competition. When you're writing code for embedded systems, faster code means you might be able to use a slower, cheaper processor, which can translate into millions of dollars in cost savings when you're shipping millions of units. I don't want D to be fundamentally locked out of these potential markets. If D compilers can produce fundamentally better code than C++, that's a big selling point for D into companies like Google. And when they use it for their critical server farm apps, they'll naturally tend to use it for much more.

That's all true but at the same time Java on embedded systems is pretty popular from what I understand, and Google is a big user of Python. I guess we're just quibbling over the definition of the "often" in your statement "Optimization often makes the difference between a successful project and a failure." Certainly it makes a difference sometimes. And those are the times you're going to want to use a compiled language like D. So I definitely agree that it makes sense for D to be second to none in generating optimized code and applaud your efforts in making the language amenable to such. --bb
Jun 23 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 No, FORTRAN does not have array operations out of the box. It has no 
 more mathematical operations than C does (in fact, it has fewer).

Hmm, maybe I'm thinking of Fortran 90 then. This doc seems to be saying that F90 has array ops http://www.ibiblio.org/pub/languages/fortran/ch1-2.html "Fortran 90 supports an array notation that allows operations on array sections, and using vector indices."

I quit using FORTRAN before F90 <g>.
 And from that page, Fortran has an efficient built-in exponentiation 
 operator, which sounds to me like one more operation than C has.  But I 
 guess C has all the += type things, which I don't think Fortran has.  So 
 I guess you could argue that C has more than F77 at least.

I'd forgotten about the exponentiation operator.
 That's all true but at the same time Java on embedded systems is pretty 
 popular from what I understand, and Google is a big user of Python.

Google extensively uses C++. A lot of people use Python, and are forced to mix in C++ to speed up the slow parts. It would be much simpler for them if they could use the same language for both.
Jun 26 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Walter Bright wrote:
 ... A lot of people use Python, and are forced
 to mix in C++ to speed up the slow parts. It would be much simpler for
 them if they could use the same language for both.

I'll have to disagree on this. I used Python for my third year comp.sci. group project, and it made things a hell of a lot easier. Most of the code was dealing with the GUI, with only a few math-heavy parts written by myself. Using Python for the mathy parts made them so much easier to debug it wasn't funny, and I've never seen anything that could touch Python for writing sane GUI code. In the end, I had to convert some of the slower code from Python to C, but that was basically a trivial exercise. Pyrex allows you to write "Python-like" code that compiles directly to C that makes writing the native module wrapping C code a non-issue. If I'd used just C++, the GUI coding would have driven me completely insane. If I'd been restricted to an interpreted language, our app would have been slow as molasses. By using Python, we got the best of both worlds :) In a wacky kind of way, one of the reasons I chose Python was *because* it could be easily optimised. True, it had to be done in a different language, but there was almost no effort required to do so, so I never had to fear hitting a performance ceiling. -- Daniel
Jun 26 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Daniel Keep wrote:
 
 Walter Bright wrote:
 ... A lot of people use Python, and are forced
 to mix in C++ to speed up the slow parts. It would be much simpler for
 them if they could use the same language for both.

I'll have to disagree on this. I used Python for my third year comp.sci. group project, and it made things a hell of a lot easier. Most of the code was dealing with the GUI, with only a few math-heavy parts written by myself. Using Python for the mathy parts made them so much easier to debug it wasn't funny, and I've never seen anything that could touch Python for writing sane GUI code. In the end, I had to convert some of the slower code from Python to C, but that was basically a trivial exercise. Pyrex allows you to write "Python-like" code that compiles directly to C that makes writing the native module wrapping C code a non-issue. If I'd used just C++, the GUI coding would have driven me completely insane. If I'd been restricted to an interpreted language, our app would have been slow as molasses. By using Python, we got the best of both worlds :)

Everyone knows that C++ sucks for writing GUIs, I hope to do much better with D.
 
 In a wacky kind of way, one of the reasons I chose Python was *because*
 it could be easily optimised.  True, it had to be done in a different
 language, but there was almost no effort required to do so, so I never
 had to fear hitting a performance ceiling.

But you did hit a performance ceiling. You had to convert the bottlenecks to C. Wouldn't it be nice not to have to do that? <g>
Jun 26 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Daniel Keep wrote:
 Walter Bright wrote:
 ... A lot of people use Python, and are forced
 to mix in C++ to speed up the slow parts. It would be much simpler for
 them if they could use the same language for both.

I'll have to disagree on this. I used Python for my third year comp.sci. group project, and it made things a hell of a lot easier. Most of the code was dealing with the GUI, with only a few math-heavy parts written by myself. Using Python for the mathy parts made them so much easier to debug it wasn't funny, and I've never seen anything that could touch Python for writing sane GUI code. In the end, I had to convert some of the slower code from Python to C, but that was basically a trivial exercise. Pyrex allows you to write "Python-like" code that compiles directly to C that makes writing the native module wrapping C code a non-issue. If I'd used just C++, the GUI coding would have driven me completely insane. If I'd been restricted to an interpreted language, our app would have been slow as molasses. By using Python, we got the best of both worlds :)

Everyone knows that C++ sucks for writing GUIs, I hope to do much better with D.
 In a wacky kind of way, one of the reasons I chose Python was *because*
 it could be easily optimised.  True, it had to be done in a different
 language, but there was almost no effort required to do so, so I never
 had to fear hitting a performance ceiling.

But you did hit a performance ceiling. You had to convert the bottlenecks to C. Wouldn't it be nice not to have to do that? <g>

Yes, but "dynamic everything" is a big part of what makes GUIs so easy to write in a language like Python. At the end of the day D is still going to be a statically typed language, so I don't think it'll ever be able to compete with a scripting language when it comes to the malleability of the code. For example in the program I'm writing in Python now I realized that it would be convenient to get some notification any time any member of a particular class got modified. In Python it's a snap. Just make a 10-line subclass that overrides __setattribute__ and use that instead of the original class and you're done. None of the rest of the code I had written had to change a whit. With D, the conversion would not be so easy, and in the end I'd still have to change client code here and there because foo.x+=5 doesn't work with D properties. That said, it's already a lot less painful writing GUI code in D than it is in C++ (GC, delegates, 'auto', and 'with' come to mind). I just don't think a statically typed, compiled language will ever really be able to compete with a scripting language on the GUI front. On the other hand, a dynamic scripting language will never be able to compete with D on the speed front. So you need both. The price I pay for the convenience of being able to override __setattribute__ occasionally is that *all* attribute accesses have to happen via string lookups *all the time*. I tried Pyrex for the first time last week because I was worried my core math routines would become a bottleneck. It's pretty impressive. I really didn't think it would be so easy to use. I don't even know if I have a bottleneck in the math yet, but Pyrex turned out to be so easy to use it was like "heck! why not?". I think the take home message is that people are quite willing to write/re-write performance critical parts of their code in a slightly restricted dialect if they can have unbounded expressiveness in the rest of their code. What does it mean for D? Well maybe For D maybe this could mean a third flavor of aggregate -- - struct: low-level value aggregate - class: static polymorphic aggregate - dclass: dynamic polymorphic aggregate (new!) The difference would be that dclass would do everything dynamically ('d' for "dynamic"). All member lookups via strings. All property accesses via special methods that can be overridden. In that way you could start coding with dclass, and as bottlenecks appear, you just have to transition the performance critical things from 'dclass' to 'class'. Another alternative is to allow some syntax for dynamic members in a class. But that seems messier to me somehow. For instance, from my experience using Qt I was constantly asking myself "should this be a slot? should that be a QProperty?" Why worry? Just make 'em all slots/properties. If you need some non-dynamic things in your dclass, make an inner class that's not dynamic. You mentioned games as a big area where every ounce of speed is important. But game developers are also some of the biggest consumers of scripting languages today. I'm not so sure game developers would say they'd like to use the same language for everything, even if they could. For big system stuff, static typing can really eliminate a ton of typical errors (little typos and thinkos especially). For low-level rendering code it's critical. But for designing the content of the game, dynamic tweakability and flexibility is more important, and forcing everything to be declared all the time can be counter-productive (especially when we're talking about artists doing the coding). I was kinda surprised by the comment that speed was so important, because from the buzz I've been hearing more and more over recent years, the picture is quite the opposite. Computers are now faster than they need to be for most jobs. Sure there are exceptions, but they are just that, exceptions. If you want to play to the numbers, the arena to be in is not performance critical code. Java probably wouldn't be #1 on Tiobe if performance were the most important thing. In fact, among the top 10 on Tiobe right now, interpreted languages have a combined score of 56%, with compiled languages (C/C++) only pulling in 27%. --bb
Jun 26 2007
next sibling parent reply Brad Anderson <brad dsource.org> writes:
Bill Baxter wrote:
 What does it mean for D?  Well maybe For D maybe this could mean a third
 flavor of aggregate --
 - struct: low-level value aggregate
 - class: static polymorphic aggregate
 - dclass: dynamic polymorphic aggregate (new!)
 
 The difference would be that dclass would do everything dynamically ('d'
 for "dynamic").  All member lookups via strings.  All property accesses
 via special methods that can be overridden.  In that way you could start
 coding with dclass, and as bottlenecks appear, you just have to
 transition the performance critical things from 'dclass' to 'class'.
 Another alternative is to allow some syntax for dynamic members in a
 class.  But that seems messier to me somehow.  For instance, from my
 experience using Qt I was constantly asking myself "should this be a
 slot? should that be a QProperty?"  Why worry?  Just make 'em all
 slots/properties.  If you need some non-dynamic things in your dclass,
 make an inner class that's not dynamic.

Hmm. It's rather a shame this didn't get any replies. I like the idea. I wonder if MiniD helps out at all... BA
Jun 29 2007
parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Fri, 29 Jun 2007 19:26:41 +0300, Brad Anderson <brad dsource.org> wrote:

 Bill Baxter wrote:
 What does it mean for D?  Well maybe For D maybe this could mean a third
 flavor of aggregate --
 - struct: low-level value aggregate
 - class: static polymorphic aggregate
 - dclass: dynamic polymorphic aggregate (new!)

 The difference would be that dclass would do everything dynamically ('d'
 for "dynamic").  All member lookups via strings.  All property accesses
 via special methods that can be overridden.  In that way you could start
 coding with dclass, and as bottlenecks appear, you just have to
 transition the performance critical things from 'dclass' to 'class'.
 Another alternative is to allow some syntax for dynamic members in a
 class.  But that seems messier to me somehow.  For instance, from my
 experience using Qt I was constantly asking myself "should this be a
 slot? should that be a QProperty?"  Why worry?  Just make 'em all
 slots/properties.  If you need some non-dynamic things in your dclass,
 make an inner class that's not dynamic.

Hmm. It's rather a shame this didn't get any replies. I like the idea. I wonder if MiniD helps out at all... BA

Yep, it's a shame (that there were no replies)... but maybe all just silently agreed? ;)
Jun 29 2007
prev sibling parent Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Bill Baxter schrieb am 2007-06-27:

[...]

 Yes, but "dynamic everything" is a big part of what makes GUIs so easy 
 to write in a language like Python.  At the end of the day D is still 
 going to be a statically typed language, so I don't think it'll ever be 
 able to compete with a scripting language when it comes to the 
 malleability of the code.

 For example in the program I'm writing in Python now I realized that it 
 would be convenient to get some notification any time any member of a 
 particular class got modified.  In Python it's a snap.  Just make a 
 10-line subclass that overrides __setattribute__ and use that instead of 
 the original class and you're done.  None of the rest of the code I had 
 written had to change a whit.  With D, the conversion would not be so 
 easy, and in the end I'd still have to change client code here and there 
 because foo.x+=5 doesn't work with D properties.

 That said, it's already a lot less painful writing GUI code in D than it 
 is in C++ (GC, delegates, 'auto', and 'with' come to mind).  I just 
 don't think a statically typed, compiled language will ever really be 
 able to compete with a scripting language on the GUI front.

 On the other hand, a dynamic scripting language will never be able to 
 compete with D on the speed front.  So you need both.  The price I pay 
 for the convenience of being able to override __setattribute__ 
 occasionally is that *all* attribute accesses have to happen via string 
 lookups *all the time*.

 I tried Pyrex for the first time last week because I was worried my core 
 math routines would become a bottleneck.  It's pretty impressive.  I 
 really didn't think it would be so easy to use.  I don't even know if I 
 have a bottleneck in the math yet, but Pyrex turned out to be so easy to 
 use it was like "heck! why not?".

 I think the take home message is that people are quite willing to 
 write/re-write performance critical parts of their code in a slightly 
 restricted dialect if they can have unbounded expressiveness in the rest 
 of their code.

 What does it mean for D?  Well maybe For D maybe this could mean a third 
 flavor of aggregate --
 - struct: low-level value aggregate
 - class: static polymorphic aggregate
 - dclass: dynamic polymorphic aggregate (new!)

 The difference would be that dclass would do everything dynamically ('d' 
 for "dynamic").  All member lookups via strings.  All property accesses 
 via special methods that can be overridden.  In that way you could start 
 coding with dclass, and as bottlenecks appear, you just have to 
 transition the performance critical things from 'dclass' to 'class'. 
 Another alternative is to allow some syntax for dynamic members in a 
 class.  But that seems messier to me somehow.  For instance, from my 
 experience using Qt I was constantly asking myself "should this be a 
 slot? should that be a QProperty?"  Why worry?  Just make 'em all 
 slots/properties.  If you need some non-dynamic things in your dclass, 
 make an inner class that's not dynamic.

'dynamic light' is currently implemented in Flectioned (http://flectioned.kuehne.cn) as a prove of concept code for DMD. # import cn.kuehne.flectionedCall : call; # import std.stdio : writefln; # # class Test{ # void bar(){ # writefln("Test.bar"); # } # void bar(int a, char[] b){ # writefln("Test.bar(%s, \"%s\")", a, b); # } # } # # void main(){ # auto t = new Test(); # # call(t, "bar"); # call(t, "bar", 6, "Erwin"); # } The main stumbling block is that DMD and GDC use different calling conventions. I could implement the 'dclass' concept but am unsure how usefull it is. Thomas -----BEGIN PGP SIGNATURE----- iQIVAwUBRoVZbrZlboUnBhRKAQIjbBAAr+DBeltIb++qKzFwZmZrfODnozZMuURD FrVgHTUmlEv/cDZHwdTZacypm9aU6ZnqnlG1OBNA8cluFHuKuMA/fQ11uVzebGQb ceh2spfuplwlEG+zlHU7sqtL//gIK/cyFS84Fx2+DXuPn53QhEl4IshpmI8Hi47o eLcKSndyo6b5mOjzNlyzdGRfYRDbWXf1oIHgTTP1j0jkhUOc5oP59iN0yHDQphqq IuSQIV/HijVNbpyXz/cOAQ3PLNz5URPpj33kn5RUFjMZC5Z5YJ0MWkT2U39LGUDp x7uegAxWfDy+1WjeLNQyZSSZ6QdFL9llynBH4284Vg+nL3BFW8oYYA3HjrV5Zb7b Eoz2IBGX0fa8Phmyu9sts6J04wHwZw8YjudyhPnFSelQX9wDn82XLD8iVHCOdb30 M32TrFXm5TXiavX3zwGEXcZEFov09GLc3adBm096NTJ301YzPD2KFhVvDfk7exC3 XmBdOYfuxgcS92NCF+ETS5Mf01U2iwIvRmfXr+l2W7WSHASM5nEb6tcEU9Vi+fb4 YSqiS2mZpX3n0lQRTWSwb2Zwb5djeXIDtifc/Q5to7bxSyPOqLnTdS7rJBgUtkti Fig1bdfvywX7tznWvxMZkuCa4MO527x2xgjc3GzcqeYsJu9wepwuAl1jP7OxXEkV 0XckFCgKPgc= =kGY1 -----END PGP SIGNATURE-----
Jun 29 2007
prev sibling next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Walter Bright wrote:
 Daniel Keep wrote:
 In a wacky kind of way, one of the reasons I chose Python was *because*
 it could be easily optimised.  True, it had to be done in a different
 language, but there was almost no effort required to do so, so I never
 had to fear hitting a performance ceiling.

But you did hit a performance ceiling. You had to convert the bottlenecks to C. Wouldn't it be nice not to have to do that? <g>

I rather like to think you can convert those bottlenecks to D. :-) -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Jun 26 2007
parent Dave <Dave_member pathlink.com> writes:
Kirk McDonald wrote:
 Walter Bright wrote:
 Daniel Keep wrote:
 In a wacky kind of way, one of the reasons I chose Python was *because*
 it could be easily optimised.  True, it had to be done in a different
 language, but there was almost no effort required to do so, so I never
 had to fear hitting a performance ceiling.

But you did hit a performance ceiling. You had to convert the bottlenecks to C. Wouldn't it be nice not to have to do that? <g>

I rather like to think you can convert those bottlenecks to D. :-)

I'd rather just do it all in D and leave Python and C on the shelf <g>
Jun 26 2007
prev sibling parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Walter Bright wrote:

 If I'd used just C++, the GUI coding would have driven me completely
 insane.  If I'd been restricted to an interpreted language, our app
 would have been slow as molasses.  By using Python, we got the best of
 both worlds :)

Everyone knows that C++ sucks for writing GUIs, I hope to do much better with D.

On the other hand, Objective-C is very nice for writing GUIs with and is what I am using while the D language and the D libraries grows up. It also allows for interfacing with C (reads C headers) and with C++ (understands C++ objects), so I don't have to wrap or port those over. The best part is still the IDE (Project Builder/Xcode) and the RAD tool (Interface Builder), but the objc language blends very well with these. Much nicer than how it was in legacy environments like Win32 or MacOS, and less writing than what I get with lowlevel tools like C and GTK+... If only it was more portable and open source, it would be the solution. Meanwhile, I'll have to make do with D (and Code::Blocks and wxWidgets). But when the language specification and standard library issues with D are sorted, some GUI APIs are looking good: http://clarographics.org/ --anders
Jun 27 2007
prev sibling parent Charlie <charlie.fats gmail.com> writes:
What python GUI did you use ?

Charlie

Daniel Keep wrote:
 
 Walter Bright wrote:
 ... A lot of people use Python, and are forced
 to mix in C++ to speed up the slow parts. It would be much simpler for
 them if they could use the same language for both.

I'll have to disagree on this. I used Python for my third year comp.sci. group project, and it made things a hell of a lot easier. Most of the code was dealing with the GUI, with only a few math-heavy parts written by myself. Using Python for the mathy parts made them so much easier to debug it wasn't funny, and I've never seen anything that could touch Python for writing sane GUI code. In the end, I had to convert some of the slower code from Python to C, but that was basically a trivial exercise. Pyrex allows you to write "Python-like" code that compiles directly to C that makes writing the native module wrapping C code a non-issue. If I'd used just C++, the GUI coding would have driven me completely insane. If I'd been restricted to an interpreted language, our app would have been slow as molasses. By using Python, we got the best of both worlds :) In a wacky kind of way, one of the reasons I chose Python was *because* it could be easily optimised. True, it had to be done in a different language, but there was almost no effort required to do so, so I never had to fear hitting a performance ceiling. -- Daniel

Jun 30 2007
prev sibling parent reply BCS <ao pathlink.com> writes:
Reply to Bill,

 I guess we're just quibbling over the definition of the "often" in
 your statement "Optimization often makes the difference between a
 successful project and a failure."

That reminds me of a thought I had a while ago: compilers are interesting beasts because they are a program that deals with programs. Not only that, but a compiler must be written to handle anything that is allowable, not just stuff that is a good idea (as opposed to the compiler that should be written only with code that is a good idea). This starts looking like the point that if a compiler is going to be used by some market where a few people need something, then the compiler needs it even if most people in the market don't need it. I'm not sure if this is even remarkable but I though it was kind of interesting.
Jun 26 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
BCS wrote:
 Reply to Bill,
 
 I guess we're just quibbling over the definition of the "often" in
 your statement "Optimization often makes the difference between a
 successful project and a failure."

That reminds me of a thought I had a while ago: compilers are interesting beasts because they are a program that deals with programs. Not only that, but a compiler must be written to handle anything that is allowable, not just stuff that is a good idea (as opposed to the compiler that should be written only with code that is a good idea). This starts looking like the point that if a compiler is going to be used by some market where a few people need something, then the compiler needs it even if most people in the market don't need it. I'm not sure if this is even remarkable but I though it was kind of interesting.

It's the same thing with library API design, too. A good library needs to consider all possible use cases, even the ones that are wacky. Probably same thing with all code. Haven't you ever been at that point where you find yourself writing way too much code to handle some case that probably won't even come up? It's just a result of not knowing what your input will be in advance. --bb
Jun 26 2007
prev sibling parent Dave <Dave_member pathlink.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 Matter of opinion, I suppose.  The C++ design was immediately clear 
 to me, though it obviously wasn't for others.  I grant that the 
 aliasing problem can be confusing, but I feel that it is a 
 peripheral issue.

I don't think it is a peripheral issue. It completely screws up optimization, is useless for threading support, and has spawned endless angst about why C++ code is slower than Fortran code.

As a programmer, I consider the optimization problem to be a non-issue. Optimization is just magic that happens to make my program run faster.

Optimization often makes the difference between a successful project and a failure. C++ has failed to supplant FORTRAN, because although in every respect but one C++ is better, that one - optimization of arrays - matters a whole lot. It drives people using C++ to use inline assembler. They spend a lot of time on the issue. Various proposals to fix it, like 'noalias' and 'restrict', consume vast amounts of programmer time. And time is money.

FORTRAN is also helped by having a fairly standardized ABI that can be called easily from lots of languages, which C++ lacks. But C has that, and it has also failed to supplant FORTRAN for numeric code. But I think Sean's right. A lot of that is just that the language supports things like actual multi-dimensional arrays (by 'actual' I mean contiguous memory rather than pointers to pointers), and mathematical operations on them right out of the box. Telling a numerics person that C/C++ will give them much better IO and GUI support, but take them a

But, what if you could get all of that in a language, plus the array performance of Fortran with the 'safety' of things like invariant... To Walter's point, if a language can handle arrays well (great performance), it makes a lot more sense to support things like MD arrays properly in the language, and it will make it a lot easier to justify numerical libraries written in D as well. Plus a lot of applications that never even touch the FP stack could benefit from great array performance. D has a dilemma - because of its C lineage and semantics, it can't just ignore the aliasing issue (like Fortran does), but in order to be an improvement over C and C++ in all important aspects, it should address the issue somehow. IMHO the most logical way (since pointer/reference and data-flow analysis probably can't eliminate even a majority of the aliasing issues) is to improve on C++'s idea of 'const' and hand the semantic control over to the programmer in a way the compiler can rely on.
 step back in terms of core numerics is like trying to sell a hunter a 
 fancy new gun with a fantastic scope that will let you pinpoint a mouse 
 at 500 yards but -- oh, I should mention it only shoots bb's.

 On the other hand, I suspect there's lots of code that's written in 
 FORTRAN supposedly for performance reasons that doesn't really need to 
 be.  Just as there's lots of code written in C++ that would perform fine 
 in a scripting language.  But people will still swear up and down that 
 {whatever} is the only language fast enough.  A lot of numerics folks do 
 realize this, however.  They just go from Fortran straight to Matlab, 
 and skip the other compiled languages altogether.
 
 I guess what I'd like to say in summary is that I'm skeptical about the 
 claim that optimization "often" makes the difference between success and 
 failure.  "occasionally" I could believe.  Ill-advised premature 
 optimization has probably led to the demise of many more a project than 
 actual optimization problems in the end product.  We'll all gladly take 
 a free 20% speed improvement if the compiler can give it to us, but I 
 don't believe there are that many projects that will fail simply for 
 lack of that 20%.
 

<soapbox> In my experience this is not the case - I've worked on projects where "tuning" took up a significant (say ~20%) of the total cost even though the software nominally met the requirements, because end-users were not happy with performance. These projects probably would not have strictly "failed" because they did the job (slowly), but they would still have been "failures" in the eyes of the end-users to a significant degree. I'm sure we've all been there to one degree or another. Some of these particular issues were taken care of by things like applying databases indexes (that are not directly related to what we're talking about here) but some of the issues involved exactly what Walter is talking about - how fast the language the application was written in could handle arrays of data. In one for example, we had to settle on single precision FP, even though double precision met the requirements better, in order to mitigate performance complaints [the perf. issues weren't all strictly related to double the data size, but also because the compiler handled singles better]. In this case the users wanted the speed rather than the exact sales commissions out to the penny every month, but of course the optimal solution would have been both, which a better compiler could have provided. It takes a little extrapolation, but in the end, if the algorithms are correct, too often it can all boil down to either the compiler emitting good code, a programmer having to emit it via an assembler, or unhappy users. In another project, the lead decided to do a general "usability" type user-group survey before and after "tuning" and there was a big difference that could only be explained by the improved performance. The users didn't really recognize that there was a perf. problem until they saw that it _could_ be faster, and when they saw the difference, the application became that much more quickly accepted. Users care about this stuff, and I've always been dismayed when it is thought of only at the end of a project. Also, one of the advantages of the new design is that most of it doesn't _have_ to be used... If you don't need to make an argument 'invariant' just for performance reasons, you don't have to use it. Part of the problem with const littering code in C++ I think is because programmers often use it to be "const correct" when otherwise it might not make a lot of sense. The more a language and compiler can do to make performance optimal when it's needed, and offset having to hack together shortcuts, workarounds or call into another language API, the better IMHO. </soapbox>
 --bb

Jun 23 2007
prev sibling parent James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Matter of opinion, I suppose.  The C++ design was immediately clear to
 me, though it obviously wasn't for others.  I grant that the aliasing
 problem can be confusing, but I feel that it is a peripheral issue.

I don't think it is a peripheral issue.

No, you clearly don't. But to many of us, it is a peripheral issue, and the fact that you make it so central seems odd.
 It completely screws up optimization,

It has some detrimental effects on some optimizations. It's a huge aid to good design.
 is useless for threading support, and has spawned endless
 angst about why C++ code is slower than Fortran code.

Which will continue, even though C++ compilers can often now beat Fortran compilers for speed. (When used with idiomatic C++ styles, rather than lower-level, C style extensive use of pointers.)
 Or am I missing something?

Yes, you've missed the distinction between a readonly view and a truly immutable value.

I doubt that.
 That's not surprising, since my experience is that very
 few who are used to C++ const see the difference, and it took me a while
 to figure it out.

It's pretty trivial. Which isn't to say that it's of no use, but it's not complicated. Then again, neither is the C++ model of const, so maybe it depends on perspective. -- James
Jun 22 2007
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
I think the most important thing regarding const/final/invariant is simply to
remove redundancy. If every combination/keyword/construct means only one thing
when applied to a particular declaration,it drastically reduces complexity for
the user. For example, having "final int x = 5", "const int x = 5" and
"invariant int x = 5" all mean the same thing is difficult, because users don't
expect that. Similarly, having:

struct Foo
{
int x;
int* y;
}

const Foo bar;

, in which bar.x can freely change, but *bar.y can't makes structs seem far
different than classes - you can no longer replace one with the other (besides
having to put in/remove a "new"), if it's const.

I think having three keywords is probably a good thing, as long as whenever
they're applied they mean different things. Overlapping meaning and unexpected
behavior is one of the big problems with C++ - what's "static" mean? About 5
things?

On that note, I think "invariant" should be changed to "immutable". At the cost
of a new keyword and a new token, we get both backwards compatibility with
class invariants and disambiguation.

As long as I'm throwing my cent out there, dare I mention
in-parameter-passing-by-default again?

All that said, I think this is a far cry better than the C++ version (which I
never even bothered to understand), and I'm starting to see the advantages of
using const (why was it left out in C#? The Java devs mentioned at some point
that if they had the chance to redo it without breaking backwards
compatibility, they would have). So, thanks, Walter, for the great update! D is
definitely the language to beat for anything bigger than a Perl script.

All the best,
Fraser
Jun 22 2007
prev sibling parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 In C++, sometimes const means invariant, and sometimes it means
 readonly view. I've found even C++ experts who don't know how it works.


It isn't.

It is to many people; you seem to have more experience with people who have trouble understanding it, maybe. I don't know why else it is that I find your diatribes about the terrible problems of C++'s const to be so strange, given that C++'s const works widely and well.
 I run into people all the time who are amazed to discover that
 const references can change. 

In C++ code without undefined behavior, references cannot change. Maybe you're referring to the fact that some people don't learn that const means read-only in C++, rather than the "invariant" notion that you've introduced for D 2.0. That seems not to be hard for competent programmers to learn, compared to many of the other things they have to learn. (I'm concerned at using two synonyms to mean different things, but it's almost certainly too late to change that now.)
 Few understand when const is invariant and
 when it isn't. I've never even seen anyone mention the problem where the
 non-transitive const destroys any hope of having FP like capabilities in
 C++.

And yet C++ *has* "FP-like" capabilities, depending on how we define our terms. Const works well in the hands of many, many C++ programmers. Bad programmers will be bad programmers with D too. Value, rather than reference, semantics are a great aid in providing FP capabilities in a manner that scales to parallel systems. I'm glad to see that D is getting closer to C++ in providing more capable structs.
 I personally find the use of three keywords to represent three
 overlapping facets of const behavior to be very confusing, and am
 concerned about trying to explain it to novice programmers.  With
 three keywords, there are six possible combinations:

 final
 const invariant
 final const
 final invariant
 const invariant
 final const invariant

Probably the thing to do is simply outlaw using more than one.

That would simplify things, I think. -- James
Jun 22 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
James Dennett wrote:
 Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 In C++, sometimes const means invariant, and sometimes it means
 readonly view. I've found even C++ experts who don't know how it works.



It is to many people; you seem to have more experience with people who have trouble understanding it, maybe.

Many of these people are well known C++ experts (no, I won't name names <g>). It's a lot more complicated than it appears.
 I don't
 know why else it is that I find your diatribes about the
 terrible problems of C++'s const to be so strange, given
 that C++'s const works widely and well.

Probably because not many people realize that two well known shortcomings of C++, the alias optimization problem and the parallelization problem, are related to inadequacies of const.
 Maybe you're referring to the fact that some people don't
 learn that const means read-only in C++, rather than the
 "invariant" notion that you've introduced for D 2.0.

Yes. But sometimes C++ const means invariant, sometimes it means final, and sometimes it means readonlyview.
 That
 seems not to be hard for competent programmers to learn,
 compared to many of the other things they have to learn.

C++ programmers with many years experience often are surprised by this. C++ is a hard language to learn, and takes several years to master.
 (I'm concerned at using two synonyms to mean different
 things, but it's almost certainly too late to change
 that now.)

It's better than C++ using one word (const) to mean 3 different things, which is the root of why people are confused with what C++ const means.
 Few understand when const is invariant and
 when it isn't. I've never even seen anyone mention the problem where the
 non-transitive const destroys any hope of having FP like capabilities in
 C++.

And yet C++ *has* "FP-like" capabilities, depending on how we define our terms.

Not really. C++ has been pretty resistant to attempts to automatically parallelize code, and that's because of the weakness of const.
 Const works well in the hands of many, many C++ programmers.

For its limited application, yes. But rethinking the role of const can open the door to a lot more.
 Value, rather than reference, semantics are a great aid in
 providing FP capabilities in a manner that scales to parallel
 systems.  I'm glad to see that D is getting closer to C++ in
 providing more capable structs.

Invariant can provide value semantics with the performance of references. After all, you can't really pass a dictionary by value. The other crucial bit is transitivity of const/invariant. In C++, you can say with const that you won't change the root of the dictionary, but you simply cannot specify that you won't change any nodes of it. Without that, you don't have FP. It's why people who want to parallelize C++ have to write extensions to the language to do it. Writing extensions to C++, then implementing it as a translator that outputs C++, is a very difficult and expensive thing to do. People are not going to do it unless they can expect a very large return on their investment. This means they are trying to address a serious shortcoming of the language. Based on this, I don't agree that C++ const is good enough for the future. It's a 20 year old design, and we can learn from it and do better.
Jun 23 2007
parent reply BLS <nanali nospam-wanadoo.fr> writes:
I am afraid you will not like this idea, but not afraid enough. <g>

Why not using a single keyword "const_" adding a number 1, 2, 3 to 
represent :
invariant, final, readonlyview.
the higher the number the higher (the stronger) the const.
means : const_3 == invariant;
At least a mental help, IMO; somehow borrowed from Modula 2 processes.

Bjoern
Jun 23 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>
 
 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 processes.

You're right, I don't like the idea <g>.
Jun 23 2007
parent reply OF <nospam nospamington.com> writes:
Walter Bright Wrote:

 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>
 
 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 processes.

You're right, I don't like the idea <g>.

I'm curious. Was 'readonly' or 'readonlyview' considered as an alternative for 'const'? I kind of like the sound of 'readonly', and it doesn't make you think of C++ 'const'... but maybe that's bad.
Jun 23 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
OF wrote:
 Walter Bright Wrote:
 
 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>

 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 processes.


I'm curious. Was 'readonly'

readonly is a synonym for const, so no improvement there.
 or 'readonlyview' considered as an alternative for 'const'? I kind of like the
sound of 'readonly', and it doesn't make you think of C++ 'const'... but maybe
that's bad.

readonlyview is too long.
Jun 25 2007
next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Walter Bright wrote:
 OF wrote:
 Walter Bright Wrote:

 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>

 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 processes.


I'm curious. Was 'readonly'

readonly is a synonym for const, so no improvement there.
 or 'readonlyview' considered as an alternative for 'const'? I kind of 
 like the sound of 'readonly', and it doesn't make you think of C++ 
 'const'... but maybe that's bad.

readonlyview is too long.

But its only 3 characters longer than 'invariant'... ;) Still, it is a little unwieldy aside from that. I'm not too keen on keywords that are, linguistically, more than one word. (I know, that sounds silly from one of the same people who begged for 'foreach' ages ago, but that's a keyword with enough history on it to be easily recognizable. I might have preferred 'each' if it wasn't a common variable name.) -- Chris Nicholson-Sauls
Jun 25 2007
prev sibling next sibling parent Jeff Nowakowski <jeff dilacero.org> writes:
Walter Bright wrote:
 readonly is a synonym for const, so no improvement there.

I think readonly is a much better mnemonic than const, especially considering that const is often explained in terms of being a read-only view. Yes, invariant can be described in terms of read-only too, but const vs invariant look *exactly* the same to me, whereas readonly doesn't. -Jeff
Jun 26 2007
prev sibling next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright skrev:
 OF wrote:
 Walter Bright Wrote:

 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>

 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 processes.


I'm curious. Was 'readonly'

readonly is a synonym for const, so no improvement there.

Read-only isn't synonymous with constant. The etymology of constant is from latin constare, which means "to stand firm". The most common meaning of 'constant' is 'unchanging'. It also has a well defined mathemathical meaning that is quite far from read-only. It is perfectly valid to talk about read-only access, read-only registers, read-only properties, read-only pins, etc without any of them being considered constant. In all those cases, D 2.0's current 'const' fits perfectly. Some examples: A A/D unit on a device may be read by reading a io mapped memory register. The register is certainty read-only, but no one would call it constant. The Instruction Counter on may microprocessors can be read but may seldom be written to directly. It is certainly read-only, but definitely not constant. You may only be allowed a read-only view of a file. Other processes can modify the file so it can't be considered constant (unchanging).
 readonlyview is too long.

Agreed. /Oskar
Jun 26 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Oskar Linde wrote:
 Walter Bright skrev:
 OF wrote:
 Walter Bright Wrote:

 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>

 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 processes.


I'm curious. Was 'readonly'

readonly is a synonym for const, so no improvement there.

Read-only isn't synonymous with constant.

Consider ROM, i.e. "Read Only Memory".
Jun 26 2007
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 26 Jun 2007 11:46:30 -0700, Walter Bright wrote:

 Oskar Linde wrote:
 Walter Bright skrev:
 OF wrote:
 Walter Bright Wrote:

 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>

 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 processes.


I'm curious. Was 'readonly'

readonly is a synonym for const, so no improvement there.

Read-only isn't synonymous with constant.

Consider ROM, i.e. "Read Only Memory".

Which is obviously misnamed. It should be more like Write-Once memory <G> If it were truely *always* read-only then nothing would ever be written to it, thus it is not constant as its state has changed at least once. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Jun 26 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 Which is obviously misnamed. It should be more like Write-Once memory <G>
 If it were truely *always* read-only then nothing would ever be written to
 it, thus it is not constant as its state has changed at least once.

Technically, ROMs are actually manufactured that way, there never was a write to them. You're thinking of PROMs ("programmable read only memory"), and EPROMs ("Erasable Programmable Read Only Memory") and EEPROMs ("Electrically Erasable ...").
Jun 26 2007
prev sibling next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright skrev:
 Oskar Linde wrote:
 Walter Bright skrev:
 OF wrote:
 Walter Bright Wrote:

 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>

 Why not using a single keyword "const_" adding a number 1, 2, 3 to 
 represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 
 processes.


I'm curious. Was 'readonly'

readonly is a synonym for const, so no improvement there.

Read-only isn't synonymous with constant.

Consider ROM, i.e. "Read Only Memory".

Yes, there are cases where the meaning of read-only and constant overlap. You could even argue that the overlapping meanings are large enough to make the words synonymous, but that is irrelevant to the discussion. What is relevant is which word does best describe the properties the D2.0 'const' has? IMHO, read-only wins by a large margin. Read-only describes everything 'const' is, while the generally accepted meaning of constant is a poor fit. This has been irking me for quite some time, and I believe it is the reason the final-const-invariant thing feels so awkward (to me). The design has been carefully considered and is quite likely the best possible given the requirements. However, the use of "const" to mean read-only sticks out like a turd in a punch bowl (pardon the expression). People argue that of the different meanings of "const" in C++ (that you like to point out), the non-constant meaning is the worst and most confusing. Still, the only meaning for "const" that D inherit from C++ is that one. <parenthesis> I've never heard anyone complain about "const" as a "storage class", like in: const double e = 2.7182818284590452354; const double pi = 3.14159265358979323846; <parenthesis> I believe *many* people would be *much* less confused and have a *much* easier time acceping and grasping the D const behavior if better keywords were used. "What? Const means it can change???" So I plead: Please reconsider the keywords. The implications of not doing that are severe. The publics acceptance of D is at stake. What if you could say, "Look: We fixed C++'s const. Const now really means constant -- not could-actually-change and you will be damned if you assume anything else". This is a hypothetical scenario of changing final-const-invariant -> final-readonly-const. There could possibly be even better choices of keywords. Apologies for being so harsh (and hyperbolic), but I feel this is (as strange as it may sound) a quite important issue. /Oskar
Jun 26 2007
next sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Oskar Linde wrote:

 Apologies for being so harsh (and hyperbolic), but I feel this is (as
 strange as it may sound) a quite important issue.

No reason to apologize, the current implementation is far from perfect, even ignoring the plethora of keywords involved. Let me try to list some that I have been able to pick up, even in my ignorance: * constant don't mean read only, so even though const can be considered to mean constrained, it don't, and should thus not be used to mean read only. Oskar's suggestion readonly is the best yet. Add to the damage of the new const that it in 1.0 always mean immutable. Looking to C++/listening to C++ users in this respect may be the worst mistake in D's history yet. * invariant as the keyword for immutable/constant would have been ok, if it hadn't been for the fact that it is used in another context, where empty () had to be added to separate from the immutable meaning. The easy fix is of course to use const to mean immutable/constant, since readonly should be used to mean read only. * const-by-default has been completely ignored/put down, even if _everyone_ giving their opinion in the newsgroup said "give it a try" Then there are some things that at least appears to be inconsistent: * why are D string literals invariant(char)[] whereas C string literals are invariant(char*) ? This probably ties in with why invariant(char*) and invariant(char[]) behaves differently in the first place. I'm sure there are other questions to ask, or that have already been asked. Walter, I think it's time to pick up your promise again, to have D go wherever the community wants it to go. This don't currently seem to be happening. It was mentioned on IRC that D 2.000 don't seem to have got much praise yet, and as far as I can see, there are good reasons for this. D is having it's biggest change yet, and the community in general seems to have more or less qualms with the current implementation. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Jun 27 2007
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
  * constant don't mean read only, so even though const can be considered to
 mean constrained, it don't, and should thus not be used to mean read only.
 Oskar's suggestion readonly is the best yet. Add to the damage of the new
 const that it in 1.0 always mean immutable. Looking to C++/listening to C++
 users in this respect may be the worst mistake in D's history yet.
  * invariant as the keyword for immutable/constant would have been ok, if it
 hadn't been for the fact that it is used in another context, where empty ()
 had to be added to separate from the immutable meaning. The easy fix is of
 course to use const to mean immutable/constant, since readonly should be
 used to mean read only.
  * const-by-default has been completely ignored/put down, even if _everyone_
 giving their opinion in the newsgroup said "give it a try"
 

Up to here, I totally agree with you.
 Then there are some things that at least appears to be inconsistent:
 
  * why are D string literals invariant(char)[] whereas C string literals are
 invariant(char*) ? This probably ties in with why invariant(char*) and
 invariant(char[]) behaves differently in the first place.
 

Not sure about the C string thing, but now that I understand the difference, I think it's important that invariant(char[]) and invariant(char)[], and specifying it like that seems a hell of a lot easier than some C++ism along the lines of "invariant char invariant[]".
 I'm sure there are other questions to ask, or that have already been asked.
 
 Walter, I think it's time to pick up your promise again, to have D go
 wherever the community wants it to go. This don't currently seem to be
 happening. It was mentioned on IRC that D 2.000 don't seem to have got much
 praise yet, and as far as I can see, there are good reasons for this. D is
 having it's biggest change yet, and the community in general seems to have
 more or less qualms with the current implementation. 

I think the implementation is all right, I just think the syntax needs to be reconsidered. Personally, I think the whole const-final-invariant thing is quite a bit nicer than any other implementation I've seen: being able to mark an object as invariant makes writing threadsafe code a lot easier. If there was a way to mark a class/struct as "invariant after being constructed," then it'd be unbeatable.
Jun 27 2007
parent Sean Kelly <sean f4.ca> writes:
Robert Fraser wrote:
 
 I think the implementation is all right, I just think the syntax needs to be
reconsidered. Personally, I think the whole const-final-invariant thing is
quite a bit nicer than any other implementation I've seen: being able to mark
an object as invariant makes writing threadsafe code a lot easier. If there was
a way to mark a class/struct as "invariant after being constructed," then it'd
be unbeatable.

Frankly, I expect to be casting stuff to invariant constantly, based on the current design. Sean
Jun 27 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Oskar Linde wrote:
 Walter Bright skrev:
 Oskar Linde wrote:
 Walter Bright skrev:
 OF wrote:
 Walter Bright Wrote:

 BLS wrote:
 I am afraid you will not like this idea, but not afraid enough. <g>

 Why not using a single keyword "const_" adding a number 1, 2, 3 
 to represent :
 invariant, final, readonlyview.
 the higher the number the higher (the stronger) the const.
 means : const_3 == invariant;
 At least a mental help, IMO; somehow borrowed from Modula 2 
 processes.


I'm curious. Was 'readonly'

readonly is a synonym for const, so no improvement there.

Read-only isn't synonymous with constant.

Consider ROM, i.e. "Read Only Memory".

Yes, there are cases where the meaning of read-only and constant overlap. You could even argue that the overlapping meanings are large enough to make the words synonymous, but that is irrelevant to the discussion. What is relevant is which word does best describe the properties the D2.0 'const' has? IMHO, read-only wins by a large margin. Read-only describes everything 'const' is, while the generally accepted meaning of constant is a poor fit. This has been irking me for quite some time, and I believe it is the reason the final-const-invariant thing feels so awkward (to me). The design has been carefully considered and is quite likely the best possible given the requirements. However, the use of "const" to mean read-only sticks out like a turd in a punch bowl (pardon the expression). People argue that of the different meanings of "const" in C++ (that you like to point out), the non-constant meaning is the worst and most confusing. Still, the only meaning for "const" that D inherit from C++ is that one. <parenthesis> I've never heard anyone complain about "const" as a "storage class", like in: const double e = 2.7182818284590452354; const double pi = 3.14159265358979323846; <parenthesis> I believe *many* people would be *much* less confused and have a *much* easier time acceping and grasping the D const behavior if better keywords were used. "What? Const means it can change???" So I plead: Please reconsider the keywords. The implications of not doing that are severe. The publics acceptance of D is at stake. What if you could say, "Look: We fixed C++'s const. Const now really means constant -- not could-actually-change and you will be damned if you assume anything else". This is a hypothetical scenario of changing final-const-invariant -> final-readonly-const. There could possibly be even better choices of keywords. Apologies for being so harsh (and hyperbolic), but I feel this is (as strange as it may sound) a quite important issue. /Oskar

I *highly* agree. As explained by Oskar, 'readonly' is a way better description for D2.0's const than 'const'. It is not just a "better" description, it is a more *accurate* and *correct* one. The only reason we have this concept named 'const' in D is pure C++ legacy mentality, as D's mutability system (final-const-invariant) was designed from C++ mutability system(just const). However, since D's mutability system is so different from C++'s, Walter, why would you want to maintain the 'const' keyword and not change it to something better? Walter, please consider these arguments. PS: If one wants a shorter alternative than 'readonly', I think the shorter 'rdonly' keyword would rapidly, and as easily, be assimilated by our programmer minds to represent the read-only concept. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 01 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Oskar Linde wrote:
 Walter Bright skrev:
 readonly is a synonym for const, so no improvement there.

Read-only isn't synonymous with constant.

Consider ROM, i.e. "Read Only Memory".

"read only" doesn't say anything about the scope of the property. It's only equivalent to "constant" if that scope is equal to the scope of the data it references. Sean
Jun 26 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 
 or 'readonlyview' considered as an alternative for 'const'? I kind of 
 like the sound of 'readonly', and it doesn't make you think of C++ 
 'const'... but maybe that's bad.

readonlyview is too long.

And why not just 'view' again? It's short and seems to imply what's being done. Sean
Jun 26 2007
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
I don't like "view"... it's too verby and doesn't really imply "you can't touch
this" to me. readonly gets my vote, but then again, Walter was able to put
const/final/invariant into D without adding a new keyword. On the other hand,
he _did_ add ref when a more descriptive one (inout) was already in use... but
that's a different issue.

Sean Kelly Wrote:

 Walter Bright wrote:
 
 or 'readonlyview' considered as an alternative for 'const'? I kind of 
 like the sound of 'readonly', and it doesn't make you think of C++ 
 'const'... but maybe that's bad.

readonlyview is too long.

And why not just 'view' again? It's short and seems to imply what's being done. Sean

Jun 27 2007
parent Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 27 Jun 2007 03:18:58 -0400, Robert Fraser wrote:

 I don't like "view"... it's too verby and doesn't really imply "you can't
touch this" to me.
 readonly gets my vote, but then again, Walter was able to put
const/final/invariant
 into D without adding a new keyword. On the other hand, he _did_ add ref when 
 a more descriptive one (inout) was already in use... but that's a different
issue.

Let's face it, there isn't a single word in English that means "write access for you using this symbol is denied" which is what the 'const' keyword is trying to express. If we really must use 'const' (and I'm pretty sure Walter ain't ever, ever, ever going to change his mind) we must start thinking of it as meaning 'constrained' rather than 'constant'. That is, our access to the data represented by the symbol is constrained to just being able to look at it. -- Derek (skype: derek.j.parnell) Melbourne, Australia 27/06/2007 6:03:39 PM
Jun 27 2007
prev sibling parent reply Reiner Pope <some address.com> writes:
I think that invariant-as-storage-class vs. const-as-storage-class is a 
false dichotomy, and also that they are incorrectly associated with the 
completely different type-constructors, const() and invariant().


As storage classes, const and invariant both mean "compile-time 
constant" -- their initializers must be evaluatable at compile time, and 
their data need not be stored in memory.

However, apparently, data referred to by const-as-storage-class could 
actually be liable to change (this is cited as the difference between 
const-as-storage-class and invariant-as-storage-class).

Yet this seems to clash with the idea of being initialized at compile 
time: in order for the data pointed to by const-as-storage-class to be 
changed by another reference, there must be a mutable pointer from which 
such a variable is initialized. Something like:

void main() {
    int a = 10;
    int* p = &a; // here's our mutable view
    const int* cp = &a; // here's our const-as-storage-class
}

At the moment, this doesn't compile, because &a is not evaluatable at 
compile time (even though the address of a is statically known). 
Assuming this is never supported (and I don't think that it should be), 
then the data pointed to by a const-storage-class variable can never be 
changed, so the data is really invariant. In that case, 
const-as-storage-class should be abolished, as it is identical to 
invariant-as-storage-class.[1]

---

I also think that the idea of a "compile time constant" is distinct from 
invariant-as-type-constructor. For the purposes of clarity, I think it 
would ideally be better to make this distinction clear, and give this 
storage class a different name (sorry about Yet Another Keyword). I 
think 'define' could make sense:

     define PI = 3.14;
     define LanguageName = "D Programming Language";

This also emphasises the similarity in meaning to #define.

Of course, typeof(LanguageName) == invariant(char[])

---

[1] Although the data pointed to by const-as-storage-class *is* 
invariant, the type system doesn't believe it. The following doesn't 
compile (correctly so, according to the specs)

     const char[] a = "abc";
     invariant char[] b = a;

but how can the data in 'a' possibly change?



    Reiner
Jun 23 2007
next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Reiner Pope wrote:
 I think that invariant-as-storage-class vs. const-as-storage-class is a 
 false dichotomy, and also that they are incorrectly associated with the 
 completely different type-constructors, const() and invariant().
 
 
 As storage classes, const and invariant both mean "compile-time 
 constant" -- their initializers must be evaluatable at compile time, and 
 their data need not be stored in memory.
 
 However, apparently, data referred to by const-as-storage-class could 
 actually be liable to change (this is cited as the difference between 
 const-as-storage-class and invariant-as-storage-class).
 
 Yet this seems to clash with the idea of being initialized at compile 
 time: in order for the data pointed to by const-as-storage-class to be 
 changed by another reference, there must be a mutable pointer from which 
 such a variable is initialized. Something like:
 
 void main() {
    int a = 10;
    int* p = &a; // here's our mutable view
    const int* cp = &a; // here's our const-as-storage-class
 }
 
 At the moment, this doesn't compile, because &a is not evaluatable at 
 compile time (even though the address of a is statically known). 
 Assuming this is never supported (and I don't think that it should be), 
 then the data pointed to by a const-storage-class variable can never be 
 changed, so the data is really invariant. In that case, 
 const-as-storage-class should be abolished, as it is identical to 
 invariant-as-storage-class.[1]
 
 ---

I agree. Since in D, compile time values cannot be altered at compile time, then any compile time value is immutable/invariant. Thus const-as-storage-class is the same as invariant-as-storage-class and those concepts should be cleared up.
 
 I also think that the idea of a "compile time constant" is distinct from 
 invariant-as-type-constructor. For the purposes of clarity, I think it 
 would ideally be better to make this distinction clear, and give this 
 storage class a different name (sorry about Yet Another Keyword). I 
 think 'define' could make sense:
 
     define PI = 3.14;
     define LanguageName = "D Programming Language";
 
 This also emphasises the similarity in meaning to #define.
 
 Of course, typeof(LanguageName) == invariant(char[])
 
 ---
 

I agree. I don't like this overloading of concepts very much, such that 'invariant' is used both for compile time values, and invariant data. It's quite similiar to 'const' keyword in D 1.0, where it was used both for compile time values, and final values. As for an actual alternative, maybe instead of 'define' we could simply use alias: Either: alias PI = 3.14; or alias 3.14 PI; In any case, using 'alias' that way is actually pretty consistent with the current meaning of 'alias'. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 23 2007
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sat, 23 Jun 2007 18:34:59 +0100, Bruno Medeiros wrote:

 Reiner Pope wrote:
 
 I also think that the idea of a "compile time constant" is distinct from 
 invariant-as-type-constructor. For the purposes of clarity, I think it 
 would ideally be better to make this distinction clear, and give this 
 storage class a different name (sorry about Yet Another Keyword). I 
 think 'define' could make sense:
 
     define PI = 3.14;
     define LanguageName = "D Programming Language";
 
 This also emphasises the similarity in meaning to #define.
 
 Of course, typeof(LanguageName) == invariant(char[])
 
 ---
 

I agree. I don't like this overloading of concepts very much, such that 'invariant' is used both for compile time values, and invariant data. It's quite similiar to 'const' keyword in D 1.0, where it was used both for compile time values, and final values. As for an actual alternative, maybe instead of 'define' we could simply use alias: Either: alias PI = 3.14; or alias 3.14 PI; In any case, using 'alias' that way is actually pretty consistent with the current meaning of 'alias'.

I like both 'define' and 'alias' for this idea. 'alias' is not being overloaded because the concept behind it is retained. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnell
Jun 23 2007
parent Peter Modzelewski <peter.modzelewski gmail.com> writes:
Hmm last days I've noticed chaos going on in D comunity caused by D 
branches. I've written a small post about it on my devblog 
http://petermodzelewski.blogspot.com/ with some solution proposals. I 
don't want to play smartass but if D news become messy - D can lose its 
chance to be considered serious by professional developers. I hope my 
article can help a little with minimizing the chaos caused by the branches.
Jun 23 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Bruno Medeiros wrote:
 
 I agree. Since in D, compile time values cannot be altered at compile 
 time, then any compile time value is immutable/invariant. Thus 
 const-as-storage-class is the same as invariant-as-storage-class and 
 those concepts should be cleared up.

The storage class vs. type constructor issue should be cleared up in general. From the documentation, I had originally (falsely) assumed that invariant was a storage class only, when in reality the issue is much more simple. So far as I can tell, as type constructors, 'invariant' means the data will not change, period, 'const' means that the data can not be changed through the const-qualified reference, and 'final' means the reference cannot be reassigned once set. As storage classes, 'const' and 'invariant' mean exactly the same thing, and I'm still not entirely sure if there is a storage class interpretation of 'final'. As far as their application to non-reference variables is concerned, I think the overlap is unavoidable: they all mean effectively the same thing. Sean
Jun 23 2007
prev sibling next sibling parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Sat, 23 Jun 2007 17:22:41 +0300, Reiner Pope <some address.com> wrote=
:
 I think that invariant-as-storage-class vs. const-as-storage-class is =

 false dichotomy, and also that they are incorrectly associated with th=

 completely different type-constructors, const() and invariant().


 As storage classes, const and invariant both mean "compile-time  =

 constant" -- their initializers must be evaluatable at compile time, a=

 their data need not be stored in memory.

 However, apparently, data referred to by const-as-storage-class could =

 actually be liable to change (this is cited as the difference between =

 const-as-storage-class and invariant-as-storage-class).

 Yet this seems to clash with the idea of being initialized at compile =

 time: in order for the data pointed to by const-as-storage-class to be=

 changed by another reference, there must be a mutable pointer from whi=

 such a variable is initialized. Something like:

 void main() {
     int a =3D 10;
     int* p =3D &a; // here's our mutable view
     const int* cp =3D &a; // here's our const-as-storage-class
 }

 At the moment, this doesn't compile, because &a is not evaluatable at =

 compile time (even though the address of a is statically known).  =

 Assuming this is never supported (and I don't think that it should be)=

 then the data pointed to by a const-storage-class variable can never b=

 changed, so the data is really invariant. In that case,  =

 const-as-storage-class should be abolished, as it is identical to  =

 invariant-as-storage-class.[1]

 ---

 I also think that the idea of a "compile time constant" is distinct fr=

 invariant-as-type-constructor. For the purposes of clarity, I think it=

 would ideally be better to make this distinction clear, and give this =

 storage class a different name (sorry about Yet Another Keyword). I  =

 think 'define' could make sense:

      define PI =3D 3.14;
      define LanguageName =3D "D Programming Language";

 This also emphasises the similarity in meaning to #define.

 Of course, typeof(LanguageName) =3D=3D invariant(char[])

 ---

 [1] Although the data pointed to by const-as-storage-class *is*  =

 invariant, the type system doesn't believe it. The following doesn't  =

 compile (correctly so, according to the specs)

      const char[] a =3D "abc";
      invariant char[] b =3D a;

 but how can the data in 'a' possibly change?



     Reiner

I also thought about using a different keyword for compile-time constant= s. I used 'literal' instead of 'define' though. :) const int a =3D 10; //ok, compile-time constant const int b =3D f(); //error (assuming 'f()' cannot be evaluted at = compile time) I think that can be a bit confusing. I would like that 'b' would be = immutable, not a compile-time constant. (Yep, I know, 'final' is for that, but = still...) After that change, 'final' would be useless for value types. Unfortunately, refence types (and pointers) are more problematic. Old: const int v =3D 10; New: literal int v =3D 10; Old: const int v =3D f(); New: const int v =3D f(); Old: final const(MyClass) v; New: const MyClass v; Old: const(MyClass) v; New: const(MyClass) v; Old: final MyClass v; New: ?
Jun 24 2007
prev sibling next sibling parent reply Jascha Wetzel <firstname mainia.de> writes:
Reiner Pope wrote:
 As storage classes, const and invariant both mean "compile-time 
 constant" -- their initializers must be evaluatable at compile time, and 
 their data need not be stored in memory.

the docs aren't very clear about that, but it's not fully correct when you think about function parameters. the "initializers" of function parameters are the arguments to the call, which need not be evaluatable at compile time.
 void main() {
    int a = 10;
    int* p = &a; // here's our mutable view
    const int* cp = &a; // here's our const-as-storage-class
 }

 At the moment, this doesn't compile, because &a is not evaluatable at 

it compiles when you change it to int f(const int* cp) { return *cp+3; } void main() { int a = 10; int* p = &a; a = f(&a); } and it's also clear how *p can change here.
 [1] Although the data pointed to by const-as-storage-class *is* 
 invariant, the type system doesn't believe it. The following doesn't 
 compile (correctly so, according to the specs)
 
     const char[] a = "abc";
     invariant char[] b = a;
 
 but how can the data in 'a' possibly change?

again - if it is a parameter. besides holes and overlap in the final/const/invariant keyword's definition ranges, what is nice about D's const-system, is that while in some situations A means the same as B, A always means A and B always means B - unlike const in C++.
Jun 25 2007
parent Jason House <jason.james.house gmail.com> writes:
Jascha Wetzel wrote:
 Reiner Pope wrote:
 As storage classes, const and invariant both mean "compile-time 
 constant" -- their initializers must be evaluatable at compile time, 
 and their data need not be stored in memory.

the docs aren't very clear about that, but it's not fully correct when you think about function parameters. the "initializers" of function parameters are the arguments to the call, which need not be evaluatable at compile time.

That part of the documentation actually bugged me. One of the really nice features of pre-2.0 D was the option to delay initialization of const variables to the constructor... either a class constructor or static this(){}.
Jun 25 2007
prev sibling parent Christian Kamm <kamm.incasoftware shift-at-left-and-remove-this.de> writes:
 I think that invariant-as-storage-class vs. const-as-storage-class is a
 false dichotomy, and also that they are incorrectly associated with the
 completely different type-constructors, const() and invariant().

I totally agree. It took me a while to accept that const int i = 1; const(int) j = 1; mean something totally different. Especially since for function parameters void foo(const int* p); actually is the same as void foo(const(int*) p); I think it'd be easier to understand if compile time constants used a different keyword altogether and 'const type' was always the same as 'const(type)', not just in function parameter declarations. Christian
Jun 27 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

It's a good article. However, it could really use some examples. The article on scope was very convincing because it gives plausible use-cases. In particular, I think there needs to be an example for 'invariant'. When is 'const' not good enough? Where you state that invariant solves the aliasing problem, you could prove it by rewriting the example from the C++ section. QUOTE --- But there is a need for a constant declaration referencing a mutable type.This is provided with the final storage class for declarations...Its main purpose is ...[that it]...can be mentally separated from variable declarations that are meant to change --- I don't find this very convincing. Is the mental benefit really significant enough to justify the extra complexity? The existence of three const-related keywords is pretty tough on mental space! QUOTE ---- int x = 3; const int *p = &x; *p = 4; // error, read-only view x = 5; // ok int y = *p; // y is set to 5 This is one instance of the so-called aliasing problem, since while the above snippet is trivial, the existence of such aliases can be very hard to detect in a complex program. It is impossible for the compiler to reliably detect it. This means that the compiler cannot cache 4 in a register and reuse the cached value to replace *p, it must go back and actually dereference p again. --- Shouldn't that be: "the compiler cannot cache 3 in a register" ?
Jun 22 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Don Clugston wrote:
 QUOTE ----
 int x = 3;
 const int *p = &x;
 *p = 4;        // error, read-only view
 x = 5;        // ok
 int y = *p;    // y is set to 5
 
 This is one instance of the so-called aliasing problem, since while the
 above snippet is trivial, the existence of such aliases can be very hard
 to detect in a complex program. It is impossible for the compiler to
 reliably detect it. This means that the compiler cannot cache 4 in a
 register and reuse the cached value to replace *p, it must go back and
 actually dereference p again.
 ---
 Shouldn't that be: "the compiler cannot cache 3 in a register" ?

No. At "*p = 4;" the compiler can't cache 4, because then at "int y = *p;" y would become 4 instead of 5. Although in the example it's moot because "*p = 4;" is an error. -- Remove ".doesnotlike.spam" from the mail address.
Jun 22 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Deewiant wrote:
 Don Clugston wrote:
 QUOTE ----
 int x = 3;
 const int *p = &x;
 *p = 4;        // error, read-only view
 x = 5;        // ok
 int y = *p;    // y is set to 5

 This is one instance of the so-called aliasing problem, since while the
 above snippet is trivial, the existence of such aliases can be very hard
 to detect in a complex program. It is impossible for the compiler to
 reliably detect it. This means that the compiler cannot cache 4 in a
 register and reuse the cached value to replace *p, it must go back and
 actually dereference p again.
 ---
 Shouldn't that be: "the compiler cannot cache 3 in a register" ?

No. At "*p = 4;" the compiler can't cache 4, because then at "int y = *p;" y would become 4 instead of 5. Although in the example it's moot because "*p = 4;" is an error.

Better, I think, would be: int x = 3; const int *p = &x; *p = 4; // error, read-only view int y = *p; x=5; int z = *p; and then compiler can't set z=y. Then we can see what invariant is for: int x = 3; invariant int *p = &x; // error, x is not invariant invariant int x = 3; invariant int *p = &x; *p = 4; // error, p is invariant int y = *p; x=5; // error, x is invariant int z = *p; // compiler knows that y==z.
Jun 22 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Don Clugston wrote:
 Better, I think, would be:

Some good suggestions. I made some changes.
Jun 22 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

Very nice read. But I have a few nitpicks too. :-) """ Which brings up another aspect of const in D - it's transitive. Const in C++ is not transitive, which means one can have a pointer to const pointer to mutable int. To declare a variable that is const at each level, one must write: int const *const *const *p; // C++ The const is left associative, so the declaration is a pointer to const pointer to const pointer to const int. Const being transitive in D means that every reference reachable through the const is also const. An entire logical region of an application can be protected by placing only one qualifier. To reflect that, the syntax is different, using constructor-like notation: const(int **)* p; // D Here the const applies to the part of the type that is in parentheses. """ The way these two examples come one right after the other it leads one to expect that the D example is going to show the D equivalent of the C++ code. But I don't think that's what it's doing. I would suggest first showing the D version of the C++ snippet (which is just const int***p -- right?). That gives people a chance to say "ooh ah D is so much simpler". Then follow up by saying "what if we instead want part of that to be modifiable? And give the const(int**)* p example (and maybe a C++ version of that too). Also the way it is it doesn't explain sufficiently what "applies to the part in parenthesis" means. Being explicit about what's modifiable there would be helpful. Maybe remind people to read decls from right to left and spell it out "this is a mutable pointer to const pointer to const pointer" """ But there is a need for a constant declaration referencing a mutable type. """ This transition could be clearer. This line comes shortly after introducing 'invariant', so it sounds like you're still talking about a nuance of invariant here rather than moving on to the last of the big three const-related keywords. Maybe say something like "Finally, there is also a need ..." Also in the discussion of final, the word that always comes to my mind is 'binding'. It's the binding of the variable name to its value that's final. You say, "A final declaration, once it is set by its initializer or constructor, cannot ever change its value for its lifetime." which to me is kind of vague. I not sure what it means that "a declaration cannot change its value". Does a declaration even have a value? "I'm setting this declaration to 5" -- seems like unusual terminology to me. This is more of a question than a suggestion, but here: """ Since a final declaration cannot change its value, it is by nature invariant, and the address of it will become a pointer to an invariant: """ That means this is ok: int x,y; final int* p = &x; p = &y; // error, p is final *p = 3; // ok, *p is mutable But if I take &p then I lose the ability to indirectly set x? **(&p) = 5; // error *(&p) not same type as p! Finally: """ Like C++, D allows the casting away of constness and invariantness. Unlike C++, if the programmer then subverts the const or invariant guarantee and changes the underlying data, then undefined behavior results. """ This seems like kind of a weak ending. Lack of being able to trust const due to casts is listed as one of the big problems with C++'s const. Yet in the end it reads like D does *less* than C++ here. Not only can you cast at will, you can't even be sure it will work. Maybe it's just the weak statement "undefined behavior results" that bothers me. It makes it sound like it just happens -- because it was too difficult to implement or something -- rather than it being a proactive component of D's design. Something like this might read better: """ Unlike C++, D specifies that subversion of the const or invariant guarantee will result in undefined behavior. Casting away const is safe only if the data referenced is truly not modified. Of course even in C++, casting away const in order to modify data is usually not /safe/, merely well defined. D allows the compiler to make optimizations which assume const data will not change, whereas C++ requires compilers to assume that it always changes, despite being const. """ --bb
Jun 22 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 http://www.digitalmars.com/d/const.html

But there is a need for a constant declaration referencing a mutable type. """ Also in the discussion of final, the word that always comes to my mind is 'binding'. It's the binding of the variable name to its value that's final. You say, "A final declaration, once it is set by its initializer or constructor, cannot ever change its value for its lifetime." which to me is kind of vague. I not sure what it means that "a declaration cannot change its value". Does a declaration even have a value? "I'm setting this declaration to 5" -- seems like unusual terminology to me.

I highly agree. That terminology usage made me grind my teeth when reading the article! A declaration "having a value"/"referencing a mutable type"? It's not just unusual, it's plain nonsensical! 'Binding' is better, but I'm also not 100% keen on it. I've been using terms such as 'top-level type', and also 'immediate value'. BTW, the 'immediate value' of a variable/expression is what I call, by definition: the value that changes when an assignment is made to that variable/expression. I've found that in Walter's article, when he mentions "declaration", in most cases he means either 'top-level type', or 'immediate value'. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 23 2007
prev sibling next sibling parent Manfred Hansen <m.hansen kielnet.net> writes:
Walter Bright wrote:

 http://www.digitalmars.com/d/const.html

When i read the discussion, about the complicated const/final/invariant, it come in my mind, if we really need pointer "*"? Maybe we can solve this with ".ptr" . What i have understand, we need it only to interface with C. Ok, this is important enough. I knew many people didn't like pointer "*" and make a bow about D. My 2 cents. Manfred
Jun 23 2007
prev sibling parent eao197 <eao197 intervale.ru> writes:
On Thu, 21 Jun 2007 10:36:12 +0400, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 http://www.digitalmars.com/d/const.html

While reading this topic (specialy discussion between Sean and Walter) = I've understood what confusing me in current const/invariant/final desig= n: Keyword 'const' for value-types means immutability. For example, as I = think, the following declarations are the same: const int i =3D 0; invariant int j =3D 0; because both i and j cannot be changed or reassinged, both i and j must = be = initialized at the compile time. DMD 2.000 even doesn't allow getting = address of i. But for reference types and for pointers 'const' means 'constant view' t= o = other data. That dualism little confuses me. My propose is dissable use 'const' keyword for value-type and allow = 'const' only for 'constant view'. Let value-type constant be defined by = = 'invariant'. -- = Regards, Yauheni Akhotnikau
Jun 23 2007