www.digitalmars.com         C & C++   DMDScript  

D - c++ style const concept

reply Chris Sokol <chris echosproject.org> writes:
Does D support it at all?
Aug 07 2003
parent reply "Walter" <walter digitalmars.com> writes:
"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
parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
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
parent reply "Walter" <walter digitalmars.com> writes:
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
next sibling parent reply Farmer <itsFarmer. freenet.de> writes:
"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
next sibling parent "Walter" <walter digitalmars.com> writes:
"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:

 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;
 }
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
prev sibling parent "Philippe Mori" <philippe_mori hotmail.com> writes:
"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:

 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:
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
prev sibling next sibling parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
 1) Too complicated (look at all the wierd rules and perterbations it
causes
 with 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 wrong
 4) Optimizers can't reliably use the info anyway.
Have no knowledge here
 5) Ugly.
Not meaningful. Most things in SE are ugly until you get used to them like, say, D's templates ... ;)
Aug 09 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew stlsoft.org> wrote in message
news:bh3rhp$u1$1 digitaldaemon.com...
 1) Too complicated (look at all the wierd rules and perterbations it
causes
 with 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.
'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.
 3) Insufficient utility.
You couldn't be more wrong
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.
 4) Optimizers can't reliably use the info anyway.
Have no knowledge here
I 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.
 5) Ugly.
Not meaningful. Most things in SE are ugly until you get used to them
like,
 say, 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
parent reply Bill Cox <bill viasic.com> writes:
3) Insufficient utility.
You couldn't be more wrong
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.
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 11 2003
next sibling parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
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...
3) Insufficient utility.
You couldn't be more wrong
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.
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
next sibling parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
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.  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...
3) Insufficient utility.
You couldn't be more wrong
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.
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
parent reply Bill Cox <bill viasic.com> writes:
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 message
 
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.
The 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; }
 const
a[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 certainly wouldn't want a language to permit anyone to modify data I
declare as a constant.
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.
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?
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.
 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.
So do the rest of us.
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.
I agree. However, Walter's asserted that the compiler doesn't get to do such optimizations. I'm always for making faster code. Bill
Aug 12 2003
next sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
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
parent reply "Fabian Giesen" <rygNO SPAMgmx.net> writes:
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
next sibling parent "Philippe Mori" <philippe_mori hotmail.com> writes:
"Fabian Giesen" <rygNO SPAMgmx.net> a écrit dans le message de
news:bhggt3$fiq$1 digitaldaemon.com...
 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.
So IMO we need a way that allows us to tell the compiler that we want strict guarantees for some data...
 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 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
prev sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bhggt3$fiq$1 digitaldaemon.com>, Fabian Giesen wrote:
 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".
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.
Aug 15 2003
next sibling parent reply "Fabian Giesen" <rygNO SPAMgmx.net> writes:
 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
next sibling parent "Philippe Mori" <philippe_mori hotmail.com> writes:
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
prev sibling next sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen wrote:
 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.
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. -Antti
Aug 17 2003
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"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:
 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.
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.
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])
Aug 17 2003
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
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...
 In article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen wrote:
 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.
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.
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])
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 17 2003
next sibling parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"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
prev sibling next sibling parent "Matthew Wilson" <matthew stlsoft.org> writes:
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:
 "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:
 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.
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.
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])
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
prev sibling parent reply "Fabian Giesen" <rygNO SPAMgmx.net> writes:
 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
next sibling parent reply "Peter Hercek" <vvp no.post.spam.sk> writes:
"Fabian Giesen" <rygNO SPAMgmx.net> wrote in message
news:bhrn8p$251q$1 digitaldaemon.com...
 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).
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.
Aug 18 2003
parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 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.
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
prev sibling next sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bhrn8p$251q$1 digitaldaemon.com>, Fabian Giesen wrote:
 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).
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 19 2003
next sibling parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
"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:
 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).
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.
I 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!
 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
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++).
 - 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
For 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
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bhug4k$78v$1 digitaldaemon.com>, Philippe Mori wrote:
 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
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.
 - 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
For 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.
Can 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 }
 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
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 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.
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...
 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).
 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.
 Can 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)...
 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? :)
I 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...
Aug 23 2003
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bi9df5$1bh7$1 digitaldaemon.com>, Philippe Mori wrote:
 
 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.
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.
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)
 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? :)
I see a few cases: - The functions is implictly generated
I guess there aren't implicitly generated functions in D currently (I might be wrong?)
 - 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 X
 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. -Antti
Aug 24 2003
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 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 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?)

 - 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
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...
 - 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 X
Compile 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 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.
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...)
Aug 25 2003
next sibling parent Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bid8ts$12qg$1 digitaldaemon.com>, Philippe Mori wrote:
 (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 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
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.
 D has deprecated: http://www.digitalmars.com/d/attribute.html
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...
Sounds 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. -Antti
Aug 25 2003
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Philippe Mori" <philippe_mori hotmail.com> wrote in message
news:bid8ts$12qg$1 digitaldaemon.com...
 D has deprecated: http://www.digitalmars.com/d/attribute.html
But does it is possible to control in which module or on some other condition when deprecated take effects.
No. It applies to any use of a function.
 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.
 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 X
Compile 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:
This is now done with a new feature, static asserts.
Sep 19 2003
prev sibling next sibling parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
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:
 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).
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
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 I'm a heavy const user, a true believer in it, I'd say.  I'm going to
throw
 out 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 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.
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.  Just
put
 it 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.
