www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - const(FAQ)

reply Walter Bright <newshound1 digitalmars.com> writes:
The same questions about const come up repeatedly, so I decided that it 
was past time to put up a FAQ dedicated to const. The initial version is 
far from complete, but it's a start.

http://www.digitalmars.com/d/2.0/const-faq.html
Mar 28 2008
next sibling parent reply Gregor Richards <Richards codu.org> writes:
Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
But the FAQ is const, so you can't write new entries :( - Gregor Richards P.S. :P
Mar 28 2008
parent reply janderson <askme me.com> writes:
Gregor Richards wrote:
 Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that 
 it was past time to put up a FAQ dedicated to const. The initial 
 version is far from complete, but it's a start.

 http://www.digitalmars.com/d/2.0/const-faq.html
But the FAQ is const, so you can't write new entries :( - Gregor Richards P.S. :P
LOL. Seriously though, you could on the "comments" page. -Joel
Mar 28 2008
parent reply "Craig Black" <craigblack2 cox.net> writes:
"janderson" <askme me.com> wrote in message 
news:fskhgv$e31$1 digitalmars.com...
 Gregor Richards wrote:
 Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.

 http://www.digitalmars.com/d/2.0/const-faq.html
But the FAQ is const, so you can't write new entries :( - Gregor Richards P.S. :P
LOL. Seriously though, you could on the "comments" page. -Joel
Nope, because D const is transitive, so the comments page is const as well.
Mar 28 2008
parent Walter Bright <newshound1 digitalmars.com> writes:
Craig Black wrote:
 "janderson" <askme me.com> wrote in message 
 news:fskhgv$e31$1 digitalmars.com...
 Gregor Richards wrote:
 But the FAQ is const, so you can't write new entries :(
Seriously though, you could on the "comments" page.
Nope, because D const is transitive, so the comments page is const as well.
Since it's const, another user could change it.
Mar 29 2008
prev sibling next sibling parent reply Jacob Carlborg <doobnet gmail.com> writes:
Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
Very nice. But you need to update the D 2.0 main page also so people actually can find it.
Mar 28 2008
parent Extrawurst <spam extrawurst.org> writes:
Jacob Carlborg schrieb:
 Very nice. But you need to update the D 2.0 main page also so people 
 actually can find it.
while at it u could add the SafeD article to the main page too ;)
Mar 28 2008
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright wrote:

 The same questions about const come up repeatedly, so I decided that it
 was past time to put up a FAQ dedicated to const. The initial version is
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
It's good to see this :) I think it needs something on enums for manifest constants.
Mar 28 2008
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Jason House wrote:
 Walter Bright wrote:
 
 The same questions about const come up repeatedly, so I decided that it
 was past time to put up a FAQ dedicated to const. The initial version is
 far from complete, but it's a start.

 http://www.digitalmars.com/d/2.0/const-faq.html
It's good to see this :) I think it needs something on enums for manifest constants.
Agreed, and also explain why invariant(int) x = 5; Still reserves 4 bytes of space for an int.
Apr 01 2008
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 01 Apr 2008 00:20:20 -0700, Robert Fraser wrote:

 explain why
 
 invariant(int) x = 5;
 
 Still reserves 4 bytes of space for an int.
So you can take the address of it?? -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Apr 01 2008
parent Robert Fraser <fraserofthenight gmail.com> writes:
Derek Parnell wrote:
 On Tue, 01 Apr 2008 00:20:20 -0700, Robert Fraser wrote:
 
 explain why

 invariant(int) x = 5;

 Still reserves 4 bytes of space for an int.
So you can take the address of it??
Right, but that should be in the FAQ.
Apr 01 2008
prev sibling next sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
Question:

In my limited use of const thus far, I've found that "ignoring" or "just 
not using" it is not as possible as suggested.  This is because the 
standard library imposes it.

Or, in other words, if I ignore const and pretend it doesn't exist, 
Phobos will wake me up from my sleep whenever I use a function that, 
e.g. returns a string.

I may be mistaken but this has been my experience.  Not that I don't 
want to use const, necessarily.

-[Unknown]


Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
Mar 28 2008
next sibling parent reply serg kovrov <sergk mailinator.com> writes:
Unknown W. Brackets wrote:
 Question:
 
 In my limited use of const thus far, I've found that "ignoring" or "just 
 not using" it is not as possible as suggested.  This is because the 
 standard library imposes it.
 
 Or, in other words, if I ignore const and pretend it doesn't exist, 
 Phobos will wake me up from my sleep whenever I use a function that, 
 e.g. returns a string.
 
 I may be mistaken but this has been my experience.  Not that I don't 
 want to use const, necessarily.
 
 -[Unknown]
This is my experience so far as well. It's a shame, whenever I try D2 (I usually do once in couple months, after seeing "most of the issues are fixed now"), I always fall back to D1 after coule of hours figuring what I have to do (cast/dup to and when) to achieve the task at hand. Either it is me too dumb to grok it, or const system is not intuitive(prodictive) yet. And productiveness (applied to me) is what I look for in first place. -- serg.
Mar 29 2008
parent Derek Parnell <derek psych.ward> writes:
On Sun, 30 Mar 2008 02:42:25 +0200, serg kovrov wrote:

 Unknown W. Brackets wrote:
 Question:
 
 In my limited use of const thus far, I've found that "ignoring" or "just 
 not using" it is not as possible as suggested.  This is because the 
 standard library imposes it.
