D - c++ style const concept
- Chris Sokol (1/1) Aug 07 2003 Does D support it at all?
- Walter (3/4) Aug 07 2003 D supports const as a storage class, but not as a type modifier.
- Matthew Wilson (3/7) Aug 07 2003 What's the rationale again?
- Walter (8/19) Aug 08 2003 1) Too complicated (look at all the wierd rules and perterbations it cau...
- Farmer (46/47) Aug 09 2003 Optimizing *library writers* need that info to write optimized code that...
- Walter (7/53) Aug 09 2003 is
- Philippe Mori (15/21) Aug 12 2003 is
- Matthew Wilson (8/14) Aug 09 2003 I cannot contest that it's complicated, as you're the expert on
-
Walter
(16/30)
Aug 09 2003
'Seems' is the operative word here
. Const has a seductive simplicity - Bill Cox (15/22) Aug 11 2003 Hi, Mathew.
- Sean L. Palmer (71/93) Aug 12 2003 Walter has done so by sacrificing the language's ability to pass around
- Matthew Wilson (18/117) Aug 12 2003 That's a damn site better job than I was revving up for, Bill, so I'll
- Bill Cox (53/109) Aug 12 2003 Hi, Matthew and Sean.
- Antti =?iso-8859-1?Q?Syk=E4ri?= (26/26) Aug 12 2003 The "const" qualifier is conceptually similar to Java's "throws"
- Fabian Giesen (22/29) Aug 14 2003 The C++ const doesn't make any strict static guarantees, that's why
- Philippe Mori (64/93) Aug 14 2003 So IMO we need a way that allows us to tell the compiler that
- Antti =?iso-8859-1?Q?Syk=E4ri?= (14/26) Aug 15 2003 There's no point arguing about the meaning of const, just look up the
- Fabian Giesen (4/9) Aug 16 2003 Unless someone casts your donstness away, which anyone can do without
- Philippe Mori (25/34) Aug 16 2003 I would vote that it would not be possible to cast away constness... but
- Antti =?iso-8859-1?Q?Syk=E4ri?= (7/16) Aug 17 2003 You can be pretty certain that the callee won't cast your constness away
- Mike Wynn (10/26) Aug 17 2003 I'm not sure a quite follow the argument here, and not all platforms seg
- Antti =?iso-8859-1?Q?Syk=E4ri?= (90/119) Aug 17 2003 It's just a question of the proportion of trust you are ready to place
- Mike Wynn (11/19) Aug 17 2003 your whole argument would seem to me to agree that const is pointless, y...
- Matthew Wilson (7/126) Aug 18 2003 Perfectly put! I agree.
- Fabian Giesen (31/44) Aug 18 2003 This is beside the point of what I'm trying to say. The question is why
- Peter Hercek (17/37) Aug 18 2003 I don't think it needs to be a guarantee. I look at const more as a domu...
- Philippe Mori (16/32) Aug 19 2003 intention
- Antti =?iso-8859-1?Q?Syk=E4ri?= (69/80) Aug 19 2003 Point taken. I didn't particularly try to advocate "const" as it is in
- Philippe Mori (42/84) Aug 19 2003 it's an
- Antti =?iso-8859-1?Q?Syk=E4ri?= (34/96) Aug 23 2003 What do you mean by source and target in this context?
- Philippe Mori (48/79) Aug 23 2003 Suppose something similar to this:
- Antti =?iso-8859-1?Q?Syk=E4ri?= (43/89) Aug 24 2003 If it's immutable, then it cannot. If it's mutable, like in your
- Philippe Mori (48/108) Aug 25 2003 If I want to uses an in parameter, does I have the choice between
- Antti =?iso-8859-1?Q?Syk=E4ri?= (29/50) Aug 25 2003 Whoops :) Of course immutable objects would be "in" since they cannot be
- Walter (5/34) Sep 19 2003 No. It applies to any use of a function.
- Sean L. Palmer (151/231) Aug 20 2003 I'm a heavy const user, a true believer in it, I'd say. I'm going to th...
- Philippe Mori (90/236) Aug 20 2003 applies
- Sean L. Palmer (57/143) Aug 21 2003 too,
- Philippe Mori (14/22) Aug 23 2003 As mentionned in my other answer, you are right, it does not works
- Daniel Yokomiso (95/101) Aug 24 2003 Hi,
- Antti =?iso-8859-1?Q?Syk=E4ri?= (113/166) Aug 25 2003 So this would be equivalent to C++'s const. (Without the possibility of
- Daniel Yokomiso (191/325) Aug 26 2003 Hi,
- Sean L. Palmer (21/44) Aug 26 2003 This seems logical.
- Daniel Yokomiso (95/113) Aug 26 2003 ----- Original Message -----
- Philippe Mori (41/75) Aug 26 2003 same
-
Daniel Yokomiso
(16/88)
Aug 26 2003
"Philippe Mori"
escreveu na mensagem - Antti =?iso-8859-1?Q?Syk=E4ri?= (140/198) Aug 26 2003 [snip]
- Philippe Mori (27/81) Aug 26 2003 Then what we would like is is template with automatic return type deduct...
- Daniel Yokomiso (33/125) Aug 26 2003 But I like immutable so much... ;)
- Farmer (8/14) Aug 26 2003 ConstJava looks like a straight adaption of C++'s const notion. They jus...
- Walter (10/14) Sep 18 2003 assembler
- Matthew Wilson (33/37) Sep 18 2003 It's only useless from the perspective of a compiler writer. For
- Walter (32/43) Sep 19 2003 Not exactly. Consider the case of passing a parameter that is int****p. ...
- Antti =?iso-8859-1?Q?Syk=E4ri?= (15/33) Sep 19 2003 This is a good summary of the similarities of const and exception
- Walter (9/22) Sep 19 2003 If you add a const to a type, that can ripple downwards through the call
- Philippe Mori (30/52) Sep 19 2003 IMO the problem in C++ is the fact that we have to explictly
- Matthew Wilson (12/68) Sep 19 2003 exception.
- Philippe Mori (20/31) Sep 19 2003 I should have said implies... An in parameter should not be modified
- scott_mccaskill yahoo.com (22/30) Aug 27 2003 Remember that const is type information. The same could be argued of an...
- Philippe Mori (20/28) Aug 27 2003 not be
- Walter (17/21) Sep 18 2003 The problem is that the standard doesn't preclude anyone *else* from
- Ilya Minkov (15/30) Nov 03 2003 ^^^^^^^
- Walter (3/13) Nov 03 2003 Ah, I have it backwards. Put the const in front of p1.
- Peter Hercek (11/22) Aug 12 2003 From my point of view:
- Walter (9/14) Sep 18 2003 Frankly, I think the way to do this is to put const data into a 'read on...
- Mike Wynn (58/66) Aug 12 2003 It's
- Sean L. Palmer (19/33) Aug 13 2003 So you're saying you want a version of 'in' that works by reference alwa...
- Mike Wynn (27/39) Aug 13 2003 always?
- Ilya Minkov (16/18) Aug 17 2003 It took me looooooong to remember... i was telling to myself all the
- Fabian Giesen (19/27) Aug 18 2003 Nope, whether something's const or not doesn't change a bit about how
- Antti =?iso-8859-1?Q?Syk=E4ri?= (9/13) Aug 18 2003 "Dag Brück and others reviewed considerable amounts of real code to see
- Ilya Minkov (11/17) Aug 18 2003 f
- Philippe Mori (36/42) Aug 18 2003 IMO, if the default was const, the situation would be a bit better...
- Scott McCaskill (54/60) Aug 19 2003 The canonical example of a good/useful const_cast in C++ is to avoid
- Ilya Minkov (3/6) Aug 20 2003 Why do you need a non-const version at all?
- Fabian Giesen (2/3) Aug 20 2003 Because the (class-)const version only returns a const reference.
- Scott McCaskill (8/13) Aug 20 2003 same
- Philippe Mori (44/66) Aug 12 2003 I think this is right that the interaction is too complicated and it oft...
- Ilya Minkov (2/2) Aug 12 2003 Philippe, you're speaking my mind!
"Chris Sokol" <chris echosproject.org> wrote in message news:bgv1t6$1ijj$1 digitaldaemon.com...Does D support it at all?D supports const as a storage class, but not as a type modifier.
Aug 07 2003
What's the rationale again? "Walter" <walter digitalmars.com> wrote in message news:bgvd22$1sna$1 digitaldaemon.com..."Chris Sokol" <chris echosproject.org> wrote in message news:bgv1t6$1ijj$1 digitaldaemon.com...Does D support it at all?D supports const as a storage class, but not as a type modifier.
Aug 07 2003
1) Too complicated (look at all the wierd rules and perterbations it causes with template specialization and overloading). 2) Too confusing. 3) Insufficient utility. 4) Optimizers can't reliably use the info anyway. 5) Ugly. "Matthew Wilson" <matthew stlsoft.org> wrote in message news:bgvdh2$1t4n$1 digitaldaemon.com...What's the rationale again? "Walter" <walter digitalmars.com> wrote in message news:bgvd22$1sna$1 digitaldaemon.com..."Chris Sokol" <chris echosproject.org> wrote in message news:bgv1t6$1ijj$1 digitaldaemon.com...Does D support it at all?D supports const as a storage class, but not as a type modifier.
Aug 08 2003
"Walter" <walter digitalmars.com> wrote in news:bh1f3a$psi$1 digitaldaemon.com:4) Optimizers can't reliably use the info anyway.Optimizing *library writers* need that info to write optimized code that is *robust*. Quotting from Phobos string.d: /************** Constants ****************/ const char[16] hexdigits = "0123456789ABCDEF"; /*********************************************** * Convert to char[]. */ char[] toString(uint u) { char[uint.size * 3] buffer; int ndigits; char c; char[] result; ndigits = 0; if (u < 10) // Avoid storage allocation for simple stuff result = digits[u .. u + 1]; else { while (u) { c = (u % 10) + '0'; u /= 10; ndigits++; buffer[buffer.length - ndigits] = c; } result = new char[ndigits]; result[] = buffer[buffer.length - ndigits .. buffer.length]; } return result; } Looks like nice code ? But the code isn't robust as this example proves: int main(char args[][]) { char[] digit=toString(1); assert(digit[0] == '1'); digit[0]='9'; char[] digit2=toString(1); assert(digit2[0] == '1'); // assertion fails ! return 0; }
Aug 09 2003
"Farmer" <itsFarmer. freenet.de> wrote in message news:Xns93D2F2BCBD361itsFarmer 63.105.9.61..."Walter" <walter digitalmars.com> wrote in news:bh1f3a$psi$1 digitaldaemon.com:is4) Optimizers can't reliably use the info anyway.Optimizing *library writers* need that info to write optimized code that*robust*. Quotting from Phobos string.d: /************** Constants ****************/ const char[16] hexdigits = "0123456789ABCDEF"; /*********************************************** * Convert to char[]. */ char[] toString(uint u) { char[uint.size * 3] buffer; int ndigits; char c; char[] result; ndigits = 0; if (u < 10) // Avoid storage allocation for simple stuff result = digits[u .. u + 1]; else { while (u) { c = (u % 10) + '0'; u /= 10; ndigits++; buffer[buffer.length - ndigits] = c; } result = new char[ndigits]; result[] = buffer[buffer.length - ndigits .. buffer.length]; } return result; } Looks like nice code ? But the code isn't robust as this example proves: int main(char args[][]) { char[] digit=toString(1); assert(digit[0] == '1'); digit[0]='9'; char[] digit2=toString(1); assert(digit2[0] == '1'); // assertion fails ! return 0; }The assumption that Phobos uses throughout is copy-on-write. That means that unless you're the generator of a string, if you modify it, make a copy first. This avoids the problems the code snippet shows, while retaining max performance.
Aug 09 2003
"Farmer" <itsFarmer. freenet.de> a écrit dans le message de news:Xns93D2F2BCBD361itsFarmer 63.105.9.61..."Walter" <walter digitalmars.com> wrote in news:bh1f3a$psi$1 digitaldaemon.com:is4) Optimizers can't reliably use the info anyway.Optimizing *library writers* need that info to write optimized code that*robust*. Quotting from Phobos string.d:I think that for "standard" libraries, the more information is available on a type, the more we can write code that could be optimized... This is particulary interesting for template where we could select the appropriate algorithm depending on some attributes or modifier... Ideally, it should be possible to extract modifier information and any other usefull information when we want to... but when we do not want we should be able to do 1 algorithm that works for all (supported) cases. It may be interesting to be able to distinguate concept (an object that appears const) and reality (an object in read-only memory) when needed
Aug 12 2003
1) Too complicated (look at all the wierd rules and perterbations itcauseswith template specialization and overloading).I cannot contest that it's complicated, as you're the expert on compiler-waltering.2) Too confusing.Disagree. Seems perfectly clear to me.3) Insufficient utility.You couldn't be more wrong4) Optimizers can't reliably use the info anyway.Have no knowledge here5) Ugly.Not meaningful. Most things in SE are ugly until you get used to them like, say, D's templates ... ;)
Aug 09 2003
"Matthew Wilson" <matthew stlsoft.org> wrote in message news:bh3rhp$u1$1 digitaldaemon.com...'Seems' is the operative word here <g>. Const has a seductive simplicity that's a mirage. Check out all the weird and wild ways it influences function overloading, argument deduction, partial specialization, and partial ordering.1) Too complicated (look at all the wierd rules and perterbations itcauseswith template specialization and overloading).I cannot contest that it's complicated, as you're the expert on compiler-waltering.2) Too confusing.Disagree. Seems perfectly clear to me.Show me where it's useful. I finally stripped it out of my code, as it never found a bug after 15 years using it thinking it might help find a bug.3) Insufficient utility.You couldn't be more wrongI have a lot of practice trying to make use of const info in the optimizer. There isn't any. The optimizer always winds up having to assume that the data pointed to by a pointer to const changes anyway. Then there's 'mutable', const casts, etc. Const doesn't guarantee that the underlying data doesn't change, so it is useless for optimization.4) Optimizers can't reliably use the info anyway.Have no knowledge herelike,5) Ugly.Not meaningful. Most things in SE are ugly until you get used to themsay, D's templates ... ;)I see all the 'const' qualifiers over and over as obfuscation. API declarations look a lot cleaner and straightforward without them.
Aug 09 2003
Hi, Mathew. I would be very interested in hearing about the utility of const. I'm not disputing your assertion, but I haven't had the same experience. We also dropped the use of const from our coding style at work, and I can't remember spending 1 minute tracking down a bug that const would have prevented. Perhaps there was a better way, but I feel like we were force to drop the use of 'const' pointers. The problem is that it was too much for our guys to type on 90% of all object handles passed to functions. I had a hell of a time enforcing it. Overall, it seemed to hurt our programmers ability to work together. Perhaps a 'variable' keyword or some such to declare what parameters get modified would have worked out. To me, it seems Walter has chosen good middle ground by supporting the use of 'const' only as a storage class modifier. BillShow me where it's useful. I finally stripped it out of my code, as it never found a bug after 15 years using it thinking it might help find a bug.3) Insufficient utility.You couldn't be more wrong
Aug 11 2003
Walter has done so by sacrificing the language's ability to pass around constants except by copying them. Think about big arrays of constants, or constant structures. To safely pass a constant by reference you have to be able to ensure that nobody will alter the original version. Once you start passing pointers around to various functions it becomes impossible to statically ascertain whether a given call will violate the constness of the data you are essentially passing in by reference. Thus to be certain, the compiler is forced to make a copy of all const data before passing it by reference. One way around this is make the static checking more robust, by say decorating the function signature with information about how the function modifies its reference parameters, and information about all functions this function might call with those parameters so that checking can pursue all possible routes by which the function might alter the parameter. Only once this has been done can the compiler safely pass in the original const data instead of a copy. I believe the same problem exists for determining register saving when using so-called fastcall calling conventions. If you can determine a called function will not clobber a register, you are safe to assume you can leave values there over the call, and much saving and restoring can be prevented or at least delayed to the point where it must be done due to the limited number of registers. With const, however, a protocol for parameter passing is not so easy. You surely want to avoid copying data as much as possible, but occasionally it might be worth copying the data if that would be easier than burdening an entire call with restrictions. I certainly wouldn't want a language to permit anyone to modify data I declare as a constant. Yet I want to be able to pass those constants to functions by reference without them being copied, so long as the function is safe for passing const data to. And if I try to pass a constant to a function that will potentially modify the contents, I wish to be warned at compile time instead of having silent copies generated that I can't keep track of. The compiler is probably much better at keeping tabs on what functions do or do not alter their parameters than I am. Therefore it should be assigned the task of adorning parameters with constness, as well as making sure const data only gets passed by reference to those sorts of parameters. An error would mention the const data passed, as well as point out at least one bit of offending code which prevents such a call, directly or indirectly, preferrably with some sort of route taken thru the code by which said problem could happen. That would be enough info for a programmer that wished the function to actually be const-safe to eradicate the offending writes. Presto, D language syntax and grammar doesn't change, just the semantics slightly altered with a validity check inserted into the compiler, which it should enforce to the best of its ability. Problem solved, everybody happy. ;) Except poor Walter, who would have to implement it. Does anyone else think const in C++ is a poor attempt at letting the user decorate info that a sophisticated enough compiler could be able to figure out on its own? Somewhat akin to the "register" keyword, which "aids" the compiler by disallowing any possibility of aliasing the value stored? It's also something the compiler can figure out for you, something that modern compilers do. They even do a pretty good job at it, from what I understand, though C++'s laxity with pointers allows hideous data flow spaghetti that even the best compilers cannot sort out. Whatever it takes to get the compiler to make good code. I often must sound like I'm asking for alot. Another nice thing about keeping track of constness is that a pointer to constant data need never be freed, but that affects D very little since it's built around garbage collection which gives you this anyway, for free. ;) Yet another nice thing about passing constants is that it serves as an indicator to the optimizer that it might be able to generate a custom version of the function tailored with the constant completely inlined. It's something people would want to do (identity matrix, anybody?) and I would expect the compiler to try to take advantage of such a situation. Sean "Bill Cox" <bill viasic.com> wrote in message news:bh85p6$1324$1 digitaldaemon.com...neverShow me where it's useful. I finally stripped it out of my code, as it3) Insufficient utility.You couldn't be more wrongfound a bug after 15 years using it thinking it might help find a bug.Hi, Mathew. I would be very interested in hearing about the utility of const. I'm not disputing your assertion, but I haven't had the same experience. We also dropped the use of const from our coding style at work, and I can't remember spending 1 minute tracking down a bug that const would have prevented. Perhaps there was a better way, but I feel like we were force to drop the use of 'const' pointers. The problem is that it was too much for our guys to type on 90% of all object handles passed to functions. I had a hell of a time enforcing it. Overall, it seemed to hurt our programmers ability to work together. Perhaps a 'variable' keyword or some such to declare what parameters get modified would have worked out. To me, it seems Walter has chosen good middle ground by supporting the use of 'const' only as a storage class modifier. Bill
Aug 12 2003
That's a damn site better job than I was revving up for, Bill, so I'll merely say "hear, hear". "Sean L. Palmer" <palmer.sean verizon.net> wrote in message news:bha4n0$8q2$1 digitaldaemon.com...Walter has done so by sacrificing the language's ability to pass around constants except by copying them. Think about big arrays of constants, or constant structures. To safely pass a constant by reference you have to be able to ensure that nobody will alter the original version. Once you start passing pointers around to various functions it becomes impossible to statically ascertain whether a given call will violate the constness of the data you are essentially passing in by reference. Thustobe certain, the compiler is forced to make a copy of all const data before passing it by reference. One way around this is make the static checking more robust, by say decorating the function signature with information about how the function modifies its reference parameters, and information about all functionsthisfunction might call with those parameters so that checking can pursue all possible routes by which the function might alter the parameter. Onlyoncethis has been done can the compiler safely pass in the original const data instead of a copy. I believe the same problem exists for determining register saving whenusingso-called fastcall calling conventions. If you can determine a called function will not clobber a register, you are safe to assume you can leave values there over the call, and much saving and restoring can be prevented or at least delayed to the point where it must be done due to the limited number of registers. With const, however, a protocol for parameter passing is not so easy. You surely want to avoid copying data as much as possible, but occasionally it might be worth copying the data if that would be easier than burdening an entire call with restrictions. I certainly wouldn't want a language to permit anyone to modify data I declare as a constant. Yet I want to be able to pass those constants to functions by reference without them being copied, so long as the functionissafe for passing const data to. And if I try to pass a constant to a function that will potentially modify the contents, I wish to be warned at compile time instead of having silent copies generated that I can't keep track of. The compiler is probably much better at keeping tabs on what functions doordo not alter their parameters than I am. Therefore it should be assigned the task of adorning parameters with constness, as well as making sureconstdata only gets passed by reference to those sorts of parameters. An error would mention the const data passed, as well as point out at least one bit of offending code which prevents such a call, directly or indirectly, preferrably with some sort of route taken thru the code by which said problem could happen. That would be enough info for a programmer that wished the function to actually be const-safe to eradicate the offending writes. Presto, D language syntax and grammar doesn't change, just the semantics slightly altered with a validity check inserted into the compiler, whichitshould enforce to the best of its ability. Problem solved, everybody happy. ;) Except poor Walter, who would havetoimplement it. Does anyone else think const in C++ is a poor attempt at letting the user decorate info that a sophisticated enough compiler could be able to figure out on its own? Somewhat akin to the "register" keyword, which "aids" the compiler by disallowing any possibility of aliasing the value stored?It'salso something the compiler can figure out for you, something that modern compilers do. They even do a pretty good job at it, from what Iunderstand,though C++'s laxity with pointers allows hideous data flow spaghetti that even the best compilers cannot sort out. Whatever it takes to get the compiler to make good code. I often mustsoundlike I'm asking for alot. Another nice thing about keeping track of constness is that a pointer to constant data need never be freed, but that affects D very little sinceit'sbuilt around garbage collection which gives you this anyway, for free. ;) Yet another nice thing about passing constants is that it serves as an indicator to the optimizer that it might be able to generate a custom version of the function tailored with the constant completely inlined.It'ssomething people would want to do (identity matrix, anybody?) and I would expect the compiler to try to take advantage of such a situation. Sean "Bill Cox" <bill viasic.com> wrote in message news:bh85p6$1324$1 digitaldaemon.com...neverShow me where it's useful. I finally stripped it out of my code, as it3) Insufficient utility.You couldn't be more wrongfound a bug after 15 years using it thinking it might help find a bug.Hi, Mathew. I would be very interested in hearing about the utility of const. I'm not disputing your assertion, but I haven't had the same experience. We also dropped the use of const from our coding style at work, and I can't remember spending 1 minute tracking down a bug that const would have prevented. Perhaps there was a better way, but I feel like we were force to drop the use of 'const' pointers. The problem is that it was too much for our guys to type on 90% of all object handles passed to functions. I had a hell of a time enforcing it. Overall, it seemed to hurt our programmers ability to work together. Perhaps a 'variable' keyword or some such to declare what parameters get modified would have worked out. To me, it seems Walter has chosen good middle ground by supporting the use of 'const' only as a storage class modifier. Bill
Aug 12 2003
Matthew Wilson wrote:That's a damn site better job than I was revving up for, Bill, so I'll merely say "hear, hear".Hi, Matthew and Sean. I very much respect both of your opinions on this. I just have a somewhat different view point."Sean L. Palmer" <palmer.sean verizon.net> wrote in messageThe compiler doesn't make any copies of constant arrays, as demonstraited with the following code and results: const int [] a = [1, 2, 3, 4]; void writeOverConstArray( int [] a) { int i; for(i = 0; i < 4; i++) { a[i] *= a[i]; } } int main(char[][] args) { int i; writeOverConstArray(a); for(i = 0; i < 4; i++) { printf("a[%d] = %d\n", i, a[i]); } return 0; }Walter has done so by sacrificing the language's ability to pass around constants except by copying them. Think about big arrays of constants, or constant structures. To safely pass a constant by reference you have to be able to ensure that nobody will alter the original version. Once you start passing pointers around to various functions it becomes impossible to statically ascertain whether a given call will violate the constness of the data you are essentially passing in by reference. Thustobe certain, the compiler is forced to make a copy of all const data before passing it by reference.consta[0] = 1 a[1] = 4 a[2] = 9 a[3] = 16 The compiler simply doesn't prove that constant data isn't over-written. ...I think this is the heart of the issue. If you didn't feel that way, would you still want D to prove that constant data isn't modified? I'm not questioning the validity of how you feel about it, but I doubt it's based on repeated experience of getting burned by having your constants overwritten. I'd guess you've been using 'const' properly ever since it was introduced in C++, and haven't been in a position to find out how many bugs you've avoided. Again, if Walter wants D to feel like a smooth highway to C++ programmers, including full constant protection might help. Frankly, I've always been annoyed by it, since it makes it harder for our code to interface with other peoples code (our code doesn't use const protection, their code requires it). We all wind up writing lots of casts to use eachother's code.I certainly wouldn't want a language to permit anyone to modify data I declare as a constant.Yes. Definately. Why not just have the compiler try to prove that constant data is never modified, rather than having me put 'const' keywords in all my parameter declarations? Is this what you're getting at? If so, I'm for it. It would eliminate all the cast nonsense 'const' creates when we use eachother's code.Does anyone else think const in C++ is a poor attempt at letting the user decorate info that a sophisticated enough compiler could be able to figure out on its own? Somewhat akin to the "register" keyword, which "aids" the compiler by disallowing any possibility of aliasing the value stored?It'sSo do the rest of us.also something the compiler can figure out for you, something that modern compilers do. They even do a pretty good job at it, from what Iunderstand,though C++'s laxity with pointers allows hideous data flow spaghetti that even the best compilers cannot sort out. Whatever it takes to get the compiler to make good code. I often mustsoundlike I'm asking for alot.I agree. However, Walter's asserted that the compiler doesn't get to do such optimizations. I'm always for making faster code. BillAnother nice thing about keeping track of constness is that a pointer to constant data need never be freed, but that affects D very little sinceit'sbuilt around garbage collection which gives you this anyway, for free. ;) Yet another nice thing about passing constants is that it serves as an indicator to the optimizer that it might be able to generate a custom version of the function tailored with the constant completely inlined.It'ssomething people would want to do (identity matrix, anybody?) and I would expect the compiler to try to take advantage of such a situation.
Aug 12 2003
The "const" qualifier is conceptually similar to Java's "throws" statements. Both are designed to make some static guarantees about the program's behavior. However, taking care of the "throws" statements causes the programmers to fallback to the "catch and ignore all exceptions" mode. Similarly, when it comes to usage of "const" (in C++) some programmers fall back to the "ignore const altogether and when I need to interface with software that actually uses const qualifiers, just cast them away". This comes as no surprise: Manually taking care of things just complicates interfaces and makes the programmer bored. So the main question is, should one make a programming language that will make programming fun, or one that allows for as strict static guarantees as possible? D seems to have taken the first route, and I'm not complaining. There would be two solutions for ensuring that no const object will change: one that examines the program text and automatically decorates the interfaces (or an internal database, or whatever) with the knowledge like "function x treats arguments y and z as constants", as per Sean's suggestion. (If I got it right). The problem is how you define the of library interfaces and how to make the whole thing visible to the programmer so that he knows what's going on. Another could relying for run-time guarantees: placing the const data in a page with only read-only access, and turning hardware exceptions into D exceptions if possible. This is what C++ does (in addition to static guarantees of const) and that's my personal preference, too. -Antti
Aug 12 2003
Antti Sykäri wrote:So the main question is, should one make a programming language that will make programming fun, or one that allows for as strict static guarantees as possible?The C++ const doesn't make any strict static guarantees, that's why it's worthless for a compilers' optimizer and also why it ultimately is only an annoyance. In C++, not only can anyone cast away constness, there's not even a clear consensus on what actually *is* const. Some go for "no byte is ever changed", some go for "no outside visible state is ever changed". The latter in general makes more sense, but it implies that data members of const classes *can* change (as long as it's invisible to the rest of the world :), which in turn means that neither the compiler nor the programmer can really rely on const meaning "nothing ever changes", especially since both have their subtilities. Even a byte-by-byte const class that never changes can actually change its state if it contains a pointer to some non-const object that changes; and the other way round, a conceptually const object can change its internal storage quite often :) Add to that the several subtilities in C++ regarding const (things like the compiler implicitly converting values to const references if need be, which is useful, but somewhat awkward really) and I can very well understand why a compiler writer wouldn't see much benefit in it.Another could relying for run-time guarantees: placing the const data in a page with only read-only access, and turning hardware exceptions into D exceptions if possible. This is what C++ does (in addition to static guarantees of const) and that's my personal preference, too.This does not work with the conceptual const-ness approach, which limits the usefulness of const quite dramatically. -fg
Aug 14 2003
"Fabian Giesen" <rygNO SPAMgmx.net> a écrit dans le message de news:bhggt3$fiq$1 digitaldaemon.com...Antti Sykäri wrote:So IMO we need a way that allows us to tell the compiler that we want strict guarantees for some data...So the main question is, should one make a programming language that will make programming fun, or one that allows for as strict static guarantees as possible?The C++ const doesn't make any strict static guarantees, that's why it's worthless for a compilers' optimizer and also why it ultimately is only an annoyance.In C++, not only can anyone cast away constness, there's not even a clear consensus on what actually *is* const. Some go for "no byte is ever changed", some go for "no outside visible state is ever changed". The latter in general makes more sense, but it implies that data members of const classes *can* change (as long as it's invisible to the rest of the world :), which in turn means that neither the compiler nor the programmer can really rely on const meaning "nothing ever changes", especially since both have their subtilities. Even a byte-by-byte const class that never changes can actually change its state if it contains a pointer to some non-const object that changes; and the other way round, a conceptually const object can change its internal storage quite often :)There we need a few keyword to denote each interesting cases... We declaring data, we have a few possibilities: - compile-time constants - static constant data (always exists and with proper value) - run-time constant data (const while object exists) - non constant data We passing data as parameter, we have 3 possibilities: - we may want to allows modifications - we don't want visible (state) modification - we don't want any modification We using data, we have 4 possibilities: - the data is not const, we can modifie any way we want - the data is declared const but not physically const, can be modified with a cast - idem but the data may also be modified externally... - the data is physically const, cannot be modified even with a cast (this could include compile-time constant and could help making meta-programming easier by allowing function like syntax for integral types...) IMO in, inout and out should be strict for parameters. That is, it should not be possible to modify in parameters. We could uses "immuable" as a modifier to tell the compiler that it can assumes that the data won't be modified externally or alternatively "volatile" if the compiler should not assumes it (depending on what we prefer as the default - safer or faster) or maybe both. If immuable is supported, they we can const_cast regular in parameters... For out and inout, we don't have the problem...For simplicity, we should always allows modifications (at the outer level). In each cases, we may want to add modifier to data (and pointer member) of class type arguments... so that some member may always be modified (mutable) or never be modified (const). When declaring data, immuable would mean that the data will never be modified (physically) while const would applys to visible state. For immuable data, the compiler would be allowed to optimize construction by optimizing the constructor to minimize run-time execution by doing compile-time initialisation if possible. So in resume, my suggestion is that modifier works essentially as they do in C++ but we would have 1 more keyword to represent stricter constness and when data keyword is used, the compiler would not allows const_cast and assumes not external modifications except if the argument is declared also mutable). Some examples to help clarify the idea (this is not a complete analysis) void f1(in int a); // a is const (similar to const & in C++ - if a is modified, changes need not to propagate back but can) void f2(in immuable int a); // also physically const and const_cast not allowed... (if we don't want 1 more keyword...) void f3(in volatile int a); // can be modified externally - compiler must not optimize access void f4(out immuable int a); // cannot be modified externally void f5(inout const int a); // same as in int a but will always propagate changes back if const_cast is used... Some other combinaisons are allowed and some are prohibited... It may be possible that some keyword impies other one (immuable implies const except for out parameter (if supported with the above meaning)) Some other keyword may also be used: byref (or &) and/or byvalue. Also, we may have variable (or var) and we should also support the move-constructor idiom with another keyword (transfert) or by a predefined operator (or member)...Add to that the several subtilities in C++ regarding const (things likethecompiler implicitly converting values to const references if need be,whichis useful, but somewhat awkward really) and I can very well understand why a compiler writer wouldn't see much benefit in it.theAnother could relying for run-time guarantees: placing the const data in a page with only read-only access, and turning hardware exceptions into D exceptions if possible. This is what C++ does (in addition to static guarantees of const) and that's my personal preference, too.This does not work with the conceptual const-ness approach, which limitsusefulness of const quite dramatically. -fg
Aug 14 2003
In article <bhggt3$fiq$1 digitaldaemon.com>, Fabian Giesen wrote:Antti Sykäri wrote:There's no point arguing about the meaning of const, just look up the definition in the standard. Which in this case says: - trying to change any (member of an) object through a const-qualified access path (e.g. a pointer of type const T*) is an error - however, you can work around the type system using const_cast<> - but still, an attempt to change an const object results in undefined behavior with the exceptions of mutable members, which you in fact can change. So the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument. -A.So the main question is, should one make a programming language that will make programming fun, or one that allows for as strict static guarantees as possible?The C++ const doesn't make any strict static guarantees, that's why it's worthless for a compilers' optimizer and also why it ultimately is only an annoyance. In C++, not only can anyone cast away constness, there's not even a clear consensus on what actually *is* const. Some go for "no byte is ever changed", some go for "no outside visible state is ever changed".
Aug 15 2003
So the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.Unless someone casts your donstness away, which anyone can do without you ever knowing. Which means that const is really just a vague promise. -fg
Aug 16 2003
I would vote that it would not be possible to cast away constness... but then the problem is that if someone forget some const in a library and I uses that library, I will not be able to call those functions.. In fact, it would preferable to have const by default and having to specify it explictly when we want to allows modification (this would also apply to member functions). For inout and out parameter, the rule would be different. If there are only one implicit-indirection level, that level would not be const. Same for member functions.... Or better if it is possible would be that the default is detected by the compiler and user can add modifier if he want to force something. This could include the possibility of byref, const, variable (var), constness_of, byvalue, volatile, mutable and immuable... (in addition to existing in, inout and out). Also, we should be able to specify if null (empty, 0) is allowed and default. I think that we should at least have the attribute that we can specify for parameters of a COM method in an IDL file. When nothing is specified, value semantic would be used if the compiler cannot prove that something won't get modified by an in parameter (or by a member function call). "Fabian Giesen" <rygNO SPAMgmx.net> a écrit dans le message de news:bhmc8c$h12$1 digitaldaemon.com...So the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.Unless someone casts your donstness away, which anyone can do without you ever knowing. Which means that const is really just a vague promise. -fg
Aug 16 2003
In article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen wrote:You can be pretty certain that the callee won't cast your constness away without being sure that the object isn't actually const-qualified, since the object could be const-qualified and located in a read-only segment. If it is, trying to modify it through a non-const pointer will cause undefined behavior -- in practice, segmentation fault. -AnttiSo the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.Unless someone casts your donstness away, which anyone can do without you ever knowing. Which means that const is really just a vague promise.
Aug 17 2003
"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbjuvvn.man.jsykari pulu.hut.fi...In article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen wrote:I'm not sure a quite follow the argument here, and not all platforms seg fault they may quitely not change things (Rom'ed object on an embedded target). in all you are just justifying the point that c++ const is a meaninlessly vague promise. what it wanted is a concrete callee promise that they will not modify under any condition (the caller or other may [which is the root of the problem])You can be pretty certain that the callee won't cast your constness away without being sure that the object isn't actually const-qualified, since the object could be const-qualified and located in a read-only segment. If it is, trying to modify it through a non-const pointer will cause undefined behavior -- in practice, segmentation fault.So the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.Unless someone casts your donstness away, which anyone can do without you ever knowing. Which means that const is really just a vague promise.
Aug 17 2003
In article <bho1ia$2ro1$1 digitaldaemon.com>, Mike Wynn wrote:"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbjuvvn.man.jsykari pulu.hut.fi...It's just a question of the proportion of trust you are ready to place on the contract (or the promise) that the function declaration "f(const T*)" has. "I won't modify my argument." The const keyword is a way to state that clearly and concisely. If you would want to ban const_cast to make that a "concrete guarantee" instead of a promise, you'd have to ban unions, inline assembly, foreign function interfaces and all those good old dangerous features, because they can all be used to subvert constness. As for the "vague promise" that const keyword gives... Is it, by analogy, a "vague promise" that you will end up in jail if you commit a murder? You might get away with it (if the police don't catch you or if you happened to cast away the constness of an object that actually wasn't const) or then you might get caught (if the object actually was in a read-only segment). People don't usually go around killing other people, firstly because usually there's no particular reason to do that. Secondly, doing that might have dire consequences -- undefined behavior if you will. And you don't break the unspoken rules of a programming language either, or if you do, you face the consequences. I'm not going to give any reasons why it would be a good idea for anyone to kill someone (we already had a religious discussion about goto; no need to bring abortion, euthanasia or death penalty into the picture now), but sometimes it's needed to cast away the constness and therefore C++ has standard, working, well-documented const_cast operator for that purpose. Anyway, my point, if it didn't state it clearly enough, was that no one in their sane mind would do, for example, this: cheater.h: -------------------------- // I promise not to touch str void cheater(const char* str); -------------------------- cheater.cpp: -------------------------- void cheater(const char* str) { char* cheat = const_cast<char*>(str); // Ha-ha! A little trick, hopefully no one will notice. cheat[0] = 'x'; } -------------------------- The reason being: main.cpp: -------------------------- #include "cheater.h" #include "stdio.h" int main(int argc, char* argv[]) { if (argc < 2) return 1; const char* str; if (strcmp(argv[1], "1") == 0) { const char const_string[] = "abc"; str = const_string; } else if (strcmp(argv[1], "2") == 0) { char non_const_string[] = "abc"; str = non_const_string; } else if (strcmp(argv[1], "3") == 0) { str = "abc"; } cheater(str); printf("str = %s\n", str); } -------------------------- jsykari:~% mv a.cc main.cc jsykari:~% g++ main.cc -o main jsykari:~% ./main 1 str = xbc jsykari:~% ./main 2 str = xbc jsykari:~% ./main 3 zsh: segmentation fault ./main 3 Whoops! One of the general rules that guided the development of C++ was "Don't try to force people" (quote from D&E): "Programmers are smart people. They are engaged in challenging tasks and need all the help they can get from a programming language as well as from other supporting tools and techniques. Trying to seriously constrain programmers to do "only what is right" is inherently wrongheaded and will fail. Programmers will find a way around rules and restrictions they find unacceptable." I hope that D is not heading in a radically different direction. Oh, and by the way, same applies for the dreaded goto statement. -AnttiIn article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen wrote:I'm not sure a quite follow the argument here, and not all platforms seg fault they may quitely not change things (Rom'ed object on an embedded target). in all you are just justifying the point that c++ const is a meaninlessly vague promise. what it wanted is a concrete callee promise that they will not modify under any condition (the caller or other may [which is the root of the problem])You can be pretty certain that the callee won't cast your constness away without being sure that the object isn't actually const-qualified, since the object could be const-qualified and located in a read-only segment. If it is, trying to modify it through a non-const pointer will cause undefined behavior -- in practice, segmentation fault.So the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.Unless someone casts your donstness away, which anyone can do without you ever knowing. Which means that const is really just a vague promise.
Aug 17 2003
"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbjvjf1.pru.jsykari pulu.hut.fi..."Programmers are smart people. They are engaged in challenging tasks and need all the help they can get from a programming language as well as from other supporting tools and techniques. Trying to seriously constrain programmers to do "only what is right" is inherently wrongheaded and will fail. Programmers will find a way around rules and restrictions they find unacceptable." I hope that D is not heading in a radically different direction. Oh, and by the way, same applies for the dreaded goto statement.your whole argument would seem to me to agree that const is pointless, yet you are argueing for it ! D does not have const params, and the above statement implies that it should never have const params (or final [java]). to me your argument actually agrees with "const it just a vague promise" so effectively useless. and not all platform have exceptions on write to location 0; *((char*)0) = 'A'; can work the same as on some platforms write into read only areas just has no effect.
Aug 17 2003
Perfectly put! I agree. "Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbjvjf1.pru.jsykari pulu.hut.fi...In article <bho1ia$2ro1$1 digitaldaemon.com>, Mike Wynn wrote:away"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbjuvvn.man.jsykari pulu.hut.fi...In article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen wrote:You can be pretty certain that the callee won't cast your constnessSo the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.Unless someone casts your donstness away, which anyone can do without you ever knowing. Which means that const is really just a vague promise.sincewithout being sure that the object isn't actually const-qualified,meaninlesslythe object could be const-qualified and located in a read-only segment. If it is, trying to modify it through a non-const pointer will cause undefined behavior -- in practice, segmentation fault.I'm not sure a quite follow the argument here, and not all platforms seg fault they may quitely not change things (Rom'ed object on an embedded target). in all you are just justifying the point that c++ const is aundervague promise. what it wanted is a concrete callee promise that they will not modifyany condition (the caller or other may [which is the root of the problem])It's just a question of the proportion of trust you are ready to place on the contract (or the promise) that the function declaration "f(const T*)" has. "I won't modify my argument." The const keyword is a way to state that clearly and concisely. If you would want to ban const_cast to make that a "concrete guarantee" instead of a promise, you'd have to ban unions, inline assembly, foreign function interfaces and all those good old dangerous features, because they can all be used to subvert constness. As for the "vague promise" that const keyword gives... Is it, by analogy, a "vague promise" that you will end up in jail if you commit a murder? You might get away with it (if the police don't catch you or if you happened to cast away the constness of an object that actually wasn't const) or then you might get caught (if the object actually was in a read-only segment). People don't usually go around killing other people, firstly because usually there's no particular reason to do that. Secondly, doing that might have dire consequences -- undefined behavior if you will. And you don't break the unspoken rules of a programming language either, or if you do, you face the consequences. I'm not going to give any reasons why it would be a good idea for anyone to kill someone (we already had a religious discussion about goto; no need to bring abortion, euthanasia or death penalty into the picture now), but sometimes it's needed to cast away the constness and therefore C++ has standard, working, well-documented const_cast operator for that purpose. Anyway, my point, if it didn't state it clearly enough, was that no one in their sane mind would do, for example, this: cheater.h: -------------------------- // I promise not to touch str void cheater(const char* str); -------------------------- cheater.cpp: -------------------------- void cheater(const char* str) { char* cheat = const_cast<char*>(str); // Ha-ha! A little trick, hopefully no one will notice. cheat[0] = 'x'; } -------------------------- The reason being: main.cpp: -------------------------- #include "cheater.h" #include "stdio.h" int main(int argc, char* argv[]) { if (argc < 2) return 1; const char* str; if (strcmp(argv[1], "1") == 0) { const char const_string[] = "abc"; str = const_string; } else if (strcmp(argv[1], "2") == 0) { char non_const_string[] = "abc"; str = non_const_string; } else if (strcmp(argv[1], "3") == 0) { str = "abc"; } cheater(str); printf("str = %s\n", str); } -------------------------- jsykari:~% mv a.cc main.cc jsykari:~% g++ main.cc -o main jsykari:~% ./main 1 str = xbc jsykari:~% ./main 2 str = xbc jsykari:~% ./main 3 zsh: segmentation fault ./main 3 Whoops! One of the general rules that guided the development of C++ was "Don't try to force people" (quote from D&E): "Programmers are smart people. They are engaged in challenging tasks and need all the help they can get from a programming language as well as from other supporting tools and techniques. Trying to seriously constrain programmers to do "only what is right" is inherently wrongheaded and will fail. Programmers will find a way around rules and restrictions they find unacceptable." I hope that D is not heading in a radically different direction. Oh, and by the way, same applies for the dreaded goto statement. -Antti
Aug 18 2003
As for the "vague promise" that const keyword gives... Is it, by analogy, a "vague promise" that you will end up in jail if you commit a murder? You might get away with it (if the police don't catch you or if you happened to cast away the constness of an object that actually wasn't const) or then you might get caught (if the object actually was in a read-only segment). People don't usually go around killing other people, firstly because usually there's no particular reason to do that. Secondly, doing that might have dire consequences -- undefined behavior if you will. And you don't break the unspoken rules of a programming language either, or if you do, you face the consequences.This is beside the point of what I'm trying to say. The question is why there is a compelling need for const. I only see an use for const if it's an actual guarantee and the compiler can rely on it and act accordingly (put all const data into readonly segments, assume that anything declared const is really constant for its whole lifetime). In C++, it can't, which is why I don't see much point with doing const like it is in C++. If the language supports this kind of interface specifications, but cannot make use of it or enforce correct usage, I don't see the reason for adding the specification in the first place. That would be like adding an inout_cast to make "in" parameters inout in certain cases. I agree that such limitations can always be worked around with inline assembler or union hacks or stuff like that, but that's not of interest to me; if you have to go into the realm of undefined behavior in the first place to do something, it's perfectly okay if you get undefined behavior in return. :) As for const, that's a software design issue IMHO. If you add const, then make it rigid. Mutables are okay, because those are also part of the interface specification and thus also documented and acceptable. Functions that claim they have const objects and still modify them aren't, because in that case the interface specification mismatches the actual interface. All uses of const_cast I've seen so far in C++ code (with C++ compilers that did support mutable) were basically quick&dirty hacks because of people either not wanting to change the interface to get a non-const parameter or people not wanting to make variables mutable and suffer LONG recompile times in big projects because a central header changed. The second is purely a problem of the C/C++ compilation model and not relevant in that way to D; and the first is something a language that employs design by contract should prevent. After all, modifying a variable that's supposed to be const is a far more basic violation of the interface specification and contract than returning a wrong value in 0.1% of all cases :) -fg
Aug 18 2003
"Fabian Giesen" <rygNO SPAMgmx.net> wrote in message news:bhrn8p$251q$1 digitaldaemon.com...I don't think it needs to be a guarantee. I look at const more as a domumentation feature of language with a few good sideeffects: 1) library optimizations possible 2) support for proxy generation is more efficient (you do not need to marshal const references both ways) 3) may be some compiler optimizations too The documentation feature is the most important. I express what is my intention by placing there const. Also comments are not guarantee that the code really does what is written in them. Anyway most porgrammers think comments are good (or am I naive here?). Without const your input only reference type argument is not really input, but in-out (or something in between) and you do not have any help from compiler here at all. Full const is nice, but probably useless if compiler does not report errors/warnings on places where const should be put, but was not in the actual code ... or some other help in this matter. Programmers are too lazy.As for the "vague promise" that const keyword gives... Is it, by analogy, a "vague promise" that you will end up in jail if you commit a murder? You might get away with it (if the police don't catch you or if you happened to cast away the constness of an object that actually wasn't const) or then you might get caught (if the object actually was in a read-only segment). People don't usually go around killing other people, firstly because usually there's no particular reason to do that. Secondly, doing that might have dire consequences -- undefined behavior if you will. And you don't break the unspoken rules of a programming language either, or if you do, you face the consequences.This is beside the point of what I'm trying to say. The question is why there is a compelling need for const. I only see an use for const if it's an actual guarantee and the compiler can rely on it and act accordingly (put all const data into readonly segments, assume that anything declared const is really constant for its whole lifetime).
Aug 18 2003
I don't think it needs to be a guarantee. I look at const more as adomumentationfeature of language with a few good sideeffects: 1) library optimizations possible 2) support for proxy generation is more efficient (you do not need tomarshalconst references both ways) 3) may be some compiler optimizations too The documentation feature is the most important. I express what is myintentionby placing there const. Also comments are not guarantee that the codereallydoes what is written in them. Anyway most porgrammers think comments are good (or am I naive here?). Without const your input only reference type argument is not really input, but in-out (or something in between) and you do not have any help from compiler here at all. Full const is nice, but probably useless if compiler does not reporterrors/warningson places where const should be put, but was not in the actual code ...orsome other help in this matter. Programmers are too lazy.I agree with that... Maybe for library implementation, it could be usefull to have keyword (or attributes) that would better control what it is allowed... For ex. a function parameter might be declared "immuable" or "strict const" to tell the compiler that it cannot be modified under any circumpstance. Personnaly, I think that extra attributes (as in IDL and .NET) could be uses to write highly optimized library but would not be used in typical user program. This could be compared to some advanced optimisations in C++ with boost, move constructors, and compiler traits (is_class, is_enum, is_POD,...) that allows to make the STL more efficients.
Aug 19 2003
In article <bhrn8p$251q$1 digitaldaemon.com>, Fabian Giesen wrote:Point taken. I didn't particularly try to advocate "const" as it is in C++, just wanted to point out that while it might play a role of a vague promise to the compiler, this is not the case for the programmer (and, importantly, maintainer), who enjoys the benefits of increased type information. I'm not sure if const is at all needed. Personally I've been a happy user of const ever since I learned C++ and haven't really tried to live without const (with the exception of occasional straying to the Java side). So I'd rather trust the opinions of the more experienced programmers. Meanwhile, let us concentrate for a moment on the meaning of const. Conceptually, the const as we know it in C++ offers a read-only access to the object. If we leave basic types and structs out of the picture, and just look at classes, we can implement it by separating the class' interface into read-only interface and the rest. If the class is already accessed via virtual functions (like a proper class should), this doesn't even impose any extra costs. "Const" access to the class can be provided by explicitly returning the read-only interface of the class, and this is in fact what happens when you specify a "const T" in C++: you actually get an object of type T, but can effectively access only the subset of member functions which are explicitly marked "const". This is also an issue that needs to be taken into consideration when designing the basic container library for D. Java, a blatant mockery of static typing, solves the "const" by providing a view to the container (via java.util.Collections.unmodifiableCollection or similar) which looks exactly like the original, but throws an UnsupportedOperationException when one tries to modify it. No wonder there's a project trying to bring "const" to Java (http://pag.lcs.mit.edu/constjava/). At any case, if the language has no const and someone wants non-writable containers, (and someone definitely will), the library designer must balance between proliferation of const interfaces and the risk of run-time errors. In C++ he could just leave the complication up to the language. Oh, and then there are the basic types which are left without any protection. But I suppose one can get away with some kind of boxing. And nobody wants to pass small values by const-qualified pointers anyway, so they are kind of non-issue. As a side note, "const" was originally proposed in 1981 and was called "readonly", and there was also "writeonly". (To mirror the properties of r/w bits in the memory controller; possibly memory-mapped hardware registers) The standardization process changed "readonly" to "const" and dropped "writeonly".People don't usually go around killing other people, firstly because [ ... ]This is beside the point of what I'm trying to say. The question is why there is a compelling need for const. I only see an use for const if it's an actual guarantee and the compiler can rely on it and act accordingly (put all const data into readonly segments, assume that anything declared const is really constant for its whole lifetime).As for const, that's a software design issue IMHO. If you add const, then make it rigid. Mutables are okay, because those are also part of the interface specification and thus also documented and acceptable.So assume that we'd have these kind of strictly constant / rigidly constant / guaranteed to have the same value objects. "immutable" would not be a bad term, IMO, so I'll use it from now on. I can distantly see some consequences: - compiler could probably do some optimizations better - object of type T cannot be assigned to variable of type immutable T; to produce an immutable T you either have to clone it or have it be immutable from the beginning - there *will* be cases where the programmer wants to cast a T to immutable T because he simply knows that the object won't change. Should a simple cast like ((immutable T) t) suffice? - there would have to be methods with immutable modifier, which would comprise the immutable object's interface - plus the added complexity of overloading + other stuff that Walter has used as an argument against const All of this would complicate the language somewhat; but probably only after implementing and experimenting you would know whether it would be worth it. Are there any languages out there (except the purely functional ones) with immutable values? Now that I think of it, I remember that Daniel Yokomiso wrote an interesting description of const stuff some time ago, let's see... There, I see it's still worth reviewing: http://www.digitalmars.com/drn-bin/wwwnews?D/9962 (Daniel: how's Eon doing, by the way?) -Antti
Aug 19 2003
"Antti Sykäri" <jsykari gamma.hut.fi> a écrit dans le message de news:slrnbk5c5l.4q6.jsykari pulu.hut.fi...In article <bhrn8p$251q$1 digitaldaemon.com>, Fabian Giesen wrote:it's anPeople don't usually go around killing other people, firstly because [ ... ]This is beside the point of what I'm trying to say. The question is why there is a compelling need for const. I only see an use for const if(putactual guarantee and the compiler can rely on it and act accordinglyconstall const data into readonly segments, assume that anything declaredI do think that we need some kind of const access weither it is usefull or not for the compiler. It is usefull for the programmer. Making it strictier would make it even more usefull but library writers would have to do it right!is really constant for its whole lifetime).Point taken. I didn't particularly try to advocate "const" as it is in C++, just wanted to point out that while it might play a role of a vague promise to the compiler, this is not the case for the programmer (and, importantly, maintainer), who enjoys the benefits of increased type information. I'm not sure if const is at all needed. Personally I've been a happy user of const ever since I learned C++ and haven't really tried to live without const (with the exception of occasional straying to the Java side). So I'd rather trust the opinions of the more experienced programmers. Meanwhile, let us concentrate for a moment on the meaning of const.So your definition of immuable that an object won't changes... but for a function parameter, does it apply to both the source and target or only to the target (as const in C++).As for const, that's a software design issue IMHO. If you add const, then make it rigid. Mutables are okay, because those are also part of the interface specification and thus also documented and acceptable.So assume that we'd have these kind of strictly constant / rigidly constant / guaranteed to have the same value objects. "immutable" would not be a bad term, IMO, so I'll use it from now on. I can distantly see some consequences: - compiler could probably do some optimizations better - object of type T cannot be assigned to variable of type immutable T; to produce an immutable T you either have to clone it or have it be immutable from the beginning- there *will* be cases where the programmer wants to cast a T to immutable T because he simply knows that the object won't change. Should a simple cast like ((immutable T) t) suffice? - there would have to be methods with immutable modifier, which would comprise the immutable object's interface - plus the added complexity of overloading + other stuff that Walter has used as an argument against constFor overloadding, we should then have a way to reuse the same code for all variations if applicable. typical case would be to be able to select the appropriate constness of the return value of a member function... I think that the compiler could automatically make the result const if this is required (for ex. when returning an element from a const container). class E; class C { E &get() { return member; } E member; } Here if get is called on a const C, the compiler could add const to E if otherwise the function would compile if it were const. Doing it that way would avoid most of the complexity of overloadding in C++ where we need to provide both a const and a regular version of the same function. Also, the programmer won't be required to put all const... The compiler would be able to do some analysis to verify if a function can be generated. And with prohibited keyword that I suggest in another thread, one would be able to prohibit using the const version if he wants (for example for a swap function, we don't want const parameter and prohibiting it would do that the compiler won't have to continue analysis...) I would opt for smart constness and a few modifier when they are required. I would add prohibited and immuable and borrow const and mutable from C++... prohibited would be a function modifier that will ensure that the function is never called. immuable would be a parameter modifier that would be more strict than const (no const_cast possible)All of this would complicate the language somewhat; but probably only after implementing and experimenting you would know whether it would be worth it.-Antti
Aug 19 2003
In article <bhug4k$78v$1 digitaldaemon.com>, Philippe Mori wrote:What do you mean by source and target in this context? Immutable's just an object that will never change after it has been initialized.So your definition of immuable that an object won't changes... but for a function parameter, does it apply to both the source and target or only to the target (as const in C++).As for const, that's a software design issue IMHO. If you add const, then make it rigid. Mutables are okay, because those are also part of the interface specification and thus also documented and acceptable.So assume that we'd have these kind of strictly constant / rigidly constant / guaranteed to have the same value objects. "immutable" would not be a bad term, IMO, so I'll use it from now on. I can distantly see some consequences: - compiler could probably do some optimizations better - object of type T cannot be assigned to variable of type immutable T; to produce an immutable T you either have to clone it or have it be immutable from the beginningCan you elaborate your ideas a bit? I'd be interested to see an example which would show that your idea works in practice. IMO, Autogenerating methods at compile-time where "const" needed seems complex and it might cause some overhead. For example, class interfaces would need to have const version for each method. For example, what if your example was derived for an interface: class C; interface C_Interface { E get(); // Compiler must autogenerate this in order to any derived C to work:: const E get() const; } class C : C_Interface { E get() { return member; } // Compiler needs to generate E get() { return member; } E member; } class C2 : C_Interface { E get() { return new E(); } // This one will always non-const E always, but how does the client // know that if it accesses a C_Interface object? // Back to the drawing board - need to add "E get() const;" function // to C_Interface. Too much bloat }- there *will* be cases where the programmer wants to cast a T to immutable T because he simply knows that the object won't change. Should a simple cast like ((immutable T) t) suffice? - there would have to be methods with immutable modifier, which would comprise the immutable object's interface - plus the added complexity of overloading + other stuff that Walter has used as an argument against constFor overloadding, we should then have a way to reuse the same code for all variations if applicable. typical case would be to be able to select the appropriate constness of the return value of a member function... I think that the compiler could automatically make the result const if this is required (for ex. when returning an element from a const container). class E; class C { E &get() { return member; } E member; } Here if get is called on a const C, the compiler could add const to E if otherwise the function would compile if it were const. Doing it that way would avoid most of the complexity of overloadding in C++ where we need to provide both a const and a regular version of the same function. Also, the programmer won't be required to put all const... The compiler would be able to do some analysis to verify if a function can be generated.And with prohibited keyword that I suggest in another thread, one would be able to prohibit using the const version if he wants (for example for a swap function, we don't want const parameter and prohibiting it would do that the compiler won't have to continue analysis...)I still don't see much use for the "prohibit" keyword. It would mostly be useful if the compiler would auto-generate a lof of functions (such as copy constructors, assignment operators in C++) and there would be a need for not having them.prohibited would be a function modifier that will ensure that the function is never called.Then why put the function there in the first place? :) -A
Aug 23 2003
Suppose something similar to this: void f(int &target) { ... } int source = 1; f(source); The source is from where the data come... Can source be modified externally (other thread, alias, while f is executing. Target is what we do inside the function... Maybe not the best terms... It is the perspective from either side (i.e. inside the function, can I assumes that the received object is constant and do some optimisation in consequence, does I have the possibility to modify the source object (possibly with a cast, or the object is really const (unmodifiable) or a copy.... So one might like to tell that the source cannot change (even externally) and another one might tell that target won't change...So your definition of immuable that an object won't changes... but for a function parameter, does it apply to both the source and target or only to the target (as const in C++).What do you mean by source and target in this context? Immutable's just an object that will never change after it has been initialized.I think that the compiler could automatically make the result const if this is required (for ex. when returning an element from a const container).generated.Also, the programmer won't be required to put all const... The compiler would be able to do some analysis to verify if a function can beCan you elaborate your ideas a bit? I'd be interested to see an example which would show that your idea works in practice. IMO, Autogenerating methods at compile-time where "const" needed seems complex and it might cause some overhead. For example, class interfaces would need to have const version for each method. For example, what if your example was derived for an interface:OK, I see my error... Essentially, it would only be possible to do such analysis for final method.... For all other method, it does not works well. In fact, const is most appropriate for concrete (final) and simple type that can be checked at compile-time... and maybe we should support const only for those types (so that for ex. if we pass a constant container (that would be a concrete type), we would be able to prevent modification to the container... This also means that for templates like a container, we want a concrete class that is final (no possible override)...beAnd with prohibited keyword that I suggest in another thread, one wouldswapable to prohibit using the const version if he wants (for example for afunctionfunction, we don't want const parameter and prohibiting it would do that the compiler won't have to continue analysis...)I still don't see much use for the "prohibit" keyword. It would mostly be useful if the compiler would auto-generate a lof of functions (such as copy constructors, assignment operators in C++) and there would be a need for not having them.prohibited would be a function modifier that will ensure that theI see a few cases: - The functions is implictly generated - There are some implicit conversion that are allowed but does not give the desired result (for ex. in C++ std::string class can be initialized from 0.0 or false because of implicit conversion that occurs but are not desirable in that specific context). In practice, if we find such a bug, then we have an easy way to ensure we won't have it another time. - One existing function become obsolete and we want to ensure that it is not used anymore but because there are other similar overload we like the compiler to trap them and issue an error. A keyword allows to make it clearer... Otherwise it is possible to do it with templates but it is more complicated... - In generic (template) code, we might know some undesired types for arguments (like swapping one constant object or swapping objects of distincts type). If we have a prohibited keyword, it is a lot easier to prohibit what we don't want that to make code that would causes a compilation error in those cases... I have done it (preventing code from compiling) a lot of times in C++ when I was refactoring the code that have known problems. I was using the compiler to help me find those problematic places...is never called.Then why put the function there in the first place? :)
Aug 23 2003
In article <bi9df5$1bh7$1 digitaldaemon.com>, Philippe Mori wrote:If it's immutable, then it cannot. If it's mutable, like in your example, it can. This also means that there is an fundamental incompatibility between mutable and immutable objects: one cannot be converted to the other, at least not without violating the basic rules of the language. (References changed to inout to look more like D. After all, this is a D newsgroup...) void f(inout immutable int); int source = 1; f(source); // compile-time error: can't convert int to immutable int If you could do the conversion, you might be able to change the object in another thread or via aliasing, and it would change without f() knowing. Which would make immutable the same as C++'s const, and prevent the precious compiler optimizations that we're trying to achieve here (or, are we? I actually doubt that they'd be so significant)Suppose something similar to this: void f(int &target) { ... } int source = 1; f(source); The source is from where the data come... Can source be modified externally (other thread, alias, while f is executing.So your definition of immuable that an object won't changes... but for a function parameter, does it apply to both the source and target or only to the target (as const in C++).What do you mean by source and target in this context? Immutable's just an object that will never change after it has been initialized.I guess there aren't implicitly generated functions in D currently (I might be wrong?)I see a few cases: - The functions is implictly generatedprohibited would be a function modifier that will ensure that the function is never called.Then why put the function there in the first place? :)- There are some implicit conversion that are allowed but does not give the desired result (for ex. in C++ std::string class can be initialized from 0.0 or false because of implicit conversion that occurs but are not desirable in that specific context). In practice, if we find such a bug, then we have an easy way to ensure we won't have it another time.I think D doesn't have as many opportunities to shoot oneself in the foot as C++, particularly with respect to implicit conversions... Then again, maybe there is someone to know better.- One existing function become obsolete and we want to ensure that it is not used anymore but because there are other similar overload we like the compiler to trap them and issue an error. A keyword allows to make it clearer... Otherwise it is possible to do it with templates but it is more complicated...D has deprecated: http://www.digitalmars.com/d/attribute.html- In generic (template) code, we might know some undesired types for arguments (like swapping one constant object or swapping objects of distincts type). If we have a prohibited keyword, it is a lot easier to prohibit what we don't want that to make code that would causes a compilation error in those cases...You might be wanting to abuse the deprecated (or private) qualifiers to do that kind of prohibiting. All of those keywords would play partly the same role, which isn't exactly orthogonal language design. I'd rather go in the direction of general compile-time error message which can have a lot more uses than just prohibiting instantiation of a certain function. class X { } template (Foo) { // your general template code goes here } template (Foo : X) { compileTimeError("You shouldn't instantiate Foo with types derived " "from X"); } Upon finding an "instance Foo(X)" the compiler would blurt out something like line 567: Cannot instantiate Foo(X) line 123: compileTimeError: You shouldn't instantiate Foo with types derived from XI have done it (preventing code from compiling) a lot of times in C++ when I was refactoring the code that have known problems. I was using the compiler to help me find those problematic places...If you can find an example that isn't solved by other features in D, people are likely to be interested in seeing it. -Antti
Aug 24 2003
If it's immutable, then it cannot. If it's mutable, like in your example, it can. This also means that there is an fundamental incompatibility between mutable and immutable objects: one cannot be converted to the other, at least not without violating the basic rules of the language. (References changed to inout to look more like D. After all, this is a D newsgroup...) void f(inout immutable int); int source = 1; f(source); // compile-time error: can't convert int to immutable intIf I want to uses an in parameter, does I have the choice between by value which I think is the default and by reference... So it would be more like void f(in byref immutable int) // More D style syntax or void f (in immutable & int the_param) // More like C++ To tell the compiler that I do not want to modify the_param and that it can assumes that it won't be modified externally.If you could do the conversion, you might be able to change the object in another thread or via aliasing, and it would change without f() knowing. Which would make immutable the same as C++'s const, and prevent the precious compiler optimizations that we're trying to achieve here (or, are we? I actually doubt that they'd be so significant)I guess there aren't implicitly generated functions in D currently (I might be wrong?)But does it is possible to control in which module or on some other condition when deprecated take effects. Maybe I am using 2 libraries that has some deprecated functions but I still want to uses them in the first one (the code depends a lot on them) but not on the second one... Also if the attribute can be depedant on other things (probably on versioning), then it might be possible to uses version for that purpose...- There are some implicit conversion that are allowed but does not give the desired result (for ex. in C++ std::string class can be initialized from 0.0 or false because of implicit conversion that occurs but are not desirable in that specific context). In practice, if we find such a bug, then we have an easy way to ensure we won't have it another time.I think D doesn't have as many opportunities to shoot oneself in the foot as C++, particularly with respect to implicit conversions... Then again, maybe there is someone to know better.- One existing function become obsolete and we want to ensure that it is not used anymore but because there are other similar overload we like the compiler to trap them and issue an error. A keyword allows to make it clearer... Otherwise it is possible to do it with templates but it is more complicated...D has deprecated: http://www.digitalmars.com/d/attribute.htmllike- In generic (template) code, we might know some undesired types for arguments (like swapping one constant object or swapping objects of distincts type). If we have a prohibited keyword, it is a lot easier to prohibit what we don't want that to make code that would causes a compilation error in those cases...You might be wanting to abuse the deprecated (or private) qualifiers to do that kind of prohibiting. All of those keywords would play partly the same role, which isn't exactly orthogonal language design. I'd rather go in the direction of general compile-time error message which can have a lot more uses than just prohibiting instantiation of a certain function. class X { } template (Foo) { // your general template code goes here } template (Foo : X) { compileTimeError("You shouldn't instantiate Foo with types derived " "from X"); } Upon finding an "instance Foo(X)" the compiler would blurt out somethingline 567: Cannot instantiate Foo(X) line 123: compileTimeError: You shouldn't instantiate Foo with types derived from XCompile time error is a must... This allows to check many constraints that are not directly supported by the language (like your example). This can be used to validate some constants to ensure no accidental changes would be done. This include some magic numbers, the size of a structure (it's alignment, member offset,...) and we should be able also to check some properties of classes and struct like: - Is it a POD type, auto type, class type, enum type,... - Is it modifiable - Does it have a given member or support a given operation (member function, copy constructor, default constructor, operator +, comparison, equality,...) - Is if final, abstract,... - Is it an interface (or a COM compatible interface)I haven't yet tried the D compiler but I think that it might be interesting to check what can be done in D form the following source (and others): - Modern C++ Design (book) & Loki - C++ Templates (book) - STL - boost - ATL - Xena ... We would then be able to compare the power and the simplicity of the language for some concrete things... and if something cannot be done directly, how hard it is to be done in D (if he can be done) or is it necessary (or we have better alternatives). It would also be interesting to check from some of well known book like Effective C++, Effective STL, and others that give some rules how to uses C++ more safely, how D compare (avoid problem at least implicitly while retaining enough power to be usefull...)I have done it (preventing code from compiling) a lot of times in C++ when I was refactoring the code that have known problems. I was using the compiler to help me find those problematic places...If you can find an example that isn't solved by other features in D, people are likely to be interested in seeing it.
Aug 25 2003
In article <bid8ts$12qg$1 digitaldaemon.com>, Philippe Mori wrote:Whoops :) Of course immutable objects would be "in" since they cannot be changed. So I'd say: void f(in immutable int); or void f(in immutable Big_Object); Were this legal D, the compiler would be free to pick either by value or by reference (whichever it would think to be more efficient) -- the semantics wouldn't change. If it were const, the semantics _would_ change; consider int f(inout const int x, inout y) { y++; return x; } void main() { int a = 1; printf("%d\n", f(a, a)); } If f gets y and x by reference, main() will print 2, and if by value, it will print 1. This is one of the places where const can deceive. D doesn't specify by-reference or by-value, instead it relies on in/out/inout, as mentioned in the "Functions" section.(References changed to inout to look more like D. After all, this is a D newsgroup...) void f(inout immutable int); int source = 1; f(source); // compile-time error: can't convert int to immutable intIf I want to uses an in parameter, does I have the choice between by value which I think is the default and by reference... So it would be more like void f(in byref immutable int) // More D style syntaxSounds like a quality of implementation issue; DMD has option: -d allow deprecated features I'd say that in this case, the answer is "yes" if you build the two projects separately. -AnttiD has deprecated: http://www.digitalmars.com/d/attribute.htmlBut does it is possible to control in which module or on some other condition when deprecated take effects. Maybe I am using 2 libraries that has some deprecated functions but I still want to uses them in the first one (the code depends a lot on them) but not on the second one...
Aug 25 2003
"Philippe Mori" <philippe_mori hotmail.com> wrote in message news:bid8ts$12qg$1 digitaldaemon.com...No. It applies to any use of a function.D has deprecated: http://www.digitalmars.com/d/attribute.htmlBut does it is possible to control in which module or on some other condition when deprecated take effects.Also if the attribute can be depedant on other things (probably on versioning), then it might be possible to uses version for that purpose...That might work.This is now done with a new feature, static asserts.class X { } template (Foo) { // your general template code goes here } template (Foo : X) { compileTimeError("You shouldn't instantiate Foo with types derived " "from X"); } Upon finding an "instance Foo(X)" the compiler would blurt out somethinglikeline 567: Cannot instantiate Foo(X) line 123: compileTimeError: You shouldn't instantiate Foo with types derived from XCompile time error is a must... This allows to check many constraints that are not directly supported by the language (like your example). This can be used to validate some constants to ensure no accidental changes would be done. This include some magic numbers, the size of a structure (it's alignment, member offset,...) and we should be able also to check some properties of classes and struct like:
Sep 19 2003
I'm a heavy const user, a true believer in it, I'd say. I'm going to throw out some ideas: How about adding another kind of access specifier, starting with readonly, such that: class A { private T t; // private data member readonly public: // readonly section, also public. this section applies to both rvalues and lvalues of class A T GetT() { return t; } readwrite: // go back to readwrite, still public. This section applies to lvalues this(in T t_) { t = t_; } T SetT(T other) { t = other; return this; } } A couple things I can see from the above: It adds complexity to initialization. I recommend just ignoring all readonly accessors during a constructor, and maybe in the destructor too, or in something obviously lvalue such as an assignment operator. You can avoid having to constantly declare member functions const. Just put it in its own section. This would save the "extra typing" overhead of C++ const quite a bit. readonly and readwrite are a different set of attribute modifiers, totally separate from private public and protected. Orthogonal to. Unrelated. So readonly sections can overlap public/private sections, or be overlapped by them. readonly and readwrite might belong in the same category as mutable. Public, private, protected control the visibility of names to everything outside the class. readonly and readwrite control how the class sees itself (readonly interface, or readonly + readwrite interface) in those member functions, and controls assignability of fields. in, out, and inout control readwrite access to parameters of those functions, and const declares individual data rvalues, which aren't even castable to anything with write access. That's a lot of names for one basic concept (readonly-ness), so perhaps it could all just be called const, or (lack of const) since the usage situation would already make it unambiguous what you meant. This readonly, for the builtin datatypes, and fundamentally, removes the ability to use the assignment operator on such values, or call anything else that's a readwrite member function, on that type. It should also prevent deletion or other forms of destruction/finalization. And the compiler should be able to treat copies of such a value made during periods in which it is sure are all protected by that attribute, that they remain identical so long as it knows the readonly attribute is attached to them. When it becomes unsure, it could save and reload it to make sure any changes are reflected, but I'd rather guarantee that nothing will violate the constraint, ever, during the lifetime of the object or data. The compiler should actively prevent the user from modifying such readonly values or allowing them to be modified whenever possible. If that's too restrictive, use something like mutable, which has different behavior in different situations. I would like to have a kind of constructor that basically allowed the caller to put whatever values they want into the class's fields, by name: this(in); // how would you specify that you want the compiler to generate a method for you, without having to specify any body? // By using prototype only, no body, kinda like C++? It would generate a method for you regardless in C++, but this way you get to control the access. I'd use it like so: class Point { float x,y,z; this(in); // allow construction with the caller being able to initialize the public fields and base class more or less directly this(float x_, float y_, float z_) { x = x_, y = y_, z = z_; } // without that, you have to do this: } new Point a(x:1.0, y:2.0, z:0.0); // call this(in) with initialization for these members new Point a(1.0, 2.0, 0.0); // initialize fields by calling this(float,float,float) or new Point a = { x:1.0; y:2.0, z:0.0 }; // same as this, which I think D allows now? D does named field initialization, right? Maybe structs just automatically work that way. things. Groups of attributes (private/public/protected would be a group, and so would readonly/readwrite) declared sort of like enums. Unlike anything I've heard of before, you should be able to alias and aggregate these attributes together (a global "alias private readonly notevenIcantouchit;" or "alias public mutable youcanlookbutyoucanttouch"). If you could hook a couple prologue/epilog functions (inlined before and after every function body) to these attributes, then use them to control code generation the same way that contracts work now. Even better, you could use such attributes to *implement* things like contracts. Think about it. What if you declared something akin to an in and out contract yourself? attribute in_custom in { print("in"); assert(this->good()); } attribute out_custom out { print("out"); assert(this->good()); } alias attribute in_custom out_custom invariant_custom; class Sign { invariant_custom: private int x; public this() { x = 0; } bit good() { return x >= 0 && x <= 10; } void badfunction() { ++x; badfunction(); } // will recurse 10 times and then trigger an assert exception! } The compiler would inject the attribute code into each function declared after it class Foo { attribute { my_read assign { assert(false && "don't assign to my_read objects, dumbass!"); } default my_readwrite { } }; // everything up to this point is my_readwrite because it's the default! my_read int x; // if you try to assign this, you'll assert! my_readwrite: // everything else goes here } So there are a bunch of basic "kinds" of modifiers such as in, out, invariant, assign, destroy, copy, create, that sort of thing. To control what situations the code attached to the attribute would apply in. Also these attributes shouldn't run until the constructor for the object has finished, and shouldn't run once the finalizer starts. That way it's guaranteed to have a complete object to deal with. I don't think it would be a good idea to allow decorating member variables with code. Or would it? A member variable or data attribute would control things like assignment, reference, and might serve as an easy way to do properties with a write-through or read-only data field. (The properties in the D spec now may never see the light of day!) Maybe there should be some kind of compile time error for when the compiler can detect at compile time that a code path is going to definitely be executed that contains an assert statement (such as the assign assert above) and issue a compile time error. I guess all this adds to the complexity of the compiler. That's a good thing, in some ways. This could be powerful stuff in the right hands. Maybe could be combined with templates somehow? It's really just the ability to add custom extra type information to your types. One of the major costs of maintenance is having code cut-and-pasted in a bunch of places, as it is hard to change something if it's scattered all over the place. With attributes you can write boilerplate code and attach it to things with an attribute in its declaration. This is good because it keeps such code in one place, in the attribute definition, rather than cut and pasted into every copy of every function that needs it. In C / C++ you have to use #define macros for this, which reduces the size of such copied code, but doesn't eliminate the problem that it has to be copied in the first place, (and subsequently maintained!). These kind of user definable attributes partially solve that problem, because you can declare declarations in blocks to all be the same attribute. Damn! It's getting late! I better rein this in! Sean "Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbk5c5l.4q6.jsykari pulu.hut.fi...In article <bhrn8p$251q$1 digitaldaemon.com>, Fabian Giesen wrote:it's anPeople don't usually go around killing other people, firstly because [ ... ]This is beside the point of what I'm trying to say. The question is why there is a compelling need for const. I only see an use for const if(putactual guarantee and the compiler can rely on it and act accordinglyconstall const data into readonly segments, assume that anything declaredis really constant for its whole lifetime).Point taken. I didn't particularly try to advocate "const" as it is in C++, just wanted to point out that while it might play a role of a vague promise to the compiler, this is not the case for the programmer (and, importantly, maintainer), who enjoys the benefits of increased type information. I'm not sure if const is at all needed. Personally I've been a happy user of const ever since I learned C++ and haven't really tried to live without const (with the exception of occasional straying to the Java side). So I'd rather trust the opinions of the more experienced programmers. Meanwhile, let us concentrate for a moment on the meaning of const. Conceptually, the const as we know it in C++ offers a read-only access to the object. If we leave basic types and structs out of the picture, and just look at classes, we can implement it by separating the class' interface into read-only interface and the rest. If the class is already accessed via virtual functions (like a proper class should), this doesn't even impose any extra costs. "Const" access to the class can be provided by explicitly returning the read-only interface of the class, and this is in fact what happens when you specify a "const T" in C++: you actually get an object of type T, but can effectively access only the subset of member functions which are explicitly marked "const". This is also an issue that needs to be taken into consideration when designing the basic container library for D. Java, a blatant mockery of static typing, solves the "const" by providing a view to the container (via java.util.Collections.unmodifiableCollection or similar) which looks exactly like the original, but throws an UnsupportedOperationException when one tries to modify it. No wonder there's a project trying to bring "const" to Java (http://pag.lcs.mit.edu/constjava/). At any case, if the language has no const and someone wants non-writable containers, (and someone definitely will), the library designer must balance between proliferation of const interfaces and the risk of run-time errors. In C++ he could just leave the complication up to the language. Oh, and then there are the basic types which are left without any protection. But I suppose one can get away with some kind of boxing. And nobody wants to pass small values by const-qualified pointers anyway, so they are kind of non-issue. As a side note, "const" was originally proposed in 1981 and was called "readonly", and there was also "writeonly". (To mirror the properties of r/w bits in the memory controller; possibly memory-mapped hardware registers) The standardization process changed "readonly" to "const" and dropped "writeonly".As for const, that's a software design issue IMHO. If you add const, then make it rigid. Mutables are okay, because those are also part of the interface specification and thus also documented and acceptable.So assume that we'd have these kind of strictly constant / rigidly constant / guaranteed to have the same value objects. "immutable" would not be a bad term, IMO, so I'll use it from now on. I can distantly see some consequences: - compiler could probably do some optimizations better - object of type T cannot be assigned to variable of type immutable T; to produce an immutable T you either have to clone it or have it be immutable from the beginning - there *will* be cases where the programmer wants to cast a T to immutable T because he simply knows that the object won't change. Should a simple cast like ((immutable T) t) suffice? - there would have to be methods with immutable modifier, which would comprise the immutable object's interface - plus the added complexity of overloading + other stuff that Walter has used as an argument against const All of this would complicate the language somewhat; but probably only after implementing and experimenting you would know whether it would be worth it. Are there any languages out there (except the purely functional ones) with immutable values? Now that I think of it, I remember that Daniel Yokomiso wrote an interesting description of const stuff some time ago, let's see... There, I see it's still worth reviewing: http://www.digitalmars.com/drn-bin/wwwnews?D/9962 (Daniel: how's Eon doing, by the way?) -Antti
Aug 20 2003
I'm a heavy const user, a true believer in it, I'd say. I'm going tothrowout some ideas:I'm also such an user of const...How about adding another kind of access specifier, starting with readonly, such that: class A { private T t; // private data member readonly public: // readonly section, also public. this section applies to both rvalues and lvalues of class A T GetT() { return t; } readwrite: // go back to readwrite, still public. This sectionappliesto lvalues this(in T t_) { t = t_; } T SetT(T other) { t = other; return this; } } A couple things I can see from the above: It adds complexity to initialization. I recommend just ignoring all readonly accessors during a constructor, and maybe in the destructor too,orin something obviously lvalue such as an assignment operator.If something is read-only, IMO, assignment should be prohibited by default and if defined by the user, the user will be able only to assign to readwrite members...You can avoid having to constantly declare member functions const. Justputit in its own section. This would save the "extra typing" overhead of C++ const quite a bit.I would even better like that when const is not specified that the function would be const candidate provide that it would have compiled if const was added. And we uses mutable to explictly tell that a member function is not const and cannot be used on const object even if it would have compiled if const was added. Note that for the return value, when a function is not decorated with a modifier the return value would become const if necessary (the compiler would also remember where const was implicitly added to display usefull error message to the user. Note that code that is compiled for a dynamic library, we should either always specify completly modifier (this would prevent accidental changes) or the compiler should remember the possibilities... Or we could have a keyword for automatic constness (autoconst) that would allows the compiler to decide...readonly and readwrite are a different set of attribute modifiers, totally separate from private public and protected. Orthogonal to. Unrelated.Soreadonly sections can overlap public/private sections, or be overlapped by them. readonly and readwrite might belong in the same category asmutable.We could also have some more specifier that would be usefull (static, prohibited, published, exported, designtime, runtime, debug, final., inline, unline..) - static would make member static - prohibited would prevent uses (from everywhere - related to access) - published is public and visible by browsing tool (may be related to access) - exported would ensure the item is exported in a dynamic library (DLL) - designtime, runtime would indicate a property available for design or execution, - final would make methods non-virtual -...Public, private, protected control the visibility of names to everything outside the class. readonly and readwrite control how the class seesitself(readonly interface, or readonly + readwrite interface) in those member functions, and controls assignability of fields. in, out, and inoutcontrolreadwrite access to parameters of those functions, and const declares individual data rvalues, which aren't even castable to anything with write access. That's a lot of names for one basic concept (readonly-ness), so perhaps it could all just be called const, or (lack of const) since the usage situation would already make it unambiguous what you meant. This readonly, for the builtin datatypes, and fundamentally, removes the ability to use the assignment operator on such values, or call anythingelsethat's a readwrite member function, on that type. It should also prevent deletion or other forms of destruction/finalization. And the compiler should be able to treat copies of such a value made during periods inwhichit is sure are all protected by that attribute, that they remain identical so long as it knows the readonly attribute is attached to them. When it becomes unsure, it could save and reload it to make sure any changes are reflected, but I'd rather guarantee that nothing will violate the constraint, ever, during the lifetime of the object or data. The compiler should actively prevent the user from modifying such readonly values or allowing them to be modified whenever possible. If that's toorestrictive,use something like mutable, which has different behavior in different situations.In fact, using some more keywords (or attributes), we would be able to better control what we do... Some keywords that could be usefull are immuable, const, mutable, var(iable), compiletime, byref, byvalue,... Also, as mentionned in this thread, some modifier could be attributes... We would have a bunch of attributes that would give us more control when it is usefull...I would like to have a kind of constructor that basically allowed thecallerto put whatever values they want into the class's fields, by name: this(in); // how would you specify that you want the compiler to generate a method for you, without having to specify any body? // By using prototype only, no body, kinda likeC++?It would generate a method for you regardless in C++, but this way you get to control the access. I'd use it like so: class Point { float x,y,z; this(in); // allow construction with the caller being able to initialize the public fields and base class more or less directlymaybe, we should have a syntax like pure virtual function: this(in) = default;this(float x_, float y_, float z_) { x = x_, y = y_, z = z_; } // without that, you have to do this: } new Point a(x:1.0, y:2.0, z:0.0); // call this(in) with initializationforthese membersAnd initialize other to default? If so, this could be nice...new Point a(1.0, 2.0, 0.0); // initialize fields by calling this(float,float,float) or new Point a = { x:1.0; y:2.0, z:0.0 }; // same as this, which I think D allows now? D does named field initialization, right?Also what would be interesting is an uninitialized keyword so that we can created unitialized object explicitly when performance matters...Maybe structs just automatically work that way. things. Groups of attributes (private/public/protected would be a group, and so would readonly/readwrite) declared sort of like enums. Unlike anything I've heard of before, you should be able to alias and aggregate these attributes together (a global "alias private readonly notevenIcantouchit;" or "alias public mutable youcanlookbutyoucanttouch"). If you could hook a couple prologue/epilog functions (inlined before and after every function body) to these attributes, then use them to control code generation the same way that contracts work now.I really like this one too... and it would be even more interesting if some attributes can be different for debug/release, design/runtime,... so that for example, we can have debug functions that are not available in release version (for implementation, it might be a "pure virtual function called" when not available or something like that so that it would be easier to handled partial recompilations...Even better, you could use such attributes to *implement* things like contracts. Think about it. What if you declared something akin to an in and out contract yourself? attribute in_custom in { print("in"); assert(this->good()); } attribute out_custom out { print("out"); assert(this->good()); } alias attribute in_custom out_custom invariant_custom; class Sign { invariant_custom: private int x; public this() { x = 0; } bit good() { return x >= 0 && x <= 10; } void badfunction() { ++x; badfunction(); } // will recurse 10timesand then trigger an assert exception! } The compiler would inject the attribute code into each function declared after it class Foo { attribute { my_read assign { assert(false && "don't assign to my_read objects, dumbass!"); } default my_readwrite { } }; // everything up to this point is my_readwrite because it's the default! my_read int x; // if you try to assign this, you'll assert! my_readwrite: // everything else goes here }I would then like to ba able to associate atributes also to classes and parameters... A great thing that may be possible is to implement a tracing facilities that would output all functions that are called with the value of their parameters.... I think this is possible with .NET... We just need to be able to get all parameters type and value...So there are a bunch of basic "kinds" of modifiers such as in, out, invariant, assign, destroy, copy, create, that sort of thing. To control what situations the code attached to the attribute would apply in. Also these attributes shouldn't run until the constructor for the object has finished, and shouldn't run once the finalizer starts. That way it's guaranteed to have a complete object to deal with. I don't think it would be a good idea to allow decorating member variables with code. Or would it? A member variable or data attribute wouldcontrolthings like assignment, reference, and might serve as an easy way to do properties with a write-through or read-only data field. (The propertiesinthe D spec now may never see the light of day!) Maybe there should be some kind of compile time error for when thecompilercan detect at compile time that a code path is going to definitely be executed that contains an assert statement (such as the assign assertabove)and issue a compile time error.I would suggest a keyword (or predefined special function) that would be used instead of assert in such cases... And also one for functions that are not yet implemented or code that is not completed (todo, bug, ...) maybe compile_time_state(a) where a is an identifier and we can tell the compiler which identifier are ignored, which one causes a run-time error and which causes a compilation error.I guess all this adds to the complexity of the compiler. That's a good thing, in some ways. This could be powerful stuff in the right hands. Maybe could be combined with templates somehow?I don't the relashion with template... but it may indeed be interesting to be able to do some metaprogramming based on those attributes.It's really just the ability to add custom extra type information to your types. One of the major costs of maintenance is having code cut-and-pasted in a bunch of places, as it is hard to change something if it's scattered all over the place. With attributes you can write boilerplate code and attach it to things with an attribute in its declaration. This is good becauseitkeeps such code in one place, in the attribute definition, rather than cut and pasted into every copy of every function that needs it. In C / C++youhave to use #define macros for this, which reduces the size of such copied code, but doesn't eliminate the problem that it has to be copied in the first place, (and subsequently maintained!). These kind of user definable attributes partially solve that problem, because you can declare declarations in blocks to all be the same attribute. Damn! It's getting late! I better rein this in! SeanI would like that the compiler would have a mode in which it case do some extended analysis of the code and potential pitfalls...
Aug 20 2003
"Philippe Mori" <philippe_mori hotmail.com> wrote in message news:bi0eoa$2uk4$1 digitaldaemon.com...too,It adds complexity to initialization. I recommend just ignoring all readonly accessors during a constructor, and maybe in the destructorHmm. Anything that's not writable has to be initialized explicitly, which causes some order of construction issues, especially if field initializers reference other fields' values.or in something obviously lvalue such as an assignment operator.If something is read-only, IMO, assignment should be prohibited by default and if defined by the user, the user will be able only to assign to readwrite members...C++You can avoid having to constantly declare member functions const. Justputit in its own section. This would save the "extra typing" overhead offunctionconst quite a bit.I would even better like that when const is not specified that thewould be const candidate provide that it would have compiled if const was added. And we uses mutable to explictly tell that a member function is not const and cannot be used on const object even if it would have compiled if const was added.This is just making it easier for people to be lazy. ;)Note that for the return value, when a function is not decorated with a modifier the return value would become const if necessary (the compiler would also remember where const was implicitly added to display usefull error message to the user.It should be easy to add modifiers to a piece of data and hard/impossible to take them away (without making a new copy of the data/object)Note that code that is compiled for a dynamic library, we should either always specify completly modifier (this would prevent accidental changes) or the compiler should remember the possibilities...I haven't thought it through that far. Not concerned with DLL's at this stage. ;)Or we could have a keyword for automatic constness (autoconst) that would allows the compiler to decide...Hmm. People would forget the autoconst keyword. ;)We could also have some more specifier that would be usefull (static, prohibited, published, exported, designtime, runtime, debug, final.,inline,unline..) - static would make member static - prohibited would prevent uses (from everywhere - related to access) - published is public and visible by browsing tool (may be related to access) - exported would ensure the item is exported in a dynamic library (DLL) - designtime, runtime would indicate a property available for design or execution, - final would make methods non-virtual -...Sure, we could think of lots of predefined ones. It's talking Walter into them that's hard. ;)In fact, using some more keywords (or attributes), we would be able to better control what we do... Some keywords that could be usefull are immuable, const, mutable, var(iable), compiletime, byref, byvalue,...People keep writing "immuable" when in fact the English word for that is "immutable". Dictionary.com says: No entry found for immuable. Did you mean immutable?Also, as mentionned in this thread, some modifier could be attributes... We would have a bunch of attributes that would give us more control when it is usefull...People always want more control, and attributes would be so much better than command line switches. Makes the code self-documenting, self-directing, and removes the need for dealing with the compiler from the command line.I never liked that syntax. I'd rather have the default in front, so it can be an attribute too!class Point { float x,y,z; this(in); // allow construction with the caller being able to initialize the public fields and base class more or less directlymaybe, we should have a syntax like pure virtual function: this(in) = default;Yes, unspecified fields would get their type's default, or whatever the class initializes them to by default.new Point a(x:1.0, y:2.0, z:0.0); // call this(in) with initializationforthese membersAnd initialize other to default? If so, this could be nice...Also what would be interesting is an uninitialized keyword so that we can created unitialized object explicitly when performance matters...Maybe you can't create uninitialized object that's derived from some other object (the bases will get initialized anyway) and if you call a constructor it has to get initialized. More of a declaration on a piece of data that says "I'm making a new object but don't want it initialized, and will not need to call any constructor". Basically just wraps allocation and typecasting. You could attribute member variables with this. Or any stack data. Maybe not classes.I really like this one too... and it would be even more interesting if some attributes can be different for debug/release, design/runtime,... so that for example, we can have debug functions that are not available in release version (for implementation, it might be a "pure virtual function called" when not available or something like that so that it would be easier to handled partial recompilations...I never considered partial recompilation. Seems like everything that needs to be recompiled should be.I would then like to ba able to associate atributes also to classes and parameters...Yes. To anything. Data, member functions, entire classes or structures or any type.A great thing that may be possible is to implement a tracing facilities that would output all functions that are called with the value of their parameters.... I think this is possible with .NET... We just need to be able to get all parameters type and value...That'd be handy sometimes.I would suggest a keyword (or predefined special function) that would be used instead of assert in such cases...Good idea. Maybe a few variants on assert. assert is just shorthand for debug { if (x) throw AssertException; } Maybe one that throws a string message, or throws a message box to be executed... and one that throws nothing at all, just effectively a breakpoint. A debugger catches it and if there's no debugger, or in release builds, it just goes on past. That's something I always put in my libraries of code, a call to cause a breakpoint. Use that to build assert macros. Seems like a debugging feature (aka BASIC's stop command) which could be really useful.And also one for functions that are not yet implemented or code that isnotcompleted (todo, bug, ...)Yeah, their code could print messages to the build log with line numbers etc.maybe compile_time_state(a) where a is an identifier and we can tell the compiler which identifier are ignored, which one causes a run-time errorandwhich causes a compilation error.Switches like this are already handled with version statements; if we can define attributes differently depending on version, we're good to go.I'm not sure. Haven't thought about that enough.I guess all this adds to the complexity of the compiler. That's a good thing, in some ways. This could be powerful stuff in the right hands. Maybe could be combined with templates somehow?I don't the relashion with template... but it may indeed be interesting to be able to do some metaprogramming based on those attributes.I would like that the compiler would have a mode in which it case do some extended analysis of the code and potential pitfalls...Pedantic. ;) lint mode. Sean
Aug 21 2003
As mentionned in my other answer, you are right, it does not works well with virtual functions and since this is the default in D, the if someone can think not to forgot to make classes final when appropriate that people should be able be uses const as appropriate. So in pratice, constness modifier would be mostly usefull for libraries writers (similar to STL, boost and others...) but not that much for user. What do you think about allwing const only for final class type and simple types (integral, enum, struct, pointers) and not allows it on other types. And there would be a way to share code between a const and non-const version of the sane thing (constnessof would be a good keyword for that - it would allows to modifiy the constness of a type (including function return value) from the constness of another one or more)I would even better like that when const is not specified that thefunctionwould be const candidate provide that it would have compiled if const was added. And we uses mutable to explictly tell that a member function is not const and cannot be used on const object even if it would have compiled if const was added.This is just making it easier for people to be lazy. ;)
Aug 23 2003
Hi, Comments embedded. ----- Original Message ----- From: "Antti Sykäri" <jsykari gamma.hut.fi> Newsgroups: D Sent: Tuesday, August 19, 2003 8:14 PM Subject: const / immutable (was: C++ style const concept) [big snip]Now that I think of it, I remember that Daniel Yokomiso wrote an interesting description of const stuff some time ago, let's see... There, I see it's still worth reviewing: http://www.digitalmars.com/drn-bin/wwwnews?D/9962Unfortunately my memory is not well lately and the newsgroup web interface seems to be down, so I talk something about this mutability issues. It's related to my pet language Eon.(Daniel: how's Eon doing, by the way?)Eon is dead, long live Eon. Or something like that. The worst thing about designing a language is the pesky design decisions you have to make. I, after one year of study, dropped side-effects, and now Eon is a pure OOFPL. Here is the story of my attempts to provide immutability in the type-system. First some definitions: 1 - by side-effects I mean both observable and unobservable side-effects. 2 - I'll use procedures to describe methods with side-effects and functions to describe "pure", side-effect free, methods. 3 - mutable types are subtypes of immutable types, so we can call functions and procedures on mutable types and only functions on immutable types. 4 - a variable with an immutable type A holds a reference to some value of A, but this value can be mutated through other (mutable) references. My desire was to provide a way to verify, via static type checking, code correctness, specially regarding DbC. One of the most powerful contracts is the immutability contract, guaranteeing that some parameters of the operation will be untouched. int sum(immutable int[] array) { int result = 0; for (int i = 0; i < array.length; i++) { result += array[i]; } return result; } The "immutable" modifier indicates that the parameter won't be changed by the operation body. Notice that a immutable array of type T is distinct from a array of type immutable T; this "immutable" modifier refers to the array immutability, not the array element type immutability. This type modifier should be carried through assignment to avoid cheating, or else the operation could store the value in a mutable variable and break the immutability. There's a second way to use the "immutable" modifier: to define pure functions. int immutable product(immutable int[] array) { int result = 0; for (int i = 0; i < array.length; i++) { result *= array[i]; } return result; } An "immutable" operation won't cause side effects, so inside a class you can separate functions from procedures to ensure immutability. The compiler could enforce this rule disallowing procedure calls through immutable references. As soon as we define such rules we get in some problems: template TMin(T) { T immutable min(immutable T x, immutable T y) { return x < y ? x : y } } The min operation correctly asserts that it's parameters aren't mutated, but it incorrectly transform the immutable reference into a mutable one, using the "return". Here comes the first problem of immutability definitions: some operations return types depend on its parameter types. In these situations the return type mutability status is defined by the caller. Immutability depends heavily on the compiler, both to check the calls and to infer correctly the return type dependencies. There's also a third type of immutability: "immutable" types. But they're a beast of their own. In my language I decided to drop side-effects because: it's very expensive to track immutability and to provide two versions of the same operations (e.g. a put method that changes the collection and a put method that returns a new colection with the new value added). Also it'd very hard to write "ensure" clauses on method's contracts that have side-effects (google for "old" expression and clone in comp.lang.eiffel). Geting rid of side-effects allow the language and the libraries to become simpler, and we can simulate side-effects with linear types and monads. So, answering your question Antti, Eon is being rewritten by the last time. Today it's a pure OOFPL, with a higher-order features, a simple syntax to define object literals (i.e. "pair := {first := 1; second := "abcde"}"), predicate based multiple-inheritance (i.e. "class a := {x : Integer; y : Real and (> x) and odd};", we define x as an Integer and y as a Real number, greater than x and odd (odd and (> x) are predicates)). Unfortunately I had to restart the compiler (again) due to the syntatic and semantic changes. I believe this will be the last big change in the language and sometime in the next year (first semester probably) I'll have a working compiler and a standard library (collections, text, numerics, xml, io and networking) - most parts of the library are already specified (I have the method types and contracts and some implementations written in older versions of Eon).-AnttiBest regards, Daniel Yokomiso. "Nature has given us two ears but only one mouth." - Benjamin Disraeli (1804-81), British politician --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.512 / Virus Database: 309 - Release Date: 19/8/2003
Aug 24 2003
In article <biafl2$2tsa$1 digitaldaemon.com>, Daniel Yokomiso wrote:Eon is dead, long live Eon. Or something like that. The worst thing about designing a language is the pesky design decisions you have to make. I, after one year of study, dropped side-effects, and now Eon is a pure OOFPL. Here is the story of my attempts to provide immutability in the type-system. First some definitions: 1 - by side-effects I mean both observable and unobservable side-effects. 2 - I'll use procedures to describe methods with side-effects and functions to describe "pure", side-effect free, methods. 3 - mutable types are subtypes of immutable types, so we can call functions and procedures on mutable types and only functions on immutable types. 4 - a variable with an immutable type A holds a reference to some value of A, but this value can be mutated through other (mutable) references.So this would be equivalent to C++'s const. (Without the possibility of subverting the type system, though.) I'll blabber a bit more about immutability below.int sum(immutable int[] array) { int result = 0; for (int i = 0; i < array.length; i++) { result += array[i]; } return result; } The "immutable" modifier indicates that the parameter won't be changed by the operation body. Notice that a immutable array of type T is distinct from a array of type immutable T; this "immutable" modifier refers to the array immutability, not the array element type immutability. This typeOne interesting question regarding the semantics of immutable here is that what is the difference between immutable array of int and array of immutable int. Because the array can be viewed as consisting of its elements, doesn't its immutability imply the immutability of its elements and therefore make it equivalent to immutable array of immutable integers? It seems to depend on the kind of array and of the kind of the language it lives in. I'd assume (in pseudo-code notation) that array in a very "pure" modern, orthogonal, clean, type-safe etc. language (I'm putting some expectations on Eon you see after all those papers you've read :-) would have operations like: class immutable_array<T> { T get(int); // return by-value copy of element at i int length(); } class array<T> extends immutable_array<T> { void set(T, int) // set value at T to int void length(int); } This would mean in practice that having an immutable array would prohibit changing its elements, which would render them practically immutable. On the other hand, if the hypothetical language were less clean and had something as down-and-dirty as aliasing and references, what would an array then look like? Like this?: array<T> { T& get(int); // return a reference to element at i // other properties } Now what would the "immutable part of the interface" look like? Would it have the function "immutable T& get(int)"? But this would have the same signature, except for the hidden argument that contains the reference to the object the method is part of. Ah, there's the catch: immutable_array<T> { immutable T& immutable get(int); // other properties } And lo, we get C++ rules along with all the special cases and all. (There probably is some kind of logic to them after all, especially if you remove the warts induced by the C compatibility.) If you have by-reference object, say of type T, what does "immutable T" mean? That you cannot change the reference to point to another object? Or that you can call only the pure methods of the object? Are the rules different if you have value objects?modifier should be carried through assignment to avoid cheating, or else the operation could store the value in a mutable variable and break the immutability. There's a second way to use the "immutable" modifier: to define pure functions.At this phase I must point out that the semantics seem awfully familiar from C++, only the names differ: method - member function immutable - const pure function - const member functionAn "immutable" operation won't cause side effects, so inside a class you can separate functions from procedures to ensure immutability. The compiler could enforce this rule disallowing procedure calls through immutable references. As soon as we define such rules we get in some problems: template TMin(T) { T immutable min(immutable T x, immutable T y) { return x < y ? x : y } }Why not: template TMin(T) { immutable T immutable min(immutable T x, immutable T y) { return x < y ? x : y; } } I'd assume it would be instantiated automatically. It would of course need along the normal version: template TMin(T) { T min(T x, T y) { return x < y ? x : y; } } And the overloading rules to pick the right version.In my language I decided to drop side-effects because: it's very expensive to track immutability and to provide two versions of the same operations (e.g. a put method that changes the collection and a put method that returns a new colection with the new value added). Also it'd very hardI'd assume that you wouldn't need two versions; if you only want to have an immutable and a mutable version of a class, just drop the procedures and leave the pure methods in the immutable version -- calling the procedure on the immutable version would be prohibited at compile time, nice and clean. Maybe you wanted something completely different?Also it'd very hard to write "ensure" clauses on method's contracts that have side-effects (google for "old" expression and clone in comp.lang.eiffel).Shouldn't "ensure" clauses *not* have side-effects? Or did you mean "ensure" clauses of methods that have side-effects? Uh, could you give an example of one hard case? The thread you pointed at is probably the one with the subject "'old' + 'implies' = bug ?" (from 1996)? An interesting thread. "old" is a funny modifier, but one wonders if the semantical trouble arised because its real semantics ("will be evaluated before entering the function although it is mentioned and used only at the end") was swept under the rug while humming noble tunes about mathematical theories about contracts. And of course all the expressions in contracts are side-effectsless, at least if we don't cheat, right? *grin* IMHO all things that go with contracts tend to be a bit flaky. I just read (OOSC 2nd ed.) about abstract contracts that make it possible to actually tighten the preconditions in a deriving class, which shouldn't be possible -- but in this case is. (See the example about BOUNDED_STACK in the chapter about inheritance techniques.) I've not finished studying Eiffel's contract system in full but I do have the feeling that it's designed with a bit too much theory in mind -- I'd rather concentrate in making the constructs as usable and transparent as possible than create overly complicated (or overly simplified) theories and then watch them break in normal practical situations.So, answering your question Antti, Eon is being rewritten by the last time. Today it's a pure OOFPL, with a higher-order features, a simple syntax[snip]standard library (collections, text, numerics, xml, io and networking) - most parts of the library are already specified (I have the method types and contracts and some implementations written in older versions of Eon).Best of luck with Eon, the type system pretty looks interesting. Must be worth the intellectual exercise, at least. (I must confess that I don't put much value on purely functional languages, but that could be just because I haven't studied them enough. Perhaps I shall be enlightened one day and write only Haskell after that.) To conclude, it seems that the efforts to combine mutable and immutable values tend to divert in different directions. Some (D) make the language simpler by forgetting constant types (almost) altogether. Some (Eon) go to the other extreme and make everything purely functional. Some (C++) march bravely forth and allow the complexity caused by having both the constant and the mutable. This makes wonder if there is at all a simple and beautiful language that would at the same time allow the constant and non-constant types to live peacefully together... -A
Aug 25 2003
Hi, Comments embedded. ----- Original Message ----- From: "Antti Sykäri" <jsykari gamma.hut.fi> Newsgroups: D Sent: Monday, August 25, 2003 9:07 PM Subject: Re: const / immutable (was: C++ style const concept) [snip]So this would be equivalent to C++'s const. (Without the possibility of subverting the type system, though.) I'll blabber a bit more about immutability below.changedint sum(immutable int[] array) { int result = 0; for (int i = 0; i < array.length; i++) { result += array[i]; } return result; } The "immutable" modifier indicates that the parameter won't bedistinctby the operation body. Notice that a immutable array of type T isthefrom a array of type immutable T; this "immutable" modifier refers toIf you have (using const to mark immutability): class Cell { private int value; this(int value) { this.value = value; } public void set(int value) { this.value = value; } public int immutable get() { return this.value; } } Cell c0 = new Cell(0), c1 = new Cell(1), c2 = new Cell(2), c3 = new Cell(3); Cell[] a0 = [c1, c2, c3]; // mutable array of mutable cell references immutable Cell[] a1 = [c1, c2, c3]; // mutable array of immutable cell references Cell[immutable] a2 = [c1, c2, c3]; // immutable array of mutable cell references const Cell[immutable] a3 = [c1, c2, c3]; // immutable array of immutable cell references a0[0].set(0); // ok, the a0[0] reference isn't immutable a0[0] = c2; // ok, the array isn't immutable a1[0].set(0); // compiler error, the a1[0] reference is immutable a1[0] = c2; // ok, a1 isn't immutable a2[0].set(0); // ok, the a2[0] reference isn't immutable a2[0] = c2; // compiler error, a2 is immutable a3[0].set(0); // compiler error, the a1[0] reference is immutable a3[0] = c2; // compiler error, a3 array is immutable Therefore I think that separating the immutability issue of the array vs. the element type leads to different semantics. Here we assume that "immutable A" is a supertype of "A".array immutability, not the array element type immutability. This typeOne interesting question regarding the semantics of immutable here is that what is the difference between immutable array of int and array of immutable int. Because the array can be viewed as consisting of its elements, doesn't its immutability imply the immutability of its elements and therefore make it equivalent to immutable array of immutable integers?It seems to depend on the kind of array and of the kind of the language it lives in. I'd assume (in pseudo-code notation) that array in a very "pure" modern, orthogonal, clean, type-safe etc. language (I'm putting some expectations on Eon you see after all those papers you've read :-) would have operations like: class immutable_array<T> { T get(int); // return by-value copy of element at i int length(); } class array<T> extends immutable_array<T> { void set(T, int) // set value at T to int void length(int); } This would mean in practice that having an immutable array would prohibit changing its elements, which would render them practically immutable.Immutable operations ("T immutable get(int)" and "int immutable length()") won't have side effects, but they may be changed by other objects/threads. But if you have a value type (like a number, matrix, etc.), a immutable struct type, you'll be safe, because the value isn't shared. Immutability tell us nothing about aliasing.On the other hand, if the hypothetical language were less clean and had something as down-and-dirty as aliasing and references, what would an array then look like? Like this?: array<T> { T& get(int); // return a reference to element at i // other properties } Now what would the "immutable part of the interface" look like? Would it have the function "immutable T& get(int)"? But this would have the same signature, except for the hidden argument that contains the reference to the object the method is part of. Ah, there's the catch: immutable_array<T> { immutable T& immutable get(int); // other properties } And lo, we get C++ rules along with all the special cases and all. (There probably is some kind of logic to them after all, especially if you remove the warts induced by the C compatibility.)The problem in C++ is that they use "a[0] = 1;" meaning, get the address of the first element and let it be assignable, so you need two versions of array_get, one yielding const references and other yielding normal ones. IMO "a[0]" should call array_get and "a[0] = 1" should call array_set, just syntatic sugar.If you have by-reference object, say of type T, what does "immutable T" mean? That you cannot change the reference to point to another object? Or that you can call only the pure methods of the object? Are the rules different if you have value objects?array<T> { T const get(int); int const length(); void set(int,T); void length(int); } int i1 = 1, i2 = 2, i3 = 3, i4 = 4; array<int> a1 = [i1, i2, i3, i4]; array<&int> a2 = [i1, i2, i3, i4]; const array<int> a3 = [i1, i2, i3, i4]; const array<&int> a4 = [i1, i2, i3, i4]; const array<const &int> a5 = [i1, i2, i3, i4]; a1.set(0, i3); // ok a2.set(0, &i3); // ok a2.get(0) = 5; // ok a3.set(0, i3); // compiler error a4.set(0, &i3); // compiler error, can't mutate the array. a4.get(0) = 8; // ok a5.set(0, &i3); // compiler error a5.get(0) = 8; // compiler error It's a bit tricky, but the const qualifies the reference type too.themodifier should be carried through assignment to avoid cheating, or elseyouoperation could store the value in a mutable variable and break the immutability. There's a second way to use the "immutable" modifier: to define pure functions.At this phase I must point out that the semantics seem awfully familiar from C++, only the names differ: method - member function immutable - const pure function - const member functionAn "immutable" operation won't cause side effects, so inside a classcompilercan separate functions from procedures to ensure immutability. TheHmmm, perhaps you've meant: template TMin(T) and template TMin(immutable T) But the issue here is that immutability, when you're dealing with generic types, generates some dependency between the parameters and the result types. Sometimes only one parameter will define the const issue, sometimes more than one. If you make a templated function to find statistical results from a array (min, max, average, etc.) you need a way to ensure that these functions won't: mutate the array, won't mutate the array elements, the return types will have the same immutability status (yes or no) that the array element type. In your example the TMin version for mutable parameters allowed the min function to change them through any mutating methods they had.could enforce this rule disallowing procedure calls through immutable references. As soon as we define such rules we get in some problems: template TMin(T) { T immutable min(immutable T x, immutable T y) { return x < y ? x : y } }Why not: template TMin(T) { immutable T immutable min(immutable T x, immutable T y) { return x < y ? x : y; } } I'd assume it would be instantiated automatically. It would of course need along the normal version: template TMin(T) { T min(T x, T y) { return x < y ? x : y; } } And the overloading rules to pick the right version.methodIn my language I decided to drop side-effects because: it's very expensive to track immutability and to provide two versions of the same operations (e.g. a put method that changes the collection and a puthardthat returns a new colection with the new value added). Also it'd veryI'd assume that you wouldn't need two versions; if you only want to have an immutable and a mutable version of a class, just drop the procedures and leave the pure methods in the immutable version -- calling the procedure on the immutable version would be prohibited at compile time, nice and clean. Maybe you wanted something completely different?I was talking about the issue I mentioned in my last (statistical) example. You need to write things like: template stats(T) { const_if_array_type_is_const T find_min(const T [const] array) { ... } } That can be very awkward. The problem here is: the result type immutability depends on the array element type immutability, but we must be able to ensure that the array element type won't be changed by the function. These examples may be simple, but when you have more classes, it's hard to track immutability dependencies. Also immutability is one level only: class Cell(T) { private T value; this(T value) { this.value = value; } public void set(T value) { this.value = value; } public T immutable get() { return this.value; } } Cell(int) c1 = new Cell(int)(1); const Cell(Cell(int)) c2 = new Cell(Cell(int))(c1); const Cell(const Cell(Cell(int))) c3 = new Cell(const Cell(Cell(int)))(c2); c3.set(c2); // compiler error; c3.get().set(c1); // compiler error; c3.get().get().set(0); // ok; With "Cell" classes it's easy, because you can specify the internal elements immutability status, but some classes, like "Department" that contains "Employee", are harder, because you may mutate "Employee" through a "immutable Department" reference, because you must be able to tell that the "Employee get(String)" won't mutate the "Department", but when called via a "immutable Deparment" reference should yield a "immutable Employee", but when calles via a common "Department" reference should yield a common "Employee". If we include the implicit this parameter in the type we may be able to do this (somehow, but's awkward): immutable Employee immutable get(immutable Department this, immutable String name); Employee immutable get(Department this, immutable String, name); where the "immutable" qualifier before the "get" means the this reference won't be mutated. Some issues regarding mutability are discussed here <http://lambda.weblogs.com/discuss/msgReader$8378?mode=topic&y=2003&m=8&d=25Tim Sweeney has some pretty good thoughts.Sorry I meant: 'Also it's very hard to write "ensure" clauses on contracts of methods that have side-effects (i.e. methods that mutate the instance)'. Set(T) { void remove(T item) out () { if (old this.contains(item)) { assert(this.size + 1 == (old this.size())); } assert(!this.contains(item)); // so far so good. for (T each; old this) { if (each != item) { assert(this.contains(item)); } } /* ops, it won't work because the "old this" expression will yield a reference * to the this object, and here it's already mutated. The correct expression is * "(old this.clone())" ensuring that we get a snapshot of the this object from * before the method execution. */ }; } This is the simplest case of "old" expressions in mutating methods. There's more fun. Check the Eiffel kernel classes' contracts for more details. [snip]Also it'd very hard to write "ensure" clauses on method's contracts that have side-effects (google for "old" expression and clone in comp.lang.eiffel).Shouldn't "ensure" clauses *not* have side-effects? Or did you mean "ensure" clauses of methods that have side-effects? Uh, could you give an example of one hard case?lastSo, answering your question Antti, Eon is being rewritten by thesyntaxtime. Today it's a pure OOFPL, with a higher-order features, a simple[snip]andstandard library (collections, text, numerics, xml, io and networking) - most parts of the library are already specified (I have the method typesAs I (hopefully) showed you, the immutability issue is very tricky. Once we assume that everything can mutate we need to state everything that won't change everytime. There are lots of hard work on this area, some propose a "mutates" clause on methods, to define which object's parts will be different after a method call. My test of Eon features was writing libraries with the proposed syntax and semantics. When Eon had side-effects as the norm and keywords to ensure immutability the code become very, very verbose. Once I dropped the side-effects, I could concentrate on behaviour and isolate the changes in Monads. IME it's almost impossible to come with a readable, concise and comprehensive notation for immutability vs. mutability. Best regards, Daniel Yokomiso. "I didn't attend the funeral, but I sent a nice letter saying I approved of it." - Mark Twain --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.512 / Virus Database: 309 - Release Date: 20/8/2003contracts and some implementations written in older versions of Eon).Best of luck with Eon, the type system pretty looks interesting. Must be worth the intellectual exercise, at least. (I must confess that I don't put much value on purely functional languages, but that could be just because I haven't studied them enough. Perhaps I shall be enlightened one day and write only Haskell after that.) To conclude, it seems that the efforts to combine mutable and immutable values tend to divert in different directions. Some (D) make the language simpler by forgetting constant types (almost) altogether. Some (Eon) go to the other extreme and make everything purely functional. Some (C++) march bravely forth and allow the complexity caused by having both the constant and the mutable. This makes wonder if there is at all a simple and beautiful language that would at the same time allow the constant and non-constant types to live peacefully together... -A
Aug 26 2003
"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message news:bifhh6$1hb7$1 digitaldaemon.com...Therefore I think that separating the immutability issue of the array vs. the element type leads to different semantics. Here we assume that "immutable A" is a supertype of "A".This seems logical.Immutable operations ("T immutable get(int)" and "int immutable length()") won't have side effects, but they may be changed by other objects/threads. But if you have a value type (like a number, matrix,etc.),a immutable struct type, you'll be safe, because the value isn't shared. Immutability tell us nothing about aliasing.Not having side effects allows one set of optimizations. I would like the language to include something like "values changed by other threads *can* show up as changed to a thread which uses those values, however, changes are not *guaranteed* to show up unless the values are marked volatile." This would allow the compiler to keep more values in registers. Not sure what to do about called functions. There is something extremely nice about knowing for sure that nothing will ever modify a structure.The problem in C++ is that they use "a[0] = 1;" meaning, get theaddressof the first element and let it be assignable, so you need two versions of array_get, one yielding const references and other yielding normal ones.IMO"a[0]" should call array_get and "a[0] = 1" should call array_set, just syntatic sugar.Basically properties.But the issue here is that immutability, when you're dealing with generic types, generates some dependency between the parameters and the result types. Sometimes only one parameter will define the const issue, sometimes more than one. If you make a templated function to find statistical results from a array (min, max, average, etc.) you need a waytoensure that these functions won't: mutate the array, won't mutate thearrayelements, the return types will have the same immutability status (yes or no) that the array element type. In your example the TMin version for mutable parameters allowed the min function to change them through any mutating methods they had.This is what I don't understand. Why should Min return an immutable value? Perhaps it can return a copy of a value that can be mutated. That's probably lower performance though. Is this the only reason? Sean
Aug 26 2003
----- Original Message ----- From: "Sean L. Palmer" <palmer.sean verizon.net> Newsgroups: D Sent: Tuesday, August 26, 2003 12:27 PM Subject: Re: const / immutable [long]"Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> wrote in message news:bifhh6$1hb7$1 digitaldaemon.com...[snip]wayBut the issue here is that immutability, when you're dealing with generic types, generates some dependency between the parameters and the result types. Sometimes only one parameter will define the const issue, sometimes more than one. If you make a templated function to find statistical results from a array (min, max, average, etc.) you need atoorensure that these functions won't: mutate the array, won't mutate thearrayelements, the return types will have the same immutability status (yesvalue?no) that the array element type. In your example the TMin version for mutable parameters allowed the min function to change them through any mutating methods they had.This is what I don't understand. Why should Min return an immutablePerhaps it can return a copy of a value that can be mutated. That's probably lower performance though. Is this the only reason?Here's an example that illustrates this. class Cell { private int value; public this(int _value) { this.value = _value; } public int immutable get() { return this.value; } public void set(int _value) { this.value = _value; } } template TPair(T, U) { class Pair { private final T first; private final U second; public this(T _first, U _second) { this.first = _first; this.second = _second; } public T immutable first() { return this.first; } public T immutable second() { return this.second; } } } alias instance TPair(Cell, Cell) CellPair; alias instance TPair(immutable Cell, immutable Cell) ImmutableCellPair; ImmutableCellPair minMax(immutable Cell[immutable] array) in { assert(array.length > 0); } body { immutable Cell min = array[0], max = array[0]; // must be immutable for (int i = 1; i < array.length; i++) { if (array[i].get() < min.get()) { min = array[i]; } else if (array[i].get() > max.get()) { max = array[i]; } } return new ImmutableCellPair(min, max); // must be immutable } int main() { Cell[] array = [new Cell(1), new Cell(2), new Cell(3)]; CellPair pair = minMax(array); // compiler error pair.first().set(0); pair.second().set(1000); return 0; } Inside "minMax" we only have immutable references, so we must construct a template to a "immutable Cell". Inside "main" we can access the array elements as mutable references, but the result of the "minMax" function is immutable, even if we are the "owners" of the object. That's the trick, a way to define a function interface, specifying that it won't mutate the parameters, but allowing the result immutability to depend upon the parameter's mutability. Sometimes the result immutability depends on all parameters, sometimes just on some of them, or even on none of them. Another interesting example. class Box : Cell { public this(int _value) { super(_value); } public Box self() { return this; } } int main() { Box b1 = new Box(1); immutable Box b2 = new Box(2); // the following statement require that "self" return a immutable Box. printf("%d\r\n", b2.self().get()); // but if it's immutable the following code doesn't work b1.self().set(2); printf("%d\r\n", b1.self().get()); return 0; } The "self" method needs to be mutable when called through mutable interfaces and immutable when called through immutable interfaces. It's just a harmless method ;)Sean--- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.512 / Virus Database: 309 - Release Date: 19/8/2003
Aug 26 2003
But the issue here is that immutability, when you're dealing with generic types, generates some dependency between the parameters and the result types. Sometimes only one parameter will define the const issue, sometimes more than one. If you make a templated function to find statistical results from a array (min, max, average, etc.) you need a waytoensure that these functions won't: mutate the array, won't mutate thearrayelements, the return types will have the same immutability status (yes or no) that the array element type. In your example the TMin version for mutable parameters allowed the min function to change them through any mutating methods they had.sameIn my language I decided to drop side-effects because: it's very expensive to track immutability and to provide two versions of thebemethodoperations (e.g. a put method that changes the collection and a puthardthat returns a new colection with the new value added). Also it'd veryI'd assume that you wouldn't need two versions; if you only want to have an immutable and a mutable version of a class, just drop the procedures and leave the pure methods in the immutable version -- calling the procedure on the immutable version would be prohibited at compile time, nice and clean. Maybe you wanted something completely different?I was talking about the issue I mentioned in my last (statistical) example. You need to write things like: template stats(T) { const_if_array_type_is_const T find_min(const T [const] array) { ... } } That can be very awkward. The problem here is: the result type immutability depends on the array element type immutability, but we mustable to ensure that the array element type won't be changed by thefunction.These examples may be simple, but when you have more classes, it's hard to track immutability dependencies. Also immutability is one level only:maybe something like that would be OK: template stats(T) { constnessof(array) T find_min(const T [const] array) } where we can indicate what to check for constness. The list can include this for member functions and for complex type we might allows a syntax similar to the one used for specialisation constnessof(array : pattern) where patern would include the optional const or alternatively a syntax where we could name modifier: For the Makes your suggestions!!! template stats(T) { constnessof(c1, c2) T find_min(const#c1 T [const#c2] array) } For non-virtual methods, we could support automatic constness detection which should not be hard for the compiler to implement (about the same complexity as generating an error in C++ when constness is not respected). template stats(T) { autoconst T find_min(const T [const] array) } where const would be added if required. This would be usefull for generic code like the TMin template or for containers that wants to return constant reference to their elements if they are themself const... Automatic modifier would also have other uses that for constness support. They would be usefull for function calling convention for example (I don't know how those specifier are handled in D - that is the equivalent of cdecl, fastcall, pascal and similar in C++). For ex. in C++, mem_fun and related classes and functions only works when the default calling convention is used... but ideally the same code should be usable with any calling convention.
Aug 26 2003
"Philippe Mori" <philippe_mori hotmail.com> escreveu na mensagem news:big4ek$2e9p$1 digitaldaemon.com... [snip]maybe something like that would be OK: template stats(T) { constnessof(array) T find_min(const T [const] array) } where we can indicate what to check for constness. The list can include this for member functions and for complex type we might allows a syntax similar to the one used for specialisation constnessof(array : pattern) where patern would include the optional const or alternatively a syntax where we could name modifier: For the Makes your suggestions!!! template stats(T) { constnessof(c1, c2) T find_min(const#c1 T [const#c2] array) } For non-virtual methods, we could support automatic constness detection which should not be hard for the compiler to implement (about the same complexity as generating an error in C++ when constness is not respected). template stats(T) { autoconst T find_min(const T [const] array) } where const would be added if required. This would be usefull for generic code like the TMin template or for containers that wants to return constant reference to their elements if they are themself const... Automatic modifier would also have other uses that for constness support. They would be usefull for function calling convention for example (I don't know how those specifier are handled in D - that is the equivalent of cdecl, fastcall, pascal and similar in C++). For ex. in C++, mem_fun and related classes and functions only works when the default calling convention is used... but ideally the same code should be usable with any calling convention.Yes, this "constnessof" and "autoconst" would solve the issues I was talking about. I think they might open new cans of worms, but there's another issue. In Eon I started doing something like that, later generalizing the idea of dependent mutability to dependent typing, which is just a few steps ahead, but improves the expressiveness by an order of magnitude. "Philippe Mori" <philippe_mori hotmail.com> escreveu na mensagem news:big4ek$2e9p$1 digitaldaemon.com... [snip]maybe something like that would be OK: template stats(T) { constnessof(array) T find_min(const T [const] array) } where we can indicate what to check for constness. The list can include this for member functions and for complex type we might allows a syntax similar to the one used for specialisation constnessof(array : pattern) where patern would include the optional const or alternatively a syntax where we could name modifier: For the Makes your suggestions!!! template stats(T) { constnessof(c1, c2) T find_min(const#c1 T [const#c2] array) } For non-virtual methods, we could support automatic constness detection which should not be hard for the compiler to implement (about the same complexity as generating an error in C++ when constness is not respected). template stats(T) { autoconst T find_min(const T [const] array) } where const would be added if required. This would be usefull for generic code like the TMin template or for containers that wants to return constant reference to their elements if they are themself const... Automatic modifier would also have other uses that for constness support. They would be usefull for function calling convention for example (I don't know how those specifier are handled in D - that is the equivalent of cdecl, fastcall, pascal and similar in C++). For ex. in C++, mem_fun and related classes and functions only works when the default calling convention is used... but ideally the same code should be usable with any calling convention.Yes, this "constnessof" and "autoconst" would solve the issues I was talking about. I think they might open new cans of worms, but there's another issue. In Eon I started doing something like that, later generalizing the idea of dependent mutability to dependent typing, which is just a few steps ahead, but improves the expressiveness by an order of magnitude.
Aug 26 2003
Ok, let's drop immutable and just talk about const. In article <bifhh6$1hb7$1 digitaldaemon.com>, Daniel Yokomiso wrote:But the issue here is that immutability, when you're dealing with generic types, generates some dependency between the parameters and the result types. Sometimes only one parameter will define the const issue, sometimes more than one. If you make a templated function to find statistical results from a array (min, max, average, etc.) you need a way to ensure that these functions won't: mutate the array, won't mutate the array elements, the return types will have the same immutability status (yes or no) that the array element type. In your example the TMin version for mutable parameters allowed the min function to change them through any mutating methods they had.[snip]Okay, now I think I have an idea about the fundamental problem. The min() function can be used as an example of the general idea. The function must return one of its arguments, whether mutable or immutable. At the same time it must provide the guarantee that it won't modify its arguments -- we'd like the type system to ensure that the only thing that "min(T, T)" is allowed to do is to pass one of its arguments back to the caller. One way to ensure this is to make its arguments const; but then you'd have to make its return type const, as well, or the following wouldn't compile: (Let's leave the bodies away -- everybody probably knows what "min" looks like if he or she has read this far :) I'll drop the template syntax because it has nothing to do with const really. Assume that T is an int or actually, reference type would be better to illustrate the const semantics. const T min(const T x, const T y); This is inacceptable, because sometimes we'd like to use min on non-const arguments; therefore we could solve the problem with overloading: T min(T x, T y); const T min(const T x, const T y); And we need to have C++-like rules to disambiguate between the calls, or maybe now we could have some use for templates: implicit template function (const T) { const T min(const T x, const T y); } implicit template function (T) { T min(T x, T y); } Another way would be unsafe and dirty but I'll still include it for completeness: T min(const T x, const T y) { T mutable_x = const_cast<T>(x), mutable_y = const_cast<T>(y); return x < y ? mutable_x : mutable_y; } However, this would require the caller to promise not to touch the result if one of the originals were const in the first place. Ok, pretty ugly idea but its actually a prelude to this solution: Don't use "const" as a type modifier at all, but merely as a way of documenting the function's behavior (as in "I won't change this parameter"). Enter contracts. Like so: T min(const T x, const T y) { // implementation... } out() { // mathematical properties of min(x,y) assert(result <= x); assert(result <= y); // it's one of them assert(result === x || result === y); // I didn't change my parameters, I promise; these would be // generated by "const" assert(old x == x); assert(old y == y); // Here we would actually have to rely on // deep_clone or similar, as you mentioned below. Tricky notion, // comparing something that exists now to something that existed // earlier in time } Side note: Don't you just love when the function's postconditions actually describe perfectly what the function does! Reminds of me of the good old combinator function in purely functional languages. It takes a function of type ('a -> 'b) and a function of type ('b -> 'c) and returns a function of type ('a -> 'c). Now what could a function of this type do? The notation "'something" means generic type parameter, so it cannot assume anything about its types. Well, what else can it do, then, than create a function that first calls the first function and then calls the second function on the result? So the function's type is a complete description of the function itself. Back to the min() problem. Ideas only keep getting wilder... well, do you remember how we did it in C times: Macro! Of course! #define min(x, y) ((x) < (y) ? (x) : (y)) This actually works better (except for the side-effects of call-by-name parameter passing) than any of its C++ equivalents, since you can do things like int x = 5; const float y = 1.0; float z = min(x, y); No need for different "const" methods, overloading or anything! Amazing! Compare this with what Andrei Alexandrescu did to get the (nearly) similar effect with templates: http://www.cuj.com/documents/s=7996/cujcexp1904alexandr/ By the way, he has an interesting comment: "Minimum and maximum are two simple concepts present both in math and in the real life. If a programming language is unable to express simple concepts of math and life, then there is something deeply wrong with that language." Anyway - macros seem to work. Why is this possible? Well, because min() is not an actual function call but merely a textual substitution. Well, what language feature was introduced in C++ (and later, in C) to get rid of a certain usage of macros? Taking Andrei's challenge, let's bring forth the final solution: inline functions. I wouldn't mean to use inline functions as a means to perform premature optimization, a purpose which programmers are often accused of misusing. But I simply would want to take advantage of the fact that inline functions can "see" the context where they are used, and therefore they would also be able to provide different kinds of code than usual functions. Actually, after an inline function were expanded several times, the compiler could gather them to a single (if possible) function and optimize the inlined code to a function call :) One thing that inline functions could provide would be determining the return type based on argument types. In fact, the inline function could work directly on the symbolic environment of the caller; assume inline Symbol min(Symbol x, Symbol y) { return x < y ? x : y; } This would expand similarly to the macro: void g(int x, float y) { float z = min(x, y); // expands into: // float z = x < y ? x : y; } It would have to have different specialisations; for non-symbol expressions, the following would be expanded: inline T min(T x, T y) { // as usual } Inline functions would of course have to have some kind of template-like implicit instantiation mechanism (which D templates is soon going to get, I hope) and ability to specialize on different types. And they could naturally be used to implement variable-sized argument lists type-safely... Hmm, I think I got sidetracked a bit. Could be time for sleep.Maybe you wanted something completely different?I was talking about the issue I mentioned in my last (statistical) example. You need to write things like: template stats(T) { const_if_array_type_is_const T find_min(const T [const] array) { ... } } That can be very awkward. The problem here is: the result type immutability depends on the array element type immutability, but we must be able to ensure that the array element type won't be changed by the function.Some issues regarding mutability are discussed here <http://lambda.weblogs.com/discuss/msgReader$8378?mode=topic&y=2003&m=8&d=25An interesting discussion. BTW, the guy who he was talking with, Vesa Karvonen, I used to work with some time ago. He's a smart guy; taught me a lot about programming. It's a small world, and particularly small in the area of functional languages and type theory :)Tim Sweeney has some pretty good thoughts.Well, that's what you get if you really want to compare two sets, only one of which is available at a time. I'd really just leave it, settle with simpler preconditions and place some trust on the programmer... I wouldn't find very complicated examples in Eiffel's libraries, either; in particular, no clone()'s in old expressions. I looked at, for example, the equivalent BINARY_SEARCH_TREE_SET at http://docs.eiffel.com/libraries/base/reference/structures/set/binary_search _tree_set.html#f_prune which has the similar postcondition removed_count_change: old has (v) implies (count = old count - 1) but does not have the last check. -AnttiUh, could you give an example of one hard case?Sorry I meant: 'Also it's very hard to write "ensure" clauses on contracts of methods that have side-effects (i.e. methods that mutate the instance)'. Set(T) { void remove(T item) out () { if (old this.contains(item)) { assert(this.size + 1 == (old this.size())); } assert(!this.contains(item)); // so far so good. for (T each; old this) { if (each != item) { assert(this.contains(item)); } } /* ops, it won't work because the "old this" expression will yield a reference * to the this object, and here it's already mutated. The correct expression is * "(old this.clone())" ensuring that we get a snapshot of the this object from * before the method execution. */ }; }
Aug 26 2003
Back to the min() problem. Ideas only keep getting wilder... well, do you remember how we did it in C times: Macro! Of course! #define min(x, y) ((x) < (y) ? (x) : (y)) This actually works better (except for the side-effects of call-by-name parameter passing) than any of its C++ equivalents, since you can do things like int x = 5; const float y = 1.0; float z = min(x, y); No need for different "const" methods, overloading or anything! Amazing!Then what we would like is is template with automatic return type deduction (which will also require implicit instanciation). This would be a great plus for metaprogramming. We may be able to uses standard template syntax for the function definition: template (R, S, T) R min(S s, T t) { return s < t ? s : t; } or we might uses a syntax that compute the return type: template (S, T) min(S s, T t) returns typeof(expr(s, t)) { } where the returns type is obtained from an expression or from some other deduction like a typedef inside template class: instance MyClass(S, T).my_typedef If we have otherwise about the same power as in C++ for templates (i.e. have the power to implement code similar to boost, C++ Template (book) or Modern C++ Design (book) and Loki), those addition would make metaprogramming far more powerfull that what we have in C++... We would have some new keyword like typeof, constnessof, and a few boost style function and template that would allows us to manipulate that information...Compare this with what Andrei Alexandrescu did to get the (nearly) similar effect with templates: http://www.cuj.com/documents/s=7996/cujcexp1904alexandr/ By the way, he has an interesting comment: "Minimum and maximum are two simple concepts present both in math and in the real life. If a programming language is unable to express simple concepts of math and life, then there is something deeply wrong with that language." Anyway - macros seem to work. Why is this possible? Well, because min() is not an actual function call but merely a textual substitution. Well, what language feature was introduced in C++ (and later, in C) to get rid of a certain usage of macros? Taking Andrei's challenge, let's bring forth the final solution: inline functions. I wouldn't mean to use inline functions as a means to perform premature optimization, a purpose which programmers are often accused of misusing. But I simply would want to take advantage of the fact that inline functions can "see" the context where they are used, and therefore they would also be able to provide different kinds of code than usual functions. Actually, after an inline function were expanded several times, the compiler could gather them to a single (if possible) function and optimize the inlined code to a function call :) One thing that inline functions could provide would be determining the return type based on argument types. In fact, the inline function could work directly on the symbolic environment of the caller; assume inline Symbol min(Symbol x, Symbol y) { return x < y ? x : y; } This would expand similarly to the macro: void g(int x, float y) { float z = min(x, y); // expands into: // float z = x < y ? x : y; } It would have to have different specialisations; for non-symbol expressions, the following would be expanded: inline T min(T x, T y) { // as usual } Inline functions would of course have to have some kind of template-like implicit instantiation mechanism (which D templates is soon going to get, I hope) and ability to specialize on different types. And they could naturally be used to implement variable-sized argument lists type-safely... Hmm, I think I got sidetracked a bit. Could be time for sleep.
Aug 26 2003
"Antti Sykäri" <jsykari gamma.hut.fi> escreveu na mensagem news:slrnbkntuv.83p.jsykari pulu.hut.fi...Ok, let's drop immutable and just talk about const.But I like immutable so much... ;) [snip]Okay, now I think I have an idea about the fundamental problem. The min() function can be used as an example of the general idea. The function must return one of its arguments, whether mutable or immutable. At the same time it must provide the guarantee that it won't modify its arguments -- we'd like the type system to ensure that the only thing that "min(T, T)" is allowed to do is to pass one of its arguments back to the caller.That's it.One way to ensure this is to make its arguments const; but then you'd have to make its return type const, as well, or the following wouldn't compile: (Let's leave the bodies away -- everybody probably knows what "min" looks like if he or she has read this far :) I'll drop the template syntax because it has nothing to do with const really. Assume that T is an int or actually, reference type would be better to illustrate the const semantics.[snip]Don't use "const" as a type modifier at all, but merely as a way of documenting the function's behavior (as in "I won't change this parameter"). Enter contracts. Like so: T min(const T x, const T y) { // implementation... } out() { // mathematical properties of min(x,y) assert(result <= x); assert(result <= y); // it's one of them assert(result === x || result === y); // I didn't change my parameters, I promise; these would be // generated by "const" assert(old x == x); assert(old y == y); // Here we would actually have to rely on // deep_clone or similar, as you mentioned below. Tricky notion, // comparing something that exists now to something that existed // earlier in time } Side note: Don't you just love when the function's postconditions actually describe perfectly what the function does! Reminds of me of the good old combinator function in purely functional languages. It takes a function of type ('a -> 'b) and a function of type ('b -> 'c) and returns a function of type ('a -> 'c). Now what could a function of this type do? The notation "'something" means generic type parameter, so it cannot assume anything about its types. Well, what else can it do, then, than create a function that first calls the first function and then calls the second function on the result? So the function's type is a complete description of the function itself.That would probably be a good compromise. But we still need a way to "attach constness" to a reference, so I can give to my caller a const reference. Otherwise we just solve one half of the problem. [snip interesting things about inline functions] Inline functions also solve just one part of the problem. Besides sometimes you can't inline the definitions (e.g. virtual functions).5Some issues regarding mutability are discussed here<http://lambda.weblogs.com/discuss/msgReader$8378?mode=topic&y=2003&m=8&d=2Yes, it's a very small world :)An interesting discussion. BTW, the guy who he was talking with, Vesa Karvonen, I used to work with some time ago. He's a smart guy; taught me a lot about programming. It's a small world, and particularly small in the area of functional languages and type theory :)Tim Sweeney has some pretty good thoughts.theUh, could you give an example of one hard case?Sorry I meant: 'Also it's very hard to write "ensure" clauses on contracts of methods that have side-effects (i.e. methods that mutateexpressioninstance)'. Set(T) { void remove(T item) out () { if (old this.contains(item)) { assert(this.size + 1 == (old this.size())); } assert(!this.contains(item)); // so far so good. for (T each; old this) { if (each != item) { assert(this.contains(item)); } } /* ops, it won't work because the "old this" expression will yield a reference * to the this object, and here it's already mutated. The correctobjectis * "(old this.clone())" ensuring that we get a snapshot of the thishttp://docs.eiffel.com/libraries/base/reference/structures/set/binary_searchfrom * before the method execution. */ }; }Well, that's what you get if you really want to compare two sets, only one of which is available at a time. I'd really just leave it, settle with simpler preconditions and place some trust on the programmer... I wouldn't find very complicated examples in Eiffel's libraries, either; in particular, no clone()'s in old expressions. I looked at, for example, the equivalent BINARY_SEARCH_TREE_SET at_tree_set.html#f_prune which has the similar postcondition removed_count_change: old has (v) implies (count = old count - 1) but does not have the last check. -AnttiThere are better contracts in the ELKS standards. They are pretty comprehensive (the String slicing includes more than 10 ensure clauses IIRC) and has to deal with some of these issues. Also there are papers only about this, specially one author talks about specifying mutability in contracts, to ensure that Set.put don't change the old contents of a Set, ditto for Stacks, Queues, etc., generalizing the notion to Observer design pattern. I don't seem to have the paper here, but if you're interested I can look around. It was about Eiffel, and using special deconstructors to decompose the object (in Stack he described an "item" operation returning the top of the stack and a "rest" operation giving everything except the top, in set he used a "choice" and "rest" pair of operations). --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.512 / Virus Database: 309 - Release Date: 19/8/2003
Aug 26 2003
Antti Sykäri <jsykari gamma.hut.fi> wrote in news:slrnbk5c5l.4q6.jsykari pulu.hut.fi:Java, a blatant mockery of static typing, solves the "const" by providing a view to the container (via java.util.Collections.unmodifiableCollection or similar) which looks exactly like the original, but throws an UnsupportedOperationException when one tries to modify it. No wonder there's a project trying to bring "const" to Java (http://pag.lcs.mit.edu/constjava/). [...]ConstJava looks like a straight adaption of C++'s const notion. They just added the useful notion (for Strings etc.) of const classes. The paper (http://pag.lcs.mit.edu/constjava/ConstJava.pdf) talks about a runtime exception for const_casts, but I didn't find any details. Wonder how this works. Farmer.
Aug 26 2003
"Fabian Giesen" <rygNO SPAMgmx.net> wrote in message news:bhrn8p$251q$1 digitaldaemon.com...I agree that such limitations can always be worked around with inlineassembleror union hacks or stuff like that, but that's not of interest to me; ifyou haveto go into the realm of undefined behavior in the first place to dosomething,it's perfectly okay if you get undefined behavior in return. :)Yet you can break const using legal and defined Standard-conforming code, as I posted in this thread. The optimizer could make use of const if breaking it was only in the realm of undefined behavior. But it is not, and so const is useless. Not that I don't have a strong opinion about it <g>.
Sep 18 2003
Yet you can break const using legal and defined Standard-conforming code,asI posted in this thread. The optimizer could make use of const if breaking it was only in the realm of undefined behavior. But it is not, and soconstis useless.It's only useless from the perspective of a compiler writer. For professional programmers (by which I mean those that observe the "contracts" of the libraries that they are using), it is anything but. I've found this whole debate quite perplexing. People are expending huge efforts to prove that const is useless from a strict point of view. I don't really think anyone would disagree with that proposition. (It'd be pretty hard to when we have "mutable" and "const_cast".) But no-one's argued successfully against the real value of const (which should have been named "readonly") which serves as Jiminy Cricket to the user. This is in just the same way that, say, speed limits on roads serve a useful function. They present an informed context within which you can exercise free will. You may choose to drive at 100mph on a winding mountain pass that has 50mph limit, but you bear the risk. I for one would not want to have engine-limiting mechanisms that prevent me from breaking the speed limit, even though most of the time I drive to it, in case I had some kind of emergency. In the same way, I'm quite happy as a coder (not a compiler writer) that const can be broken. Although such action is frowned upon, there are rare occasions where it is necessary. The majority of cases it is very good to have the (C++) compiler tell us that we cannot do something, albeit that we may recklessly choose to overwrite it. Yesterday you raised the issue of the deblank() function having the wrong semantics. If we had a readonly keyword in D, the issue would be moot. I want code to be self-documenting, and the compiler to help enforce that (to a reasonable degree). If we had "readonly", that could be achieved. I don't give a fig for optimisations based on putative vs actual read-only, and all that other guff, and will be thoroughly unperturbed if that is left out as it stands currently. But I remain strongly unconvinced that its absence from D is a +ve step. I'd be very interested to hear anyone to put forth an argument for such in response to what I (and others, who've commented similarly) want from a readonly/const keyword, rather than countering with the optimisation arguments.
Sep 18 2003
"Matthew Wilson" <matthew stlsoft.org> wrote in message news:bkdt7v$1vih$1 digitaldaemon.com...Yesterday you raised the issue of the deblank() function having the wrong semantics. If we had a readonly keyword in D, the issue would be moot.Not exactly. Consider the case of passing a parameter that is int****p. If it is 'readonly', what part of it is readonly? p? *p? **p? ***p? ****p? all of them? To resolve the issue with deblank(), more than just the string reference (i.e. p) would have to be readonly.I want code to be self-documenting, and the compiler to help enforce that (to a reasonable degree). If we had "readonly", that could be achieved. I don't give a fig for optimisations based on putative vs actual read-only, and all that other guff, and will be thoroughly unperturbed if that isleftout as it stands currently. But I remain strongly unconvinced that its absence from D is a +ve step. I'd be very interested to hear anyone to put forth an argument for such in response to what I (and others, who've commented similarly) want from a readonly/const keyword, rather than countering with the optimisation arguments.Fair enough, and I had given some earlier in this thread <g>. Let's discuss the one about self-documentation. I agree with you that code should be self-documenting, as I've argued that comments are invariably missing, out-of-date, incomplete, or wrong. The trouble with const being self-documenting is like checked exceptions in Java. Java issues a compile error if you call a function that throws something not listed in the exception specification. So, adding functionality to a function can entail going back through the entire static call chain editting all the exception specifications - so annoying that too many Java programmers, even good ones, tend to write try{f.foo();}catch{} just to shut up the compiler and get on with it. This, of course, completely subverts exception specifications and makes it far worse than not having exception specifications at all. (Even worse, despite being "exception specification correct", it is still possible to get exceptions not listed.) Const has similar problems. You can't just ignore const if it is in the language, as you'll need to inevitably interface with code that uses it. The shortest, easiest way to do it is to just throw in a few const_cast's and move on. Even worse, as I pointed out, there are legal, defined ways to pull the rug out from under const without using undefined behavior of unions, casts, inline assembler, etc. While the situation isn't near as bad as with exception specifications, the self-documenting nature of it isn't at all reliable. That said, there is a solution for const as a storage class. If it is put in read-only memory, it is const. No way around it, since the checking happens in hardware at runtime rather than compile time <g>.
Sep 19 2003
In article <bkeah6$2k5u$1 digitaldaemon.com>, Walter wrote:The trouble with const being self-documenting is like checked exceptions in Java. Java issues a compile error if you call a function that throws something not listed in the exception specification. So, adding functionality to a function can entail going back through the entire static call chain editting all the exception specifications - so annoying that too many Java programmers, even good ones, tend to write try{f.foo();}catch{} just to shut up the compiler and get on with it. This, of course, completely subverts exception specifications and makes it far worse than not having exception specifications at all. (Even worse, despite being "exception specification correct", it is still possible to get exceptions not listed.) Const has similar problems. You can't just ignore const if it is in the language, as you'll need to inevitably interface with code that uses it. The shortest, easiest way to do it is to just throw in a few const_cast's and move on. Even worse, as I pointed out, there are legal, defined ways to pull the rug out from under const without using undefined behavior of unions, casts, inline assembler, etc. While the situation isn't near as bad as with exception specifications, the self-documenting nature of it isn't at all reliable.This is a good summary of the similarities of const and exception specifications. But then we need to concentrate on the differences between them. - const is much more lightweight because it's just one bit of information in the interface. And you often get it right the first time. Even if you don't, you need to go through the call chain only once when you decide to add it. Exception specifications, on the other hand, can grow and grow, and (if you want to do it right) you have to traverse the call chain every time when you add an exception. - const affects the function's semantics directly. It tells what it can do with its arguments. This is unlike exception specifications, which might not even have anything to do with the function that they pass through. -Antti
Sep 19 2003
"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbmlrr3.hfa.jsykari pulu.hut.fi...This is a good summary of the similarities of const and exception specifications. But then we need to concentrate on the differences between them. - const is much more lightweight because it's just one bit of information in the interface. And you often get it right the first time. Even if you don't, you need to go through the call chain only once when you decide to add it. Exception specifications, on the other hand, can grow and grow, and (if you want to do it right) you have to traverse the call chain every time when you add an exception.If you add a const to a type, that can ripple downwards through the call chain, affecting a lot of functions that are quite irrelevant. Inevitably, one of those rippled functions will be modifying its argument. This has happened to me :-(. There's a powerful temptation to just put in a const_cast and forget about it rather than reengineer.- const affects the function's semantics directly. It tells what it can do with its arguments. This is unlike exception specifications, which might not even have anything to do with the function that they pass through.I agree that the const problem isn't nearly as bad as the exception specification one.
Sep 19 2003
IMO the problem in C++ is the fact that we have to explictly add const (for member functions) and not the other ways so it is easy to forgot it and (and this will happen in a third-party library you don't want to change)... so my opinion is that member function (and in parameter) should be const by default... That way most of the changes caused by an initial forgotten const would be avoided... const does have its used to validate code... and to ensure that an object would not be (reasonably) modified. In fact, if we knows an object should not be modified, we uses const and the compiler will issue an error if we accidently try to call a function that modify the object. Maybe, we should support constness only on predefined types, enumerations, pointers, struct and class that are final (no virtual members) and uses a constant interface for classes if we want to prevent modifications: interface Reader { } // Only read interface Writer : Reader { } // Modify object class MyClass : Reader, Writer { } // Actual class void f(Reader r) { } void g(Writer w) { } MyClass c; f(reader); Reader rc = c; g(rc); // Compile-time error So my opinion is that we should have const but modify it a bit to avoid the common pitfall of C++ (forgetting a const on a method). For parameter, D is already better that C++ since it has in, out and inout.... with the proper default (i.e. in --- assuming in means const).This is a good summary of the similarities of const and exception specifications. But then we need to concentrate on the differences between them. - const is much more lightweight because it's just one bit of information in the interface. And you often get it right the first time. Even if you don't, you need to go through the call chain only once when you decide to add it. Exception specifications, on the other hand, can grow and grow, and (if you want to do it right) you have to traverse the call chain every time when you add an exception.If you add a const to a type, that can ripple downwards through the call chain, affecting a lot of functions that are quite irrelevant. Inevitably, one of those rippled functions will be modifying its argument. This has happened to me :-(. There's a powerful temptation to just put in a const_cast and forget about it rather than reengineer.- const affects the function's semantics directly. It tells what it can do with its arguments. This is unlike exception specifications, which might not even have anything to do with the function that they pass through.I agree that the const problem isn't nearly as bad as the exception specification one.
Sep 19 2003
"Philippe Mori" <philippe_mori hotmail.com> wrote in message news:bkg1k3$q8b$1 digitaldaemon.com...exception.This is a good summary of the similarities of const and exception specifications. But then we need to concentrate on the differences between them. - const is much more lightweight because it's just one bit of information in the interface. And you often get it right the first time. Even if you don't, you need to go through the call chain only once when you decide to add it. Exception specifications, on the other hand, can grow and grow, and (if you want to do it right) you have to traverse the call chain every time when you add anInevitably,If you add a const to a type, that can ripple downwards through the call chain, affecting a lot of functions that are quite irrelevant.canone of those rippled functions will be modifying its argument. This has happened to me :-(. There's a powerful temptation to just put in a const_cast and forget about it rather than reengineer.- const affects the function's semantics directly. It tells what itwhichdo with its arguments. This is unlike exception specifications,in does not mean const. The in/out/inout applies to the parameter (reference) whereas the readonly applies to the object. They're quite different. (I'm not calling it const, because "const" sucks as a term, and if D gets a "readonly" this will enable it to forgoe many of the problems with const without being accused of overlooking something)IMO the problem in C++ is the fact that we have to explictly add const (for member functions) and not the other ways so it is easy to forgot it and (and this will happen in a third-party library you don't want to change)... so my opinion is that member function (and in parameter) should be const by default... That way most of the changes caused by an initial forgotten const would be avoided... const does have its used to validate code... and to ensure that an object would not be (reasonably) modified. In fact, if we knows an object should not be modified, we uses const and the compiler will issue an error if we accidently try to call a function that modify the object. Maybe, we should support constness only on predefined types, enumerations, pointers, struct and class that are final (no virtual members) and uses a constant interface for classes if we want to prevent modifications: interface Reader { } // Only read interface Writer : Reader { } // Modify object class MyClass : Reader, Writer { } // Actual class void f(Reader r) { } void g(Writer w) { } MyClass c; f(reader); Reader rc = c; g(rc); // Compile-time error So my opinion is that we should have const but modify it a bit to avoid the common pitfall of C++ (forgetting a const on a method). For parameter, D is already better that C++ since it has in, out and inout.... with the proper default (i.e. in --- assuming in means const).might not even have anything to do with the function that they pass through.I agree that the const problem isn't nearly as bad as the exception specification one.
Sep 19 2003
I should have said implies... An in parameter should not be modified inside a function (and if const support is eventually added to the language, it should be the default for in parameters... and otherwise it should be undefined what happen if we change a in parameter (the change may or may not be visible to the caller depending on werither a copy was made (a bit like in parameter in COM programming... If a proxy is used (remote call), then the change will not be visible and otherwise we should make changes...So my opinion is that we should have const but modify it a bit to avoid the common pitfall of C++ (forgetting a const on a method). For parameter, D is already better that C++ since it has in, out and inout.... with the proper default (i.e. in --- assuming in means const).in does not mean const. The in/out/inout applies to the parameter (reference) whereas the readonly applies to the object. They're quite different.(I'm not calling it const, because "const" sucks as a term, and if D getsa"readonly" this will enable it to forgoe many of the problems with const without being accused of overlooking something)const and readonly are different concept... as generaly used. Typically by const, we want to talk about a C++ style constness (logical) while when readonly is used, we want to talk about hardware style readonly (physical). Ideally both concept should be exprimable with proper construct... I would like a language that would allows to express more such concepts than C++ but it seems that we make tradeoffs... we have added some new stuff (design by contract, static assertion, ...) but OTOH, we remove some usefull things, IMHO, that where present in C++... I would prefer to have more even if not everybody uses everything...
Sep 19 2003
In article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen says...Remember that const is type information. The same could be argued of any ability of the programmer to overrule the static type system (non-dynamic downcasting, anyone?). Does the existence of such mechanisms negate the value of static type checking? Certainly not. As has been pointed out, C++ const is not an enabler for compiler optimizations, nor is it meant to be. What it does do is enable a programmer to say something about how an object should be used or how he uses an object and have the compiler enforce it. (Maybe I'm just lazy, but I like it when I'm able to get the computer to do grunt work so I don't have to. It's better at that sort of thing anyway.) And, just like with other forms of static type checking, there exist mechanisms (casts) to allow you to explicitly lie to the compiler, albeit at your own risk. Lastly, the idea that const should always imply bitwise immutability is a misconception IMO. The usual example is of an object that does some computationally expensive operation. If the externally visible state of the object cannot be changed by the operation, then the operation is logically const and should be labeled so even if the implementation does something mutable internally (say, caching the results). The operations on the cache may not be const, but if it's entirely an implementation detail then it makes no difference to the constness of the operation. Scott McCaskillSo the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.Unless someone casts your donstness away, which anyone can do without you ever knowing. Which means that const is really just a vague promise.
Aug 27 2003
Lastly, the idea that const should always imply bitwise immutability is a misconception IMO. The usual example is of an object that does some computationally expensive operation. If the externally visible state oftheobject cannot be changed by the operation, then the operation is logicallyconstand should be labeled so even if the implementation does something mutable internally (say, caching the results). The operations on the cache maynot beconst, but if it's entirely an implementation detail then it makes nodifferenceto the constness of the operation.Then your have mutable members for thinks that should not appears const (in worst case scenario) all members would end-up as being mutable... so your external state can be what you want it to be anyway. We might also need mutable function that would be allowed to be called from a const object but would be allowed to only changes mutable members and this should help remove the need to cast away constness in more situations that C++ does.... The real problem is when using code that cannot be modified and for which constness is not properly done.... but I think that if we uses mutable for those objects where some functions aren't const when they should, it would help... Also for parameters, since in is the default, we have implicit constness for those arguments... which should also help to get it right from the beginning and have properly written libraries... If more control is needed, then we might add immutable keyword in addition to const...
Aug 27 2003
"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message news:slrnbjq9vr.mtu.jsykari pulu.hut.fi...So the standard says that if we've got pointer to a const object, we can't change it, but does not indeed say the object would be immutable. Which is quite convenient: when you call a function with signature void f(const T*); you can be certain that it won't change your argument.The problem is that the standard doesn't preclude anyone *else* from changing the const data. For example: void f(int *p1, const int *p2) { int i = *p1; (*p2)++; int j = *p1; // oops! j != i } int *a; ... f(a, a); This is perfectly legal and defined behavior. This is one reason why const as a type modifier is useless for optimizers. And yes, this kind of code (in obfuscated ways) happens in real code. I know, because I found out about it the hard way :-(
Sep 18 2003
Walter wrote:The problem is that the standard doesn't preclude anyone *else* from changing the const data. For example: void f(int *p1, const int *p2) { int i = *p1; (*p2)++;^^^^^^^ Er, what? The integer pointed to by p2 cannot be modified when accessed through p2. At least that's what the FAQ says. When looking at the ANSI C++ spec, i cannot figure anything out. It's apparently been written by people not at the peak of comprehesibility. The DMC agrees with me. Where is a step which assigns to *p2 without casting away const-ness one or another way? What is a standard compliant way which would not result in undefined behaviour? And in which cases does const_cast exactly have well-defined behaviour?int j = *p1; // oops! j != i } int *a; ... f(a, a); This is perfectly legal and defined behavior.????????????????????????????????????????????????????? ?? Am i going nuts or is actually something wrong ??? ????????????????????????????????????????????????????? -eye
Nov 03 2003
"Ilya Minkov" <minkov cs.tum.edu> wrote in message news:bo6k86$2dpj$1 digitaldaemon.com...Walter wrote:Ah, I have it backwards. Put the const in front of p1.The problem is that the standard doesn't preclude anyone *else* from changing the const data. For example: void f(int *p1, const int *p2) { int i = *p1; (*p2)++;^^^^^^^ Er, what?
Nov 03 2003
"Bill Cox" <bill viasic.com> wrote in message news:bhaupl$12t1$1 digitaldaemon.com...Matthew Wilson wrote:[cut]From my point of view: 1) full const support is good 2) to write constst there is pain So I think the const should be in a language, it should be explicit and editor, should be able to add consts to my code when I write it and ask for the anotation. In the worst case compiler could do it. It is nice to see consts in souce code when you try to understand it, even when they can lie, it is an idicator what was intended. On the other side, they are not critical.Yes. Definately. Why not just have the compiler try to prove that constant data is never modified, rather than having me put 'const' keywords in all my parameter declarations? Is this what you're getting at? If so, I'm for it. It would eliminate all the cast nonsense 'const' creates when we use eachother's code.Does anyone else think const in C++ is a poor attempt at letting the user decorate info that a sophisticated enough compiler could be able to figure out on its own? Somewhat akin to the "register" keyword, which "aids" the compiler by disallowing any possibility of aliasing the value stored?
Aug 12 2003
"Bill Cox" <bill viasic.com> wrote in message news:bhaupl$12t1$1 digitaldaemon.com...Why not just have the compiler try to prove that constant data is never modified, rather than having me put 'const' keywords in all my parameter declarations? Is this what you're getting at? If so, I'm for it. It would eliminate all the cast nonsense 'const' creates when we use eachother's code.Frankly, I think the way to do this is to put const data into a 'read only' data segment. Then, the hardware does the checking for you. There would be no way to use clever casting, mutable, or other gymnastics to defeat it. And there'd be no need whatsoever to use const as a type modifier. Compilers for embedded systems tend to do this already, as the const data gets burned into a ROM. That's another big reason why I prefer const as a storage class.
Sep 18 2003
"Sean L. Palmer" <palmer.sean verizon.net> wrote in message news:bha4n0$8q2$1 digitaldaemon.com...Does anyone else think const in C++ is a poor attempt at letting the user decorate info that a sophisticated enough compiler could be able to figure out on its own? Somewhat akin to the "register" keyword, which "aids" the compiler by disallowing any possibility of aliasing the value stored?It'salso something the compiler can figure out for you, something that modern compilers do. They even do a pretty good job at it, from what Iunderstand,though C++'s laxity with pointers allows hideous data flow spaghetti that even the best compilers cannot sort out.that's only "register" in C (afaik) register in C++ just means do things as fast as pos, try this as hello.cpp and hello.c with gcc void loadup( int * fp, int iv ) { *fp = iv; } int main( int argc, char * argv[] ) { register int foo; loadup( &foo, argc ); return 0; } gcc hello.cpp (works no errors) gcc hello.c --- hello.c: In function `main': hello.c:7: warning: address of register variable `foo' requested I agree c++ const is very poor, and one of the biggest reasons ppl think gcc -03 is broken, if you use const slightly wrong it can optimise away code that should be run (and if you miss the odd volatile too). I was just about to write a page on the great uses of const or final as contracts between user and library writer when I realised ... its all pointless (Walter you've make a convert out of me, although volatile I think should still be possibly attached to an pointer (hw reg address)) initially I considered two things, one was having "signal in" which like the vhdl signal in means that the value is readable only by the function (unlike now where I can use params as locals {I think this is bad and evil, especially on risc arch's where is forces the compiler to flush the value from reg to stack}) and having a way to say "this 'ere item I've passed you by reference can will not get modified by the function I is calling" so as an example I chose strcpy if its extern then you have to say, this does not modify the src. extern (C) c_strcpy( char * dest, final char * src, int len ); if it where in D then the compiler would know `*src` is only read. void d_strcpy( char * dest, char * src, int len ) { while ( len--> 0 ) *dest++ = *src++; } either can be called with x_strcpy( foo, bar, 3 ); or x_strcpy( foo, final bar, 3 ); the latter would fail to compiler if for some reason x_strcpy was changed to write into the value referenced by src. of course this is all rather pointless as src and dest might overlap! I do belive that there is a real need to have an "assert if modified by function" or a way to pass by reference a value that "should" not change (assert if it does) as part of the lang rather than adding asserts and checks all throughout your code (same for closures and reference to stack items, these should not be storeable on the heap or in a stack frame behind them (where you came from stack dir) [so passable but not storeable outside the local scope and not returnable (aliases to them would be restricted to the same rules too)] you mention D as design by contract, will one of the contracts should be "I don't modify your values you've passed me by reference" this may seem like i'm saying give me const, but no, I would like a way a library writer can say I'll not change your stuff, and an application writer can say I only want to call this if it does no modify my values. they are not const they are immutable within the local scope.
Aug 12 2003
So you're saying you want a version of 'in' that works by reference always? Usually the compiler would be smart enough to do that when appropriate, but one huge thing about pass by value vs. pass by reference is that either you're going to change the source data and you don't want the function to be affected (need pass by value) or you're going to change the source data (say in another thread) and want the function to get the updates immediately. 'inout' does this, but it allows the function to write to the parameters. I believe the default parameter passing convention should be pass by value. I'll let you guys debate the usefulness of allowing the function to modify its value parameters. If you want pass by reference, use 'in' for readonly reference, 'inout' for read/write reference, 'out' for writeonly reference. Sean "Mike Wynn" <mike.wynn l8night.co.uk> wrote in message news:bhb8hd$1e0o$1 digitaldaemon.com...I do belive that there is a real need to have an "assert if modified by function" or a way to pass by reference a value that "should" not change (assert if it does) as part of the lang rather than adding asserts and checks all throughout your code (same for closures and reference to stack items, these should not be storeable on the heap or in a stack framebehindthem (where you came from stack dir) [so passable but not storeableoutsidethe local scope and not returnable (aliases to them would be restricted to the same rules too)] you mention D as design by contract, will one of the contracts should be"Idon't modify your values you've passed me by reference" this may seem like i'm saying give me const, but no, I would like a way a library writer can say I'll not change your stuff, and an applicationwritercan say I only want to call this if it does no modify my values. they are not const they are immutable within the local scope.
Aug 13 2003
"Sean L. Palmer" <palmer.sean verizon.net> wrote in message news:bhdq4e$qj2$1 digitaldaemon.com...So you're saying you want a version of 'in' that works by referencealways? yes (basically) currently for example extern(Windows) BOOL ClipCursor( RECT * lpRect ); which in C is BOOL ClipCursor( CONST RECT * lpRect ); I guess is could be in D as extern(Windows) BOOL ClipCursor( inout RECT lpRect ); but its not either realy its extern(Windows) BOOL ClipCursor( in byref RECT lpRect ); as the API is defined to not to modify the RECT struct who's address you've passed.Usually the compiler would be smart enough to do that when appropriate,butone huge thing about pass by value vs. pass by reference is that either you're going to change the source data and you don't want the function tobeaffected (need pass by value) or you're going to change the source data(sayin another thread) and want the function to get the updates immediately. 'inout' does this, but it allows the function to write to the parameters.the compiler (like most C++/C compilers [and Java] I believe) assume that any value not marked as volatile are not modified by any other thread during the function, so globals can be cached at any time, but not cached over function calls [they might modify globals anyway]I believe the default parameter passing convention should be pass byvalue.I'll let you guys debate the usefulness of allowing the function to modify its value parameters. If you want pass by reference, use 'in' for readonly reference, 'inout'forread/write reference, 'out' for writeonly reference.alas D does not have "reference" only pointer (personally I think D should ditch pointer (has to be array slice) and allow references `struct X * const foo` (auto derefed like java, which it already has for object rather than c++ & which can't be rereferenced) (obviously extern C need some rules about what to pass to remain compatible)
Aug 13 2003
Bill Cox wrote:I would be very interested in hearing about the utility of const. I'm not disputing your assertion, but I haven't had the same experience.It took me looooooong to remember... i was telling to myself all the time: there must be some use, there must be some use... and now i think i finally recall it! Temporaries, generated in C++ expressions, are const. This has to be to insure they can be cleaned up correctly. That is, making a function work with const, makes it more flexible in respect to temporaries. I can recall similar rules for some circumstances in (IIRC) Delphi and certainly Sather. I vaguely recall const solves thus memory leaks on temporaries. Maybe i've gotten something wrong. Just correct me. In D, this should be a non-issue because of GC -- except for performance. And i still don't think i understand, what's the sense of casting away const-ness - we have our mutable fields. Can anyone come up with a sane example? -eye
Aug 17 2003
Temporaries, generated in C++ expressions, are const. This has to be to insure they can be cleaned up correctly. That is, making a function work with const, makes it more flexible in respect to temporaries. I can recall similar rules for some circumstances in (IIRC) Delphi and certainly Sather.Nope, whether something's const or not doesn't change a bit about how it's cleaned up. There are two cases in which const makes a difference: 1. Temporaries are const so they cannot be passed by reference, but by const reference. This is mainly a convenience thing, while forbidding references to temporaries is a style issue - references to temporaries was considered to cause many problems when it was still allowed. Don't know whether it was a really big issue or not though, wasn't using C++ at the time :) 2. For some data structures, certain optimizations can be made when it's known that the data structure isn't changed during traversal; so for certain classes calling functions with const parameters or on const objects will cause completely different code to be executed than without. This can cause both notable performance advantages and notable headaches during debugging when you're not expecting it. :)And i still don't think i understand, what's the sense of casting away const-ness - we have our mutable fields. Can anyone come up with a sane example?Casting away const-ness should be outright forbidden and done away with, because as I already argued it renders the whole const qualifier into just a vague promise. However mutable is relatively recent, so I guess const_cast is still in C++ for backwards-compatibility issues (like so many things). -fg
Aug 18 2003
In article <bhqmei$l2q$1 digitaldaemon.com>, Fabian Giesen wrote:Casting away const-ness should be outright forbidden and done away with, because as I already argued it renders the whole const qualifier into just a vague promise. However mutable is relatively recent, so I guess const_cast is still in C++ for backwards-compatibility issues (like so many things)."Dag Brück and others reviewed considerable amounts of real code to see which casts were casting away const and which of those could be eliminated using 'mutable'. This study confirmed the conclusion that "casting away 'const'" cannot be avoided in general and that 'mutable' appears to eliminate casting away 'const' in less than half of the cases where it is needed in the absence of 'mutable'." D&E, §13.3.3 -Antti
Aug 18 2003
Antti Syk=E4ri wrote:"Dag Br=FCck and others reviewed considerable amounts of real code to s=eewhich casts were casting away const and which of those could be eliminated using 'mutable'. This study confirmed the conclusion that "casting away 'const'" cannot be avoided in general and that 'mutable' appears to eliminate casting away 'const' in less than half o=fthe cases where it is needed in the absence of 'mutable'."That's about changing existing code - which might be unreasonable... When designing new code, things are different. There is an opinion, that = when someone casts away const-ness, this means illogical design which=20 can be avoided from the beginning on. I don't have enough experience to=20 say whether it's true or not, but i would like to think that this=20 statement is true. I'm looking for counter-examples: maybe anyone can=20 give me a hint? -eye
Aug 18 2003
IMO, if the default was const, the situation would be a bit better... since forgetting the modifier would be catched as soon as someone try to modify something instead of waiting till someone try to pass a const something where a modifiable object is expected. I think that const is clearly desirable for pointers to struct, intergral type, other pointers (multi-level indirection) and enumeration. For class object, I think it is a bit less usefull and a bit more painfull since for member functions we could need both cases particulary if we want to returns a pointer (reference) to a sub-object (or a container element).... but OTOH const in those case in very usefull as we can provide different behavior (direct access vs a copy) for optimisation purpose... and it allows us tio ensure that at the same time we can prevent copy and modification of a given object... I also agree that in well designed code, const_cast should not be necessary at all. The problem is often in libraries where some const are missing and we try to uses them from correct code where const is not missing... I think that the best would be that we have a way to force the compiler but by default the compiler could be smarter (i.e. verify itself if something is modified) in m any cases. I do think that in, inout and out will help solve most of the problem related with constness as by default parameters are in (which should imply either by value or const reference) "Ilya Minkov" <midiclub 8ung.at> a écrit dans le message de news:bhrhij$1suj$1 digitaldaemon.com... Antti Sykäri wrote:"Dag Brück and others reviewed considerable amounts of real code to see which casts were casting away const and which of those could be eliminated using 'mutable'. This study confirmed the conclusion that "casting away 'const'" cannot be avoided in general and that 'mutable' appears to eliminate casting away 'const' in less than half of the cases where it is needed in the absence of 'mutable'."That's about changing existing code - which might be unreasonable... When designing new code, things are different. There is an opinion, that when someone casts away const-ness, this means illogical design which can be avoided from the beginning on. I don't have enough experience to say whether it's true or not, but i would like to think that this statement is true. I'm looking for counter-examples: maybe anyone can give me a hint? -eye
Aug 18 2003
The canonical example of a good/useful const_cast in C++ is to avoid duplicate code when you have const and non-const methods that do the same thing. Say I have some kind of array-like class in C++: class MyArray { public: T& operator[](unsigned i) { // (imagine non-trivial code here) return result; } const T& operator[](unsigned i) const { // Without const_cast, I'd be forced to copy & paste (and maintain!) // the non-trivial code from above. However, as the implementor of // this class, I _know_ that this is a perfectly sensible cast, therefore // the language should not get in my way by trying to save me from // myself. return const_cast<MyArray*>(this)->data()[i]; } }; I haven't used D enough to have a good feel for how much it might or might not benefit from having something like C++ const, but I do know that I would definitely miss it from C++ in spite of its shortcomings. It's extra type info, which is quite useful in a very static-type-oriented language such as C++. It is _immensely_ useful to be able to specify all the places that I, the programmer, think that something shouldn't be changed and then enlist the help of the compiler to check all of those places automatically at compile time. Yes it can be used inappropriately, but the fact that a language feature can be abused does not mean it's not useful or does not have its place (witness goto, pointers, unions, inline asm). I find such arguments appealing from a theoretical perspective but inadequate in practice. You could rely on run time tests (DBC/unit tests/assert/whatever), but what if the contract violation happens only under certain rare circumstances? In this case, the compiler has the advantage of seeing _all_ of the code, regardless of how often (or if) it actually gets executed. Even if the unit tests do catch all such violations, I would think it would be preferable to find out about them sooner if it's easy to do so. DBC/unit tests and static type checking are compliments, not substitutes. -- Scott McCaskill "Ilya Minkov" <midiclub 8ung.at> wrote in message news:bhrhij$1suj$1 digitaldaemon.com... Antti Sykäri wrote:"Dag Brück and others reviewed considerable amounts of real code to see which casts were casting away const and which of those could be eliminated using 'mutable'. This study confirmed the conclusion that "casting away 'const'" cannot be avoided in general and that 'mutable' appears to eliminate casting away 'const' in less than half of the cases where it is needed in the absence of 'mutable'."That's about changing existing code - which might be unreasonable... When designing new code, things are different. There is an opinion, that when someone casts away const-ness, this means illogical design which can be avoided from the beginning on. I don't have enough experience to say whether it's true or not, but i would like to think that this statement is true. I'm looking for counter-examples: maybe anyone can give me a hint? -eye
Aug 19 2003
Scott McCaskill wrote:The canonical example of a good/useful const_cast in C++ is to avoid duplicate code when you have const and non-const methods that do the same thing. Say I have some kind of array-like class in C++:Why do you need a non-const version at all? -eye
Aug 20 2003
Why do you need a non-const version at all?Because the (class-)const version only returns a const reference. -fg
Aug 20 2003
"Ilya Minkov" <midiclub 8ung.at> wrote in message news:bhvj1j$1o2o$1 digitaldaemon.com...Scott McCaskill wrote:sameThe canonical example of a good/useful const_cast in C++ is to avoid duplicate code when you have const and non-const methods that do theSo I can write this: // given: MyArray& a a[0] = 5; -- Scott McCaskillthing. Say I have some kind of array-like class in C++:Why do you need a non-const version at all?
Aug 20 2003
1) Too complicated (look at all the wierd rules and perterbations itcauseswith template specialization and overloading).I think this is right that the interaction is too complicated and it often cause duplicate code... but IMO we should modify the ways it works to avoid the problems while keeping the benefits. Also one think that make it complicate in C++ is the way declaration works particulary when there are some indirection level (i.e. pointers). For example, in C++, we have const int a; int const a; That mean the same thing. Alone, it is not too complicated but with pointers this can causes some confusion...2) Too confusing.I think we should modify the concept and have distinct keyword for each things... This is done at least in part by using in, inout and out parameter...3) Insufficient utility.I don't agree on that. I think if the compiler would be validating it and it would be impossible to break it (i.e. no const_cast or a different keyword for immutable data), then it would much more usefull.4) Optimizers can't reliably use the info anyway.It should be done in a way that the optimizer would be able to uses the information because the compiler should guaranties that constant data is not modified (by any means) and should not provide a way to break it. We should not allows removing const anywhere... and we should not allows aliasing to occurs globally...5) Ugly.What it is ugly IMO with C++ solution is that it cause code duplication that should not occurs in some cases. I think this should be handled by having more keyword so that things can be expressed exactly... For example, we should have a modifier "variable" that would be the opposite of "const" and we should also have "immuable" which would be the opposite of mutable (or does inout and out parameters is sufficient ?). I don't think so at least when pointer are used. I think that ideally we should be able to express the concept that a function won't modify the data (in parameter) and the concept that some data could not be modified (immutable data). immutable data could be passed to const function without problem but it would be also be possible to define function that only accept immutable (or static) data so that the compiler could do some optimizations... I think it should also be possible to express the constness of something (for example the result of a function including members) from the constness of something else (this, result of another function call, parameters,...) including combinaisons... (i.e. any const implies result is const)."Matthew Wilson" <matthew stlsoft.org> wrote in message news:bgvdh2$1t4n$1 digitaldaemon.com...What's the rationale again? "Walter" <walter digitalmars.com> wrote in message news:bgvd22$1sna$1 digitaldaemon.com..."Chris Sokol" <chris echosproject.org> wrote in message news:bgv1t6$1ijj$1 digitaldaemon.com...Does D support it at all?D supports const as a storage class, but not as a type modifier.
Aug 12 2003