So
 readonly sections can overlap public/private sections, or be overlapped by
 them.  readonly and readwrite might belong in the same category as
mutable.

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 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.
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 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
maybe, 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 initialization
for
 these members
And 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 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
 }
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 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 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 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
I 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
parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
"Philippe Mori" <philippe_mori hotmail.com> wrote in message
news:bi0eoa$2uk4$1 digitaldaemon.com...
 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.
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...
Hmm. 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.
 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.
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.
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.
 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
maybe, we should have a syntax like pure virtual function: this(in) = default;
I never liked that syntax. I'd rather have the default in front, so it can be an attribute too!
 new Point a(x:1.0, y:2.0, z:0.0);   // call this(in) with initialization
for
 these members
And initialize other to default? If so, this could be nice...
Yes, unspecified fields would get their type's default, or whatever the class initializes them to by default.
 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 is
not
 completed (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 error
and
 which 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 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'm not sure. Haven't thought about that enough.
 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
parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 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.
This is just making it easier for people to be lazy. ;)
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)
Aug 23 2003
prev sibling next sibling parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
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/9962
Unfortunately 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).
 -Antti
Best 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
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
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 type
One 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 function
     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
     }
 }
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 hard
I'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
parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
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.

 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
One 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?
If 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".
 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.
 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 function
     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
     }
 }
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.
Hmmm, 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.
     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
 I'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=25
 Tim Sweeney has some pretty good thoughts.
 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?
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]
     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
As 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/2003
Aug 26 2003
next sibling parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
"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 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.
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 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.
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
parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
----- 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]
     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.
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?
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
prev sibling next sibling parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
     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.


     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
 I'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:
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
parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"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
prev sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
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]
 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.
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.
      Some issues regarding mutability are discussed here
<http://lambda.weblogs.com/discuss/msgReader$8378?mode=topic&y=2003&m=8&d=25
 Tim Sweeney has some pretty good thoughts.
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 :)
 Uh, 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. */ }; }
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. -Antti
Aug 26 2003
next sibling parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 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
prev sibling parent "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"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).
      Some issues regarding mutability are discussed here
<http://lambda.weblogs.com/discuss/msgReader$8378?mode=topic&y=2003&m=8&d=2
5
 Tim Sweeney has some pretty good thoughts.
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 :)
Yes, it's a very small world :)
 Uh, 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.
  */
     };
 }
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.

 -Antti
There 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
prev sibling parent Farmer <itsFarmer. freenet.de> writes:
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
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"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 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. :)
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
parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
 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.
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
parent reply "Walter" <walter digitalmars.com> writes:
"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 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.
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
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
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
parent reply "Walter" <walter digitalmars.com> writes:
"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
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 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.
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).
Sep 19 2003
parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
"Philippe Mori" <philippe_mori hotmail.com> wrote in message
news:bkg1k3$q8b$1 digitaldaemon.com...
 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.
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).
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)
Sep 19 2003
parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 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 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...
 (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)
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
prev sibling parent reply scott_mccaskill yahoo.com writes:
In article <bhmc8c$h12$1 digitaldaemon.com>, Fabian Giesen says...
 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.
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 McCaskill
Aug 27 2003
parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 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.
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
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"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
parent reply Ilya Minkov <minkov cs.tum.edu> writes:
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
parent "Walter" <walter digitalmars.com> writes:
"Ilya Minkov" <minkov cs.tum.edu> wrote in message
news:bo6k86$2dpj$1 digitaldaemon.com...
 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?
Ah, I have it backwards. Put the const in front of p1.
Nov 03 2003
prev sibling next sibling parent "Peter Hercek" <vvp no.post.spam.sk> writes:
"Bill Cox" <bill viasic.com> wrote in message
news:bhaupl$12t1$1 digitaldaemon.com...
 Matthew Wilson wrote:
[cut]
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?
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.
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.
Aug 12 2003
prev sibling parent "Walter" <walter digitalmars.com> writes:
"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
prev sibling parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"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'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.
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
parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
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 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 13 2003
parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"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 reference
always? 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,
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.
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 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.
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
prev sibling parent reply Ilya Minkov <midiclub 8ung.at> writes:
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
parent reply "Fabian Giesen" <rygNO SPAMgmx.net> writes:
 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
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
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
parent reply Ilya Minkov <midiclub 8ung.at> writes:
Antti Syk=E4ri wrote:
 "Dag Br=FCck and others reviewed considerable amounts of real code to s=
ee
 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 o=
f
 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=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
next sibling parent "Philippe Mori" <philippe_mori hotmail.com> writes:
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
prev sibling parent reply "Scott McCaskill" <scott mccaskill.org> writes:
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
parent reply Ilya Minkov <midiclub 8ung.at> writes:
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
next sibling parent "Fabian Giesen" <rygNO SPAMgmx.net> writes:
 Why do you need a non-const version at all?
Because the (class-)const version only returns a const reference. -fg
Aug 20 2003
prev sibling parent "Scott McCaskill" <scott mccaskill.org> writes:
"Ilya Minkov" <midiclub 8ung.at> wrote in message
news:bhvj1j$1o2o$1 digitaldaemon.com...
 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?
So I can write this: // given: MyArray& a a[0] = 5; -- Scott McCaskill
Aug 20 2003
prev sibling parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 1) Too complicated (look at all the wierd rules and perterbations it
causes
 with 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
parent Ilya Minkov <midiclub 8ung.at> writes:
Philippe, you're speaking my mind!

-i.
Aug 12 2003