Yes it does, but that hasn't been a problem. In fact, it has been a problem in those few times that phobos doesn't use string.
 I always fall back to D1 after coule of hours figuring what 
 I have to do (cast/dup to and when) to achieve the task at hand.
This *is* wierd. I'm using D2 as my version-of-choice an I almost never have to cast or dup. In fact, I haven't needed to cast at all and the only dups I need are when I'm returing a slice to a caller who is going to later manipulate the data in the slice. Even then, it guess I should still return an invariant and get the caller to do the dup if they need to. But the D2 const system is not impeding me at all ... well I did have to work around the absence of tail-const once. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Mar 29 2008
prev sibling parent Regan Heath <regan netmail.co.nz> writes:
Unknown W. Brackets wrote:
 In my limited use of const thus far, I've found that "ignoring" or "just 
 not using" it is not as possible as suggested.  This is because the 
 standard library imposes it.
 
 Or, in other words, if I ignore const and pretend it doesn't exist, 
 Phobos will wake me up from my sleep whenever I use a function that, 
 e.g. returns a string.
The return-type-has-const-of-parameter feature/idea should hopefully resolve this, as in, if you call split on a mutable string you get mutable slices, if you call it on an immutable string you get immutable slices. Regan
Mar 31 2008
prev sibling next sibling parent reply janderson <askme me.com> writes:
Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
I apologize if this was already suggested but that last section "Why aren't function parameters const by default" got me thinking. What if you could surround code in a const block: const { //Anything in here gets const by default void foo(int* p) {} }; Ok, this maybe something to add later when the current const is stable. -Joel
Mar 28 2008
parent Walter Bright <newshound1 digitalmars.com> writes:
janderson wrote:
 I apologize if this was already suggested but that last section "Why 
 aren't function parameters const by default" got me thinking.  What if 
 you could surround code in a const block:
 
 const
 {
    //Anything in here gets const by default
     void foo(int* p) {}
 };
It already means something else: const void foo(int* p) {}
Mar 28 2008
prev sibling next sibling parent reply guslay <guslay gmail.com> writes:
Walter Bright Wrote:

 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
From my experience, the need for logical constness is a rare occurrence, but it invariably happens as projects that adhere to a const regime grow. With the const system in D offering must stronger guarantees than in C++ (which is a good thing), the need for logical const is even larger. I have a hard time figuring how const-correctness can work for large projects (which is one thing D const is intended to help) without that flexibility. - Could you elaborate more on what mutable members would break/prevent, if used reasonably? - If I do need mutable members in a const method (for lazy evaluation, caching, counter), what should I do? Is there technique you can propose to escape it (e.g. a way I can externalize the mutable part (seems difficult because of the transitive nature of const) )?
Mar 28 2008
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
guslay wrote:
 - Could you elaborate more on what mutable members would
 break/prevent, if used reasonably?
"reasonably" is not a factor. The point of compiler enforcement is you won't be relying on whether programmers are reasonable or not - it offers a guarantee.
 - If I do need mutable members in a const method (for lazy
 evaluation, caching, counter), what should I do? Is there technique
 you can propose to escape it (e.g. a way I can externalize the
 mutable part (seems difficult because of the transitive nature of
 const) )?
It can't be done with transitive const. My suggestion for such things is to use private data members, and read the values with property functions.
Mar 28 2008
prev sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
guslay Wrote:

 Walter Bright Wrote:
 
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
From my experience, the need for logical constness is a rare occurrence, but it invariably happens as projects that adhere to a const regime grow. With the const system in D offering must stronger guarantees than in C++ (which is a good thing), the need for logical const is even larger.
Since D const has different effects, the places where it is needed will be different and I think fewer, simply because the places where it is useful are (intentionally) fewer. Namely, it is no longer useful for logical const. The times that I can remember using logical const in C++ have usually been for "compatibility" reasons, i.e. there is an interface and the method is defined something like "int foo(...) const = 0;", or backward compatibility such as when "lazy" evaluation is added to a class. Was const a need or a premature optimization? When is there a case where logical const actually provides benefits over leaving the methods non-const? I would suggest that in many cases, D interfaces should avoid const since you can't know if lazy evaluation and similar are needed. Using const here is a case of overspecifying, like an interface that takes a reference to a "StudentCheckingAccount" object but only uses methods from the parent interface "CheckingAccount" or grandparent "Account" object (assume an inheritance hierarchy following the names.) Demanding that a concrete class be const is a larger burden than in C++ since there is no workaround.
 I have a hard time figuring how const-correctness can work for large 
 projects (which is one thing D const is intended to help) without that 
 flexibility.
I think just by not putting "const" on the logically const stuff. This impacts other methods that call those methods -- but the calling methods are actually "logically" const themselves, by virtue of calling logically const stuff, so they are supposed to be affected. D const is a different tool than C++ const. In K&R C, you can define a method like "int foo()" and decide later that you are going to send (and receive) arguments anyway -- in C++ this possibility is lost. In C++ you can define a method as const, but later on decide that it isn't really "bitwise" const. In D you can't -- const has been given "teeth" by D, just like function signatures were given "teeth" by C++.
 - Could you elaborate more on what mutable members would 
 break/prevent, if used reasonably?
The idea is that "const" in D means that the compiler can guarantee that no side effects happen. First, if you call a method several times, can the compiler eliminate the subsequent calls? for(int i = 0; i != v.size(); i++) cout << v[i]; The compiler has to call v.size() for every iteration in C++. In this case, if both v.operator[](int) and v.size() are inlined and simple then the compiler (might) be able to check constness via data flow analysis. But if not, the compiler can't cache .size() because in C++ the const might be "logical". More importantly, it goes back to the idea of "reentrant" code. Locks exist to prevent race conditions -- which are due to side effects. Thus, side effect free ("pure") functions can be called by multiple threads at the same time with no locking, and causing no possible problem, since the calls are guaranteed to have no effects. But that's not the case for "logical" const, so in C++ "const" is useless for making this determination. In D const is about guarantees regarding bits, bytes, synchronization, and side effects, whereas in C++ version is about telling another programmer that the object has "the same value" before and after a method call. Both goals are useful to some degree, but Walter is betting (as I read it), that in the future, guarantees about side effects and so on are much more useful, especially when code is scaled up and threaded.
 - If I do need mutable members in a const method (for lazy evaluation, 
 caching, counter), what should I do? Is there technique you can 
 propose to escape it (e.g. a way I can externalize the mutable part 
 (seems difficult because of the transitive nature of const) )?
Yes -- any data stored outside the class can be fiddled with. int * GetValue(inout int key) { static int serial = 1; static int[int] map; if (key == -1) { key = ++ serial; map[key] = 0; } return key in map; } class SomeClass { public: int serial = -1; int counter() const { int * p = GetValue(serial); return ++*p; } }; (There might be much simpler ways but I think this should work regardless of const semantics really, since const applies only to data within the object itself... I haven't tried it though.) Kevin
Mar 29 2008
next sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
Kevin Bealer Wrote:










 
 class SomeClass {
 public:
     int serial = -1;
     static int nextSerial = 1;
     
     int counter() const
     {
         int * p = GetValue(serial);
         return ++*p;
     }
 };
 
[ Okay, I've fixed this a bit since it was clearly flawed, lines starting with Kevin
Mar 29 2008
parent reply "Koroskin Denis" <2korden+D gmail.com> writes:
Slightly modifying your example...

Looks like a bug to me:

import std.stdio;

void set(inout int key, int value)
{
     key =3D value;
}

class Square {
     private int _area =3D -1;
     private int _width =3D 0;

     public this(int width)
     {
         _width =3D width;
     }

     public const int area()
     {
         if (_area =3D=3D -1) {
             //_area =3D _width*_width;      // we cannot change variabl=
e  =

value direcly
             set(_area, _width*_width);    // but we can do it _indirect=
ly_  =

???
                                           // it means that _all_ variab=
les  =

are mutable!
         }
         return _area;
     }
}

int main(string[] args)
{
     const Square c =3D new Square(10);
     writefln(c.area());

     return 0;
}
Mar 29 2008
parent reply Kevin Bealer <kevinbealer gmail.com> writes:
Koroskin Denis Wrote:

 Slightly modifying your example...
 
 Looks like a bug to me:
 
 import std.stdio;
 
 void set(inout int key, int value)
 {
      key = value;
 }
 
 class Square {
      private int _area = -1;
      private int _width = 0;
 
      public this(int width)
      {
          _width = width;
      }
 
      public const int area()
      {
          if (_area == -1) {
              //_area = _width*_width;      // we cannot change variable  
 value direcly
              set(_area, _width*_width);    // but we can do it _indirectly_  
 ???
                                            // it means that _all_ variables  
 are mutable!
          }
          return _area;
      }
 }
 
 int main(string[] args)
 {
      const Square c = new Square(10);
      writefln(c.area());
 
      return 0;
 }
Maybe I'm missing something but checking your code in the version of dmd that I currently have (2.007), the compiler says: Error: cast(int)(this._area) is not an lvalue Kevin
Mar 29 2008
parent "Koroskin Denis" <2korden gmail.com> writes:
On Sat, 29 Mar 2008 21:03:35 +0300, Kevin Bealer <kevinbealer gmail.com>  
wrote:

 Maybe I'm missing something but checking your code in the version of dmd  
 that I currently have (2.007), the compiler says:

 Error: cast(int)(this._area) is not an lvalue

 Kevin
DMD 2.012 compiles the code without a single warning.
Mar 29 2008
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Kevin Bealer wrote:
 Both goals are useful to some degree, but Walter is betting (as I
 read it), that in the future, guarantees about side effects and so on
 are much more useful, especially when code is scaled up and threaded.
Yes, your interpretation is correct. I think that being able to mathematically (and mechanically) prove things about a program will be increasingly valuable.
Mar 29 2008
prev sibling parent reply guslay <guslay gmail.com> writes:
Kevin Bealer Wrote:

 guslay Wrote:
 
 
 The times that I can remember using logical const in C++ have usually been for
"compatibility" reasons, i.e. there is an interface and the method is defined
something like "int foo(...) const = 0;", or backward compatibility such as
when "lazy" evaluation is added to a class.  Was const a  need or a premature
optimization?  When is there a case where logical const actually provides
benefits over leaving the methods non-const?
 
 I would suggest that in many cases, D interfaces should avoid const since you
can't know if lazy evaluation and similar are needed.  Using const here is a
case of overspecifying, like an interface that takes a reference to a
"StudentCheckingAccount" object but only uses methods from the parent interface
"CheckingAccount" or grandparent "Account" object (assume an inheritance
hierarchy following the names.)  Demanding that a concrete class be const is a
larger burden than in C++ since there is no workaround.
That's why I feel that this is the biggest threat to the adaption of a const-correctness policy in large D programs. You are basically saying don't use const unless you know all the implementation details about a class, and don't put const in Interface/abstract class, just in final concrete class. As you point, casting away constness is not an option. You need a way out, otherwise const will just be used for trivial things.
 
 - Could you elaborate more on what mutable members would 
 break/prevent, if used reasonably?
The idea is that "const" in D means that the compiler can guarantee that no side effects happen. First, if you call a method several times, can the compiler eliminate the subsequent calls? for(int i = 0; i != v.size(); i++) cout << v[i]; The compiler has to call v.size() for every iteration in C++. In this case, if both v.operator[](int) and v.size() are inlined and simple then the compiler (might) be able to check constness via data flow analysis. But if not, the compiler can't cache .size() because in C++ the const might be "logical".
Logical const is still const, as far has there is no visible (public) side effect on the object. The implementation is still bound to that contract. The result of .size() may still be cached.
 
 More importantly, it goes back to the idea of "reentrant" code.  Locks exist
to prevent race conditions -- which are due to side effects.  Thus, side effect
free ("pure") functions can be called by multiple threads at the same time with
no locking, and causing no possible problem, since the calls are guaranteed to
have no effects.  But that's not the case for "logical" const, so in C++
"const" is useless for making this determination.
 
 In D const is about guarantees regarding bits, bytes, synchronization, and
side effects, whereas in C++ version is about telling another programmer that
the object has "the same value" before and after a method call.
 
 Both goals are useful to some degree, but Walter is betting (as I read it),
that in the future, guarantees about side effects and so on are much more
useful, especially when code is scaled up and threaded.
 
 - If I do need mutable members in a const method (for lazy evaluation, 
 caching, counter), what should I do? Is there technique you can 
 propose to escape it (e.g. a way I can externalize the mutable part 
 (seems difficult because of the transitive nature of const) )?
Yes -- any data stored outside the class can be fiddled with. int * GetValue(inout int key) { static int serial = 1; static int[int] map; if (key == -1) { key = ++ serial; map[key] = 0; } return key in map; } class SomeClass { public: int serial = -1; int counter() const { int * p = GetValue(serial); return ++*p; } }; Kevin
So, there a way around it. You can outsource the mutable part to a static/global. This is a side effect. Why not allow a mutable private members, and keep those implementation details encapsulated within the class. This code is not more guaranteed to be "reentrant" that mutable one. With const, you promise that the object that will exhibit to the outside world a const behavior. However, you cannot say that a const-call has no side effect at all. In other words, const does not mean pure, with or without mutable members.
Mar 29 2008
parent reply Kevin Bealer <kevinbealer gmail.com> writes:
guslay Wrote:

 Kevin Bealer Wrote:
 
 guslay Wrote:
 
 
 I would suggest that in many cases, D interfaces should avoid const since you
can't know if lazy evaluation and similar are needed.  Using const here is a
case of overspecifying, like an interface that takes a reference to a
"StudentCheckingAccount" object but only uses methods from the parent interface
"CheckingAccount" or grandparent "Account" object (assume an inheritance
hierarchy following the names.)  Demanding that a concrete class be const is a
larger burden than in C++ since there is no workaround.
That's why I feel that this is the biggest threat to the adaption of a const-correctness policy in large D programs. You are basically saying don't use const unless you know all the implementation details about a class, and don't put const in Interface/abstract class, just in final concrete class.
Yes and no. You can make the interface methods const but not do lazy evaluation later, or you can make them non-const and leave the door open. But you have to decide. You can change the decision later but its like any const-correctness-scheme change, it can potentially bubble up the code dependency tree. A lot of C++ programmers seem to follow a concept that everything should be const all the time unless there is a reason not to. In the D const world, the cost of const is higher so you really have to ask the cost/benefit question -- does this buy me anything -- when applying const. For interfaces and base classes, this answer will be no more often than for concrete classes.
 As you point, casting away constness is not an option. You need a way out,
otherwise const will just be used for trivial things.
In C++, const is about observable values via a human-understood subset of the public interface (e.g. if a method changes a string's capacity() but not its size or data, is it still logically const?). In some cases, it seems like const only applies to an object's position in a sort order. It's like advisory locking in unix. In D it is about reentrant safety and bits. The C++ version is more useful as a guideline for communication between programmers, but the D version is more useful as a guarantee about code behavior.
 - Could you elaborate more on what mutable members would 
 break/prevent, if used reasonably?
The idea is that "const" in D means that the compiler can guarantee that no side effects happen. First, if you call a method several times, can the compiler eliminate the subsequent calls? for(int i = 0; i != v.size(); i++) cout << v[i]; The compiler has to call v.size() for every iteration in C++. In this case, if both v.operator[](int) and v.size() are inlined and simple then the compiler (might) be able to check constness via data flow analysis. But if not, the compiler can't cache .size() because in C++ the const might be "logical".
Logical const is still const, as far has there is no visible (public) side effect on the object. The implementation is still bound to that contract. The result of .size() may still be cached.
Assuming the person writing the code made sure of this... But once you have mutable members, the compiler cannot rely on the value of size() changing or not, it becomes a human decision. Is the value of vector.capacity() allowed to change? From the point of view of the "value" of the vector, yes. From a compiler/optimization point of view, no. To some extent, this could actually be fixed in C++ simply by the standards committee saying that its okay for a compiler to rely on repeatability of method return values and to omit duplicate calls to methods.
 So, there a way around it. You can outsource the mutable part to a
static/global.
 
 This is a side effect. Why not allow a mutable private members, and keep those
implementation details encapsulated within the class. This code is not more
guaranteed to be "reentrant" that mutable one.
 
 With const, you promise that the object that will exhibit to the outside world
a const behavior. However, you cannot say that a const-call has no side effect
at all. 
 
 In other words, const does not mean pure, with or without mutable members.
You're right - the definition of "pure" in this sense is much stronger than "const", so I overstepped here. In this case "const" is not "pure" but is kind of like "pure" relative to the object in question, so some of the optimization might not be as possible unless all behaviors of the method are "read" rather than "write", not just those affecting internal state of this object. But the "pure" keyword is reserved, so maybe we'll see that as well. Kevin
Mar 29 2008
parent guslay <guslay gmail.com> writes:
Kevin Bealer Wrote:

 guslay Wrote
 ... logical const ...
I still don't see how dividing a const composite object (ie a class) in two separate parts (mutable and immutable) would prevent any optimization or thwart future development. I can see the optimization benefit of an eventual pure qualifier. With no possible side effects, it would allow crazy functional programming tricks (reordering, parallel dispatch, memoizing). But const (or invariant) doesn't mean pure. The following does compile, and is bitwise const. It's legal and it's full of side effects. static int dim = 0; class Vector { public const int size() { return ++.dim; } public const void printElement( int i ) { // print element i to stdout } }; As opposed to pure function, the only "mathematical" guarantee that the const qualifiers gives on a method is that its immutable bits will not be modified. Nothing else. So what is the problem if I say that only half the bits are immutable? Const/invariant on data enables aggressive optimizations; const/invariant on a method does not. Or am I missing something?
Mar 29 2008
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Walter Bright, el 28 de marzo a las 15:33 me escribiste:
 The same questions about const come up repeatedly, so I decided that it was
past time to put up a FAQ dedicated to const. The initial version is far from 
 complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
About "Why aren't function parameters const by default?": Why aren't *all* variables const by default? It was discussed a long time ago and AFAIK most people liked it. And about "Why not use readonly to mean read only view?": Why not use view to mean read only view? As discussed recently? I don't think "because it's not that clear that 'view' means 'read only view'" is a good enough reason for changing the const meaning to make it mean 'read only view'. If words are arbitrary, at least this scheme let you keep the old const meaning... -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Breathe, breathe in the air. Don't be afraid to care. Leave but don't leave me. Look around and choose your own ground.
Mar 28 2008
parent Walter Bright <newshound1 digitalmars.com> writes:
Leandro Lucarella wrote:
 Walter Bright, el 28 de marzo a las 15:33 me escribiste:
 The same questions about const come up repeatedly, so I decided that it was
past time to put up a FAQ dedicated to const. The initial version is far from 
 complete, but it's a start.

 http://www.digitalmars.com/d/2.0/const-faq.html
About "Why aren't function parameters const by default?": Why aren't *all* variables const by default?
Because you'd have to throw out half of the existing D code base. It also doesn't buy you that much, after all, it isn't hard to get into the habit of: const x = 3; rather than: int x = 3;
 It was discussed a long time ago and AFAIK most people liked it.
 
 And about "Why not use readonly to mean read only view?":
 
 Why not use view to mean read only view?
 
 As discussed recently? I don't think "because it's not that clear that
 'view' means 'read only view'" is a good enough reason for changing the
 const meaning to make it mean 'read only view'. If words are arbitrary, at
 least this scheme let you keep the old const meaning...
The D 1.0 const was just a storage class, not a type modifier. It still is a storage class, and: const int x = 3; still work with 1.0 and 2.0.
Mar 29 2008
prev sibling next sibling parent Davidson Corry <davidsoncorry comcast.net> writes:
Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
Thank you! Very informative. ------------ I would find very helpful some further discussion of your thoughts on why const and invariant are crucial to multiprogramming. I sense that you are correct, but the details are yet murky to me. I mentioned, over in the "const debacle' thread, that const on a parameter could be considered as part of the method's contract. And guslay, in this thread, noted the same thing:
 Logical const is still const, as far has there is no visible (public)
 side effect on the object. The implementation is still bound to that
 contract. The result of .size() may still be cached.
That is, in some sense foo(const(X) x) carries with it the (implied or express) postcondition out { x == old.x; } which reads as "x looks just like it did when we got it". On further thought, I realize that this is not strictly true. That is, it is true ONLY in the degenerate case of single-threaded execution: contracts (in Eiffel and D) allow the method to violate the class invariant "in the interim" -- that is, while executing private code, including private calls to subfunctions -- so long as the invariant is re-established at the time of the method return. Likewise, the postcondition doesn't have to be established until the method returns. In single-threaded code, this much more limited guarantee is equivalent to "I'm not changing x anywhere in the middle", because there IS no one else using x while I've got control of it. But with multiprogramming, that's no longer true. The contract metaphor is no longer accurate. The problems -- one for language designers, a different one for language users -- is that the "degenerate" case of single-threading IS 98% OF THE CODING WE DO, without even thinking about it. Pretty much all programming languages I know of have, built into their syntax**, the assumption that nothing else is happening but what they are doing: void foo() { some_statement; <--- what is happening here? some_other_statement; } some_other_statement depends on the assumption that whatever some_statement did, nothing that it did has changed by the time some_other_statement gets control. And I don't see how it could be otherwise. For that matter, HUMAN languages all make the same assumption. When I ask, "How about them 'Blazers, guy?" I do not consider the price of first-quality saffron in Parma, even though that was going on, too. And if I put my lunch sack in the fridge at work and mark it 'const' (as in, "Please don't take my lunch"), that does not guarantee that Ralphie The Sandwich Pilferer isn't going to violate that "contract". In human affairs, these things are handled by (at least) one of two methods: convention (social contract), or active intervention by someone named "manager" or "coordinator" (or in Ralphie's case, "hit man"...). These are analogous to "language feature" and "runtime code including library support", I believe. In human affairs, the agents on the ground, the line workers, are the ones "who do the real work"... but without inserting the third-party manager AND GIVING UP SOME AUTONOMY to her (or to the "virtual" manager of convention), NOTHING OF SIGNIFICANT SCALE GETS DONE. I suspect that the analogy extends to coding. And I do not think that we have yet invented a workable (much less an elegant) syntax for relinquishing autonomy in programming. ** Some programming languages have added syntax features like 'lock', 'synchronized' and 'separate' to support multiprogramming. I think it's clear they don't go far enough, yet. Likewise, I sense that 'const' and 'invariant' will be fundamental here -- not sufficient, but necessary. I'm looking forward to seeing it all evolve. Maybe even contributing a chromosome or two... -- Dai p.s. I note that the "social contract" bit applies to programming as well. D syntax can only protect you against other programs WRITTEN IN D, not against rm -rf *. And I'm not aware of any syntax constructs that can prevent my wife from hitting the power button before clicking "save"... and then blaming me for her work getting lost. <grin>
Mar 29 2008
prev sibling next sibling parent reply Kevin Bealer <kevinbealer gmail.com> writes:
guslay Wrote:

 Kevin Bealer Wrote:
 
 guslay Wrote
 ... logical const ...
I still don't see how dividing a const composite object (ie a class) in two separate parts (mutable and immutable) would prevent any optimization or thwart future development. I can see the optimization benefit of an eventual pure qualifier. With no possible side effects, it would allow crazy functional programming tricks (reordering, parallel dispatch, memoizing). But const (or invariant) doesn't mean pure. The following does compile, and is bitwise const. It's legal and it's full of side effects.
But only because it reaches outside the object. For safety / correctness, I could argue that if you want the "dim" data to be const, you need to declare it as such, or wrap it in an object and declare a const one of those, but I guess that's beside the point. You may be right about optimization and purity.
 static int dim = 0;
 class Vector
 {
      public const int size()
      {
          return ++.dim;
      }
 
      public const void printElement( int i )
      {
          // print element i to stdout
      }
 };
 
 As opposed to pure function, the only "mathematical" guarantee that the
 const qualifiers gives on a method is that its immutable bits will not be
 modified. Nothing else. So what is the problem if I say that only half the
 bits are immutable?
(You can, technically, by making half of the fields const individually.) The question is, what does "const" mean -- if it means that all the bits are immutable, you get a certain benefit. If it means that some of them are, you get a different benefit. I think the idea is that the "all bits" guarantee is more useful for the purpose of reasoning about whether it is safe to call a method from inside / outside a locked / synchronized() section. A normal .size() method will operate on the fields of the object in question. If it is const, it does not need to modify those fields. This means that you can safely call such methods in parallel from multiple threads on the same object. Is this an absolute guarantee? No -- side effects as shown above break this assumption. But only the external ".dim" field can be corrupted, and it should have its own object and/or locking if that is an issue. But calling multiple non-const methods is not safe unless proper locking is performed. Now here is the critical distinction: this applies to the D concept of const, but not the C++ concept since mutable fields tend to reintroduce the need for locking even in the presence of C++ "logical const" correctness. The caller may need to get locks when calling the callee's methods (if the callee does not do its own locking), but only for non-const objects. If the callee does its own locking then another problems arises -- the caller must be careful of deadlock. So you are hemmed in from both directions -- it is dangerous to do too much locking (due to deadlock) or too little (due to race conditions), which is why a compiler-enforced guarantee is useful. In either case, knowing the object is const will be more useful if it is an enforced transitive const rather than a logical const, since the former implies something about the need for locking while the latter does not. (Of course, this assumes again that const methods are pure, i.e. don't reach outside the object to change things in important ways. It does require that much cooperation at any rate, although I guess I'm the one to suggest doing otherwise...)
 Const/invariant on data enables aggressive optimizations; const/invariant
 on a method does not.
Probably true.
 Or am I missing something?
I think in C++ you have a kind of contract with the std library, other developers, etc, to not change the observable value of a const object so that it is different after the call than before it. In D you have a similar contract not to change the bitwise value of the object. In C++ this contract is non-enforceable -- if you make all the fields mutable, you have essentially discarded the const qualifier. In D you have a different contract, to not modify the actual bits of an object. This contract is enforceable and solid because the compiler can truly enforce it. The code we've been discussing violates the (C++) contract in any case because it changes the conceptual value of the object from a const method. The compiler can't enforce this because the ++.dim is indistinguishable from the ++ inside a buffered stream when you output a character, or any other external effects. It doesn't violate the D "const contract", because the D contract talks about the bit state of this object (and no other) rather than the observable value. D has a const contract that the compiler really can enforce, and do so completely, whereas C++ has a const contract that the compiler can't enforce. It can make sure you fill out the proper paperwork when you ignore the contract, but it doesn't stop you from ignoring it. Kevin
Mar 31 2008
parent reply guslay <guslay gmail.com> writes:
I think we are agreeing on the benefits of "pure" methods.

But arguing that an immutable object is automatically thread-safe is simply not
true under the current definition of const. It's an over-generalization. It's
just one part of the puzzle.

A pure methods has to be const, but a const method is not implicitly pure.


Kevin Bealer Wrote:

 guslay Wrote:
 
 Kevin Bealer Wrote:
 
 guslay Wrote
 ... logical const ...
I still don't see how dividing a const composite object (ie a class) in two separate parts (mutable and immutable) would prevent any optimization or thwart future development. I can see the optimization benefit of an eventual pure qualifier. With no possible side effects, it would allow crazy functional programming tricks (reordering, parallel dispatch, memoizing). But const (or invariant) doesn't mean pure. The following does compile, and is bitwise const. It's legal and it's full of side effects.
But only because it reaches outside the object. For safety / correctness, I could argue that if you want the "dim" data to be const, you need to declare it as such, or wrap it in an object and declare a const one of those, but I guess that's beside the point. You may be right about optimization and purity.
 static int dim = 0;
 class Vector
 {
      public const int size()
      {
          return ++.dim;
      }
 
      public const void printElement( int i )
      {
          // print element i to stdout
      }
 };
 
 As opposed to pure function, the only "mathematical" guarantee that the
 const qualifiers gives on a method is that its immutable bits will not be
 modified. Nothing else. So what is the problem if I say that only half the
 bits are immutable?
(You can, technically, by making half of the fields const individually.) The question is, what does "const" mean -- if it means that all the bits are immutable, you get a certain benefit. If it means that some of them are, you get a different benefit. I think the idea is that the "all bits" guarantee is more useful for the purpose of reasoning about whether it is safe to call a method from inside / outside a locked / synchronized() section. A normal .size() method will operate on the fields of the object in question. If it is const, it does not need to modify those fields. This means that you can safely call such methods in parallel from multiple threads on the same object. Is this an absolute guarantee? No -- side effects as shown above break this assumption. But only the external ".dim" field can be corrupted, and it should have its own object and/or locking if that is an issue. But calling multiple non-const methods is not safe unless proper locking is performed. Now here is the critical distinction: this applies to the D concept of const, but not the C++ concept since mutable fields tend to reintroduce the need for locking even in the presence of C++ "logical const" correctness. The caller may need to get locks when calling the callee's methods (if the callee does not do its own locking), but only for non-const objects. If the callee does its own locking then another problems arises -- the caller must be careful of deadlock. So you are hemmed in from both directions -- it is dangerous to do too much locking (due to deadlock) or too little (due to race conditions), which is why a compiler-enforced guarantee is useful. In either case, knowing the object is const will be more useful if it is an enforced transitive const rather than a logical const, since the former implies something about the need for locking while the latter does not. (Of course, this assumes again that const methods are pure, i.e. don't reach outside the object to change things in important ways. It does require that much cooperation at any rate, although I guess I'm the one to suggest doing otherwise...)
 Const/invariant on data enables aggressive optimizations; const/invariant
 on a method does not.
Probably true.
 Or am I missing something?
I think in C++ you have a kind of contract with the std library, other developers, etc, to not change the observable value of a const object so that it is different after the call than before it. In D you have a similar contract not to change the bitwise value of the object. In C++ this contract is non-enforceable -- if you make all the fields mutable, you have essentially discarded the const qualifier. In D you have a different contract, to not modify the actual bits of an object. This contract is enforceable and solid because the compiler can truly enforce it. The code we've been discussing violates the (C++) contract in any case because it changes the conceptual value of the object from a const method. The compiler can't enforce this because the ++.dim is indistinguishable from the ++ inside a buffered stream when you output a character, or any other external effects. It doesn't violate the D "const contract", because the D contract talks about the bit state of this object (and no other) rather than the observable value. D has a const contract that the compiler really can enforce, and do so completely, whereas C++ has a const contract that the compiler can't enforce. It can make sure you fill out the proper paperwork when you ignore the contract, but it doesn't stop you from ignoring it. Kevin
Mar 31 2008
parent reply Kevin Bealer <kevinbealer gmail.com> writes:
guslay Wrote:

 I think we are agreeing on the benefits of "pure" methods.
 
 But arguing that an immutable object is automatically thread-safe is simply
not true under the current definition of const. It's an over-generalization.
It's just one part of the puzzle.
 
 A pure methods has to be const, but a const method is not implicitly pure.
I think what you're saying is right, and I think we can probably stop arguing since I think there is wisdom in each approach.... I think. In fact I could suggest a fourth variety of D const that worked like C++ const, but since there are already at least 3 of them, I would probably get rotten fruit thrown at me ... so I won't. Regarding the D approach then: I think it is a question of cooperation and goals. If you have a method in D that only operates on in-class data then it is pure. (It can also operates on other classes but only if it only calls methods following the same "pure" rules, and they do the same, recursively.) Many methods like .size() can easily be pure since they operate entirely within the object, so in practice this is quite feasible. The D const system does not prove purity, but it helps a lot when writing code and making it pure. The C++ const gives every class the tools and therefore implicitly the permission to use mutable fields in const objects, which means that if people do things in a non-pure way its not even wrong. In fact my (limited) understanding is that the C++ standard actually supports the idea of casting away const just to side step the const regime. It's kind of like garbage collection -- you can have garbage collection in C++ but its risky because it has to be conservative and no existing class expects to run under a GC regime. In D its a conservative system in just the same way, but because it is part of the language, the assumption and the APIs suggest that you shouldn't do certain things. So in D, if you do something that not GC compatible, breaking the code, then its *your fault*, whereas in C++ you could justifiably ask "what do you mean, GC?" If D can establish an understanding (i.e. best practices) that say "don't use mutable external stuff from a const method in a way that impedes purity" then there is a real possibility for more multithread-safe code. There has to be understanding that if int x; class ... { ... const int size() { return ++.x; } }; does not work, its the authors fault. But maybe a full-fledged "pure" is a better approach, but that would prevent a lot of things like output to "stdout", which is not pure but which can be proven not to deadlock. Kevin
Mar 31 2008
parent reply Jason House <jason.james.house gmail.com> writes:
Kevin Bealer Wrote: 
 Regarding the D approach then:
 
 I think it is a question of cooperation and goals.  If you have a method in D
that only
 operates on in-class data then it is pure.  (It can also operates on other
classes but
 only if it only calls methods following the same "pure" rules, and they do the
same,
 recursively.)  Many methods like .size() can easily be pure since they operate
entirely
 within the object, so in practice this is quite feasible.
 
 The D const system does not prove purity, but it helps a lot when writing code
and
 making it pure.  The C++ const gives every class the tools and therefore
implicitly 
 the permission to use mutable fields in const objects, which means that if
people
 do things in a non-pure way its not even wrong.  In fact my (limited)
understanding
 is that the C++ standard actually supports the idea of casting away const just
to
 side step the const regime.
This is actually a good way to think about this stuff. My current problem with the const regime is that global variables can be used to break the transitive const nature. For me to truly feel comfortable with the idea, I need to see equal handling of member variables and global variables. I really hate to see designs that encourage use of global variables! Personally, I like the idea of specifying some kind of exception to the transitive const such as an output-only object.
Apr 01 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 01/04/2008, Jason House <jason.james.house gmail.com> wrote:
 This is actually a good way to think about this stuff.  My current problem
with the const regime is that global variables can be used to break the
transitive const nature.
I think you are incorrect. In a class such as the following int g; class C { int * p; this() { p = &g; } } Then g cannot be modified through a const(C). const C c = new C; *c.g = 4; /*ERROR*/ What you're talking about is something different. You're talking about a member function which is declared const accessing global variables. (Or at least, so I assume). Well, that's not a violation of anything. A /const/ function gets a read-only view of "this", but can still read and write global variables. A /pure/ function cannot see global variables at all.
  I really hate to see designs that encourage use of global variables!
Every (non-pure) function ever can have some paramters const and others not. For member functions, one of those parameters happens to be "this", but that has no bearing on whether or not global variables are seen as const. Nothing "encourages" you to use global variables. However, rest assured that in the future, it will be possible to declare functions "pure", which will render global variables completely inaccessible.
Apr 01 2008
parent Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:

 On 01/04/2008, Jason House <jason.james.house gmail.com> wrote:
 This is actually a good way to think about this stuff.  My current problem
with the const regime is that global variables can be used to break the
transitive const nature.
I think you are incorrect. In a class such as the following int g; class C { int * p; this() { p = &g; } } Then g cannot be modified through a const(C). const C c = new C; *c.g = 4; /*ERROR*/ What you're talking about is something different. You're talking about a member function which is declared const accessing global variables. (Or at least, so I assume). Well, that's not a violation of anything. A /const/ function gets a read-only view of "this", but can still read and write global variables. A /pure/ function cannot see global variables at all.
You're right, I'm talking about a non-pure const function having access to global variables.
  I really hate to see designs that encourage use of global variables!
Every (non-pure) function ever can have some paramters const and others not. For member functions, one of those parameters happens to be "this", but that has no bearing on whether or not global variables are seen as const. Nothing "encourages" you to use global variables. However, rest assured that in the future, it will be possible to declare functions "pure", which will render global variables completely inaccessible.
I have to disagree. Global variables are a back door that allows a head-const-like behavior. This could include classic logging examples or objects to draw on a screen. Using a global variable can achieve this stuff, but in many cases that's bad design. What if I want my logging to be done differently depending on which object does the logging? Or different objects to be drawn to different locations? It's the asymmetry that irks me. How is access to a global variable any different than access to a member object (with a final reference)?
Apr 01 2008
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Walter Bright wrote:
 The same questions about const come up repeatedly, so I decided that it 
 was past time to put up a FAQ dedicated to const. The initial version is 
 far from complete, but it's a start.
 
 http://www.digitalmars.com/d/2.0/const-faq.html
Typo "Jave" in "It would be a huge break from past D practice, and Regan
Mar 31 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Regan Heath wrote:
 Typo "Jave" in "It would be a huge break from past D practice, and 

That's done on purpose to throw off google.
Apr 03 2008
next sibling parent Tower Ty <tytower yahoo.com.au> writes:
Walter Bright Wrote:

 Regan Heath wrote:
 Typo "Jave" in "It would be a huge break from past D practice, and 

That's done on purpose to throw off google.
When my cows get through or over my electric fence I am sure I can see them sniggering !
Apr 03 2008
prev sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Walter Bright wrote:
 Regan Heath wrote:
 Typo "Jave" in "It would be a huge break from past D practice, and 

That's done on purpose to throw off google.
And that's how we'll win at TIOBE :D -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Apr 04 2008