www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Congratulations to the D Team!

reply "Adam Wilson" <flyboynw gmail.com> writes:
Ladies and Gentlemen,

Today is a day to celebrate! By the time many of you read this you will be  
waking up to a new day, and too a new D. As of 0530 07/09/2012 UTC the  
programmers and testers of the D Development Team have merged an  
unprecedented 37 pull requests; all in a single day.

A breakdown of the merges by project:
DMD:		5  (05% of open pull requests)
DRuntime:   17 (55% of open pull requests)
Phobos:     14 (56% of open pull requests)
DPL.org:    4  (50% of open pull requests)

First I want to mention two people who really helped make this day happen.
Jonathan M. Davis: For creating, testing, and submitting a patch that he  
was opposed to in std.datetime. This was a gentlemanly thing to do, and we  
applaud you for it.
Alex Ronne Peterson: For his heroic efforts in fixing any errors in the  
merges as they arose and particularly for his efforts in fixing  
const-related errors.

Next I would like to draw your attention to a couple of major improvements  
to D that were made today:
Object is now const-correct throughout D. This has been a dream for many  
of you. Today it is a reality.
Aliases for the 'dur' template. For example, you can now call 3.minutes()  
and receive a Duration.

Also merged where fixes for the following issues:
http://d.puremagic.com/issues/show_bug.cgi?id=7965
http://d.puremagic.com/issues/show_bug.cgi?id=8274
http://d.puremagic.com/issues/show_bug.cgi?id=7375
http://d.puremagic.com/issues/show_bug.cgi?id=4809
http://d.puremagic.com/issues/show_bug.cgi?id=5547
http://d.puremagic.com/issues/show_bug.cgi?id=1824
http://d.puremagic.com/issues/show_bug.cgi?id=5011
http://d.puremagic.com/issues/show_bug.cgi?id=8323	

I want to extend congratulations to the D Team and especially the members  
that were involved in today's pull request audit and merge. Because of you  
D is a better tool today than it has ever been.

I also hope that these weekly Sunday Pull Audits will continue in the  
future as they will help propel D forward to new heights of reliability  
and usability. What the Team has accomplished is truly extraordinary, but  
we have a long way to go.

As a final note. If you are using Git-HEAD builds of D for your  
development work, I strongly suggest that you update DMD, DRuntime, and  
Phobos at the same time. The Object-const correct changes affected all  
three of those projects and attempting to build any one of them without  
applying the changes to each of them will result in build failures. I also  
strongly suggest that those intrepid D users who are using Git-HEAD for  
their regular D work upgrade and report any bugs you find with the new  
code. With your help, 2.060 will be the best release of D yet!

Again, thank you and congratulations to the D Team!

-- 
Adam Wilson
IRC: LightBender
Project Coordinator
The Horizon Project
http://www.thehorizonproject.org/
Jul 08 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-07-09 08:37, Adam Wilson wrote:
 Ladies and Gentlemen,

 Today is a day to celebrate! By the time many of you read this you will
 be waking up to a new day, and too a new D. As of 0530 07/09/2012 UTC
 the programmers and testers of the D Development Team have merged an
 unprecedented 37 pull requests; all in a single day.

 A breakdown of the merges by project:
 DMD: 5 (05% of open pull requests)
 DRuntime: 17 (55% of open pull requests)
 Phobos: 14 (56% of open pull requests)
 DPL.org: 4 (50% of open pull requests)

 First I want to mention two people who really helped make this day happen.
 Jonathan M. Davis: For creating, testing, and submitting a patch that he
 was opposed to in std.datetime. This was a gentlemanly thing to do, and
 we applaud you for it.
 Alex Ronne Peterson: For his heroic efforts in fixing any errors in the
 merges as they arose and particularly for his efforts in fixing
 const-related errors.

 Next I would like to draw your attention to a couple of major
 improvements to D that were made today:
 Object is now const-correct throughout D. This has been a dream for many
 of you. Today it is a reality.
 Aliases for the 'dur' template. For example, you can now call
 3.minutes() and receive a Duration.

 Also merged where fixes for the following issues:
 http://d.puremagic.com/issues/show_bug.cgi?id=7965
 http://d.puremagic.com/issues/show_bug.cgi?id=8274
 http://d.puremagic.com/issues/show_bug.cgi?id=7375
 http://d.puremagic.com/issues/show_bug.cgi?id=4809
 http://d.puremagic.com/issues/show_bug.cgi?id=5547
 http://d.puremagic.com/issues/show_bug.cgi?id=1824
 http://d.puremagic.com/issues/show_bug.cgi?id=5011
 http://d.puremagic.com/issues/show_bug.cgi?id=8323

 I want to extend congratulations to the D Team and especially the
 members that were involved in today's pull request audit and merge.
 Because of you D is a better tool today than it has ever been.

 I also hope that these weekly Sunday Pull Audits will continue in the
 future as they will help propel D forward to new heights of reliability
 and usability. What the Team has accomplished is truly extraordinary,
 but we have a long way to go.

 As a final note. If you are using Git-HEAD builds of D for your
 development work, I strongly suggest that you update DMD, DRuntime, and
 Phobos at the same time. The Object-const correct changes affected all
 three of those projects and attempting to build any one of them without
 applying the changes to each of them will result in build failures. I
 also strongly suggest that those intrepid D users who are using Git-HEAD
 for their regular D work upgrade and report any bugs you find with the
 new code. With your help, 2.060 will be the best release of D yet!

 Again, thank you and congratulations to the D Team!
Yeah, I wonder what the h*ll had happened when I woke up and had around 50 new email messages. But it was a couple of people going crazy with merging pull requests. Great work :) -- /Jacob Carlborg
Jul 09 2012
prev sibling next sibling parent "Paulo Pinto" <pjmlp progtools.org> writes:
On Monday, 9 July 2012 at 06:37:30 UTC, Adam Wilson wrote:
 Ladies and Gentlemen,

 Today is a day to celebrate! By the time many of you read this 
 you will be waking up to a new day, and too a new D. As of 0530 
 07/09/2012 UTC the programmers and testers of the D Development 
 Team have merged an unprecedented 37 pull requests; all in a 
 single day.

 A breakdown of the merges by project:
 DMD:		5  (05% of open pull requests)
 DRuntime:   17 (55% of open pull requests)
 Phobos:     14 (56% of open pull requests)
 DPL.org:    4  (50% of open pull requests)

 First I want to mention two people who really helped make this 
 day happen.
 Jonathan M. Davis: For creating, testing, and submitting a 
 patch that he was opposed to in std.datetime. This was a 
 gentlemanly thing to do, and we applaud you for it.
 Alex Ronne Peterson: For his heroic efforts in fixing any 
 errors in the merges as they arose and particularly for his 
 efforts in fixing const-related errors.

 Next I would like to draw your attention to a couple of major 
 improvements to D that were made today:
 Object is now const-correct throughout D. This has been a dream 
 for many of you. Today it is a reality.
 Aliases for the 'dur' template. For example, you can now call 
 3.minutes() and receive a Duration.

 Also merged where fixes for the following issues:
 http://d.puremagic.com/issues/show_bug.cgi?id=7965
 http://d.puremagic.com/issues/show_bug.cgi?id=8274
 http://d.puremagic.com/issues/show_bug.cgi?id=7375
 http://d.puremagic.com/issues/show_bug.cgi?id=4809
 http://d.puremagic.com/issues/show_bug.cgi?id=5547
 http://d.puremagic.com/issues/show_bug.cgi?id=1824
 http://d.puremagic.com/issues/show_bug.cgi?id=5011
 http://d.puremagic.com/issues/show_bug.cgi?id=8323	

 I want to extend congratulations to the D Team and especially 
 the members that were involved in today's pull request audit 
 and merge. Because of you D is a better tool today than it has 
 ever been.

 I also hope that these weekly Sunday Pull Audits will continue 
 in the future as they will help propel D forward to new heights 
 of reliability and usability. What the Team has accomplished is 
 truly extraordinary, but we have a long way to go.

 As a final note. If you are using Git-HEAD builds of D for your 
 development work, I strongly suggest that you update DMD, 
 DRuntime, and Phobos at the same time. The Object-const correct 
 changes affected all three of those projects and attempting to 
 build any one of them without applying the changes to each of 
 them will result in build failures. I also strongly suggest 
 that those intrepid D users who are using Git-HEAD for their 
 regular D work upgrade and report any bugs you find with the 
 new code. With your help, 2.060 will be the best release of D 
 yet!

 Again, thank you and congratulations to the D Team!
And I just did a git checkout on Saturday! Time to do a refresh. Great work!
Jul 09 2012
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, July 08, 2012 23:37:29 Adam Wilson wrote:
 First I want to mention two people who really helped make this day happen.
Andrei is the one who was doing most of the work by reviewing and merging most of the pull requests which got merged.
 Aliases for the 'dur' template. For example, you can now call 3.minutes()
 and receive a Duration.
As far as I'm concerned, 3.minutes() is a prime example of what's wong UFCS. UFCS can be very useful, but oh how I hate that syntax (completely aside from the particular function being called, I think that 3.anything() is horrible). But obviously not everyone agrees.
 I also hope that these weekly Sunday Pull Audits will continue in the
 future
I actually hope that it _doesn't_ continue. If Andrei (or anyone else) wants to focus on dealing with pull requests on one particular day of the week, that's fine with me (it's his time to spend however he wants to), and I think that it's great that so much got done, but I think that we'd be better off if pull requests generally got dealt with more regularly rather than all at once. - Jonathan M Davis
Jul 09 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-07-09 09:52, Jonathan M Davis wrote:

 I actually hope that it _doesn't_ continue. If Andrei (or anyone else) wants
 to focus on dealing with pull requests on one particular day of the week,
 that's fine with me (it's his time to spend however he wants to), and I think
 that it's great that so much got done, but I think that we'd be better off if
 pull requests generally got dealt with more regularly rather than all at once.
We want to have it like this everyday :) -- /Jacob Carlborg
Jul 09 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
On 09/07/2012 09:52, Jonathan M Davis wrote:
 I actually hope that it _doesn't_ continue. If Andrei (or anyone else) wants
 to focus on dealing with pull requests on one particular day of the week,
 that's fine with me (it's his time to spend however he wants to), and I think
 that it's great that so much got done, but I think that we'd be better off if
 pull requests generally got dealt with more regularly rather than all at once.

 - Jonathan M Davis
I have to agree here, but it is not very practical. It would require people full time on D.
Jul 09 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/9/12 3:52 AM, Jonathan M Davis wrote:
 On Sunday, July 08, 2012 23:37:29 Adam Wilson wrote:
 First I want to mention two people who really helped make this day happen.
Andrei is the one who was doing most of the work by reviewing and merging most of the pull requests which got merged.
Giving me credit for this is like giving credit the cable for electricity. I just let the great work of others flow.
 Aliases for the 'dur' template. For example, you can now call 3.minutes()
 and receive a Duration.
As far as I'm concerned, 3.minutes() is a prime example of what's wong UFCS. UFCS can be very useful, but oh how I hate that syntax (completely aside from the particular function being called, I think that 3.anything() is horrible). But obviously not everyone agrees.
 I also hope that these weekly Sunday Pull Audits will continue in the
 future
I actually hope that it _doesn't_ continue. If Andrei (or anyone else) wants to focus on dealing with pull requests on one particular day of the week, that's fine with me (it's his time to spend however he wants to), and I think that it's great that so much got done, but I think that we'd be better off if pull requests generally got dealt with more regularly rather than all at once.
Since this was a first, there's been a lot of pent-up work. Let's try to make one pass through each and every pull request every Sunday, the weekly work should be less than yesterday. Andrei
Jul 09 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, July 09, 2012 10:01:25 Andrei Alexandrescu wrote:
 On 7/9/12 3:52 AM, Jonathan M Davis wrote:
 On Sunday, July 08, 2012 23:37:29 Adam Wilson wrote:
 First I want to mention two people who really helped make this day
 happen.
Andrei is the one who was doing most of the work by reviewing and merging most of the pull requests which got merged.
Giving me credit for this is like giving credit the cable for electricity. I just let the great work of others flow.
It's a team effort, but the processing of pull requests has been a definite bottleneck. I'd say that for Phobos, 90+% of the time it's either you or me who merges them, so if either of us is slow about it and/or another Phobos dev is needed to review a particular request, then pull requests tend to languish. It's even worse for druntime, since a lot of the time, Sean needs to get involved (particularly since some of us feel a bit unqualified reviewing and merging some of the critical stuff in druntime), and he seems to be very, very busy (though with Martin Nowak recently added as a committer for druntime, the situation may improve). So, the fact that you took the time to review and merge so many pull requests had a big impact. - Jonathan M Davis
Jul 09 2012
parent reply "Adam Wilson" <flyboynw gmail.com> writes:
On Mon, 09 Jul 2012 12:24:12 -0700, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Monday, July 09, 2012 10:01:25 Andrei Alexandrescu wrote:
 On 7/9/12 3:52 AM, Jonathan M Davis wrote:
 On Sunday, July 08, 2012 23:37:29 Adam Wilson wrote:
 First I want to mention two people who really helped make this day
 happen.
Andrei is the one who was doing most of the work by reviewing and
merging
 most of the pull requests which got merged.
Giving me credit for this is like giving credit the cable for electricity. I just let the great work of others flow.
It's a team effort, but the processing of pull requests has been a definite bottleneck. I'd say that for Phobos, 90+% of the time it's either you or me who merges them, so if either of us is slow about it and/or another Phobos dev is needed to review a particular request, then pull requests tend to languish. It's even worse for druntime, since a lot of the time, Sean needs to get involved (particularly since some of us feel a bit unqualified reviewing and merging some of the critical stuff in druntime), and he seems to be very, very busy (though with Martin Nowak recently added as a committer for druntime, the situation may improve). So, the fact that you took the time to review and merge so many pull requests had a big impact. - Jonathan M Davis
When I mentioned this post on IRC he specifically requested to be left out of it. So I did. However, I personally feel that his willingness to get in there and swing his +50 Hammer of Merging did in fact have a big impact on the successes of yesterday. -- Adam Wilson IRC: LightBender Project Coordinator The Horizon Project http://www.thehorizonproject.org/
Jul 09 2012
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, July 09, 2012 12:28:32 Adam Wilson wrote:
 When I mentioned this post on IRC he specifically requested to be left out
 of it. So I did. However, I personally feel that his willingness to get in
 there and swing his +50 Hammer of Merging did in fact have a big impact on
 the successes of yesterday.
Ah. Okay. It just seemed really odd to me that you were singling people out and didn't say anything about him. I've never used the D IRC channel, so I have no clue what goes on in there. I lose enough time just dealing with the newsgroup. - Jonathan M Davis
Jul 09 2012
prev sibling parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
Thanks for doing this! I haven't contributed yet, but it was 
worrisome hearing about various pull requests languishing for 
long periods. Now maybe I should go learn how to use git...

On Monday, 9 July 2012 at 07:56:40 UTC, Jonathan M Davis wrote:
 As far as I'm concerned, 3.minutes() is a prime example of 
 what's wong UFCS.
 UFCS can be very useful, but oh how I hate that syntax 
 (completely aside from
 the particular function being called, I think that 3.anything() 
 is horrible).
 But obviously not everyone agrees.
extension methods until v3.0, but IIRC you could always write 3.ToString() or 3.HashCode and, incidentally, int.Parse("3") etc. Ruby has it too (not UFCS per se, but you actually can add methods to any class including integers, IIRC)
Jul 09 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/9/12 11:13 AM, David Piepgrass wrote:
 Thanks for doing this! I haven't contributed yet, but it was worrisome
 hearing about various pull requests languishing for long periods. Now
 maybe I should go learn how to use git...
You definitely should get to the point where you can contribute to D via github. Once you're there, it's easy and rewarding to fix issues small and large, or to make creative contributions. Andrei
Jul 09 2012
prev sibling next sibling parent reply Guillaume Chatelet <chatelet.guillaume gmail.com> writes:
On 07/09/2012 08:37 AM, Adam Wilson wrote:
 Ladies and Gentlemen,

 Today is a day to celebrate! By the time many of you read this you will
 be waking up to a new day, and too a new D. As of 0530 07/09/2012 UTC
 the programmers and testers of the D Development Team have merged an
 unprecedented 37 pull requests; all in a single day.

 A breakdown of the merges by project:
 DMD:        5  (05% of open pull requests)
 DRuntime:   17 (55% of open pull requests)
 Phobos:     14 (56% of open pull requests)
 DPL.org:    4  (50% of open pull requests)

 First I want to mention two people who really helped make this day happen.
 Jonathan M. Davis: For creating, testing, and submitting a patch that he
 was opposed to in std.datetime. This was a gentlemanly thing to do, and
 we applaud you for it.
 Alex Ronne Peterson: For his heroic efforts in fixing any errors in the
 merges as they arose and particularly for his efforts in fixing
 const-related errors.

 Next I would like to draw your attention to a couple of major
 improvements to D that were made today:
 Object is now const-correct throughout D. This has been a dream for many
 of you. Today it is a reality.
 Aliases for the 'dur' template. For example, you can now call
 3.minutes() and receive a Duration.

 Also merged where fixes for the following issues:
 http://d.puremagic.com/issues/show_bug.cgi?id=7965
 http://d.puremagic.com/issues/show_bug.cgi?id=8274
 http://d.puremagic.com/issues/show_bug.cgi?id=7375
 http://d.puremagic.com/issues/show_bug.cgi?id=4809
 http://d.puremagic.com/issues/show_bug.cgi?id=5547
 http://d.puremagic.com/issues/show_bug.cgi?id=1824
 http://d.puremagic.com/issues/show_bug.cgi?id=5011
 http://d.puremagic.com/issues/show_bug.cgi?id=8323

 I want to extend congratulations to the D Team and especially the
 members that were involved in today's pull request audit and merge.
 Because of you D is a better tool today than it has ever been.

 I also hope that these weekly Sunday Pull Audits will continue in the
 future as they will help propel D forward to new heights of reliability
 and usability. What the Team has accomplished is truly extraordinary,
 but we have a long way to go.

 As a final note. If you are using Git-HEAD builds of D for your
 development work, I strongly suggest that you update DMD, DRuntime, and
 Phobos at the same time. The Object-const correct changes affected all
 three of those projects and attempting to build any one of them without
 applying the changes to each of them will result in build failures. I
 also strongly suggest that those intrepid D users who are using Git-HEAD
 for their regular D work upgrade and report any bugs you find with the
 new code. With your help, 2.060 will be the best release of D yet!

 Again, thank you and congratulations to the D Team!
Congrats :)
Jul 09 2012
next sibling parent d coder <dlang.coder gmail.com> writes:
Congratulations.

I found that my applications still do not compile using the git HEAD.
Waiting eagerly for the 2.060 alpha release.

Regards
- Puneet
Jul 09 2012
prev sibling parent "SomeDude" <lovelydear mailmetrash.com> writes:
On Monday, 9 July 2012 at 08:05:01 UTC, Guillaume Chatelet wrote:
 Congrats :)
+1 The D community is awesome, and is getting better everyday.
Jul 15 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/09/2012 08:37 AM, Adam Wilson wrote:
 Object is now const-correct throughout D. This has been a dream for many
 of you. Today it is a reality.
PITA. Forced const can severely harm a code base that wants to be flexible -- it leaks implementation details and is infectuous. Any options planned to allow not inheriting all the cruft from Object if it is of no use? Eg: // object.di class RawObject { } class Object : RawObject { ... } // user.d class C { } // inherits from Object class D : RawObject { } // this does not
Jul 09 2012
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Jul 09, 2012 at 01:44:24PM +0200, Timon Gehr wrote:
 On 07/09/2012 08:37 AM, Adam Wilson wrote:
Object is now const-correct throughout D. This has been a dream for
many of you. Today it is a reality.
PITA. Forced const can severely harm a code base that wants to be flexible -- it leaks implementation details and is infectuous.
[...] Can you give an explicit example of code that is harmed by const correctness? IME, it rather helps code quality than harm it. Besides, since everything converts to const, it doesn't harm as much code as one might imagine (most code will be unchanged except where it matters -- which IMO is a good thing). But YMMV. T -- This sentence is false.
Jul 09 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/09/2012 05:00 PM, H. S. Teoh wrote:
 On Mon, Jul 09, 2012 at 01:44:24PM +0200, Timon Gehr wrote:
 On 07/09/2012 08:37 AM, Adam Wilson wrote:
 Object is now const-correct throughout D. This has been a dream for
 many of you. Today it is a reality.
PITA. Forced const can severely harm a code base that wants to be flexible -- it leaks implementation details and is infectuous.
[...] Can you give an explicit example of code that is harmed by const correctness?
1. Most code that gives amortized complexity guarantees, eg: interface Map(K, V){ V opIndex(K k) const; // ... } class SplayTree(K, V) : Map!(K, V) { // ??? } 2. - hash table - opApply compacts the table if it is occupied too sparsely, in order to speed up further iteration. - toString iterates over all key/value pairs by the means of opApply. Clearly, toString cannot be const in this setup. 3. Often, objects can cache derived properties to speed up the code. With 'const-correctness' in place, such an optimization is not transparent nor doable in a modular way.
 IME, it rather helps code quality than harm it.
I am not talking about code quality. I am talking about code maintainability, extensibility and performance.
 Besides, since everything converts to const, it doesn't harm as much code as
one
 might imagine (most code will be unchanged except where it matters --
 which IMO is a good thing).
Methods do not convert to const.
Jul 09 2012
next sibling parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
On Monday, 9 July 2012 at 16:02:38 UTC, Timon Gehr wrote:
 On 07/09/2012 05:00 PM, H. S. Teoh wrote:
 On Mon, Jul 09, 2012 at 01:44:24PM +0200, Timon Gehr wrote:
 On 07/09/2012 08:37 AM, Adam Wilson wrote:
 Object is now const-correct throughout D. This has been a 
 dream for many of you. Today it is a reality.
PITA. Forced const can severely harm a code base that wants to be flexible -- it leaks implementation details and is infectuous.
[...] Can you give an explicit example of code that is harmed by const correctness?
1. Most code that gives amortized complexity guarantees, eg: interface Map(K, V){ V opIndex(K k) const; // ... } class SplayTree(K, V) : Map!(K, V) { // ??? } 2. - hash table - opApply compacts the table if it is occupied too sparsely, in order to speed up further iteration. - toString iterates over all key/value pairs by the means of opApply. Clearly, toString cannot be const in this setup. 3. Often, objects can cache derived properties to speed up the code. With 'const-correctness' in place, such an optimization is not transparent nor doable in a modular way.
I guess D does not have 'mutable' (like C++) to override const on methods? Caching anything slow-to-compute is my typical use case, and I know a hashtable design where the getter will move whatever value at finds to the front of a hash collision chain. Oh, and this is interesting, I implemented a B+tree-like data "frozen", making it copy-on-write. In order to clone in O(1), the children are not marked as frozen until later, when someone actually wants to mutate one of the copies. A user can also make the tree immutable in O(1) time and freely make mutable copies of it. This use case is pretty complex, so if I port this to D, I'd course, has no const so it was never a concern there. *it's actually way fancier than that, I should really write a CodeProject article on it. Of course, the trouble is, you can call any const method on an immutable object, so any const method that mutates needs to be thread safe. Many uses of C++ 'mutable' are thread-safe (e.g. most platforms guarantee atomic pointer-size writes, right? So two threads can cache the same int or two equivalent class instances, and it doesn't matter who wins)... but many other cases are not (e.g. the hashtable). This is not a solved problem, is it. Ideas?
Jul 09 2012
next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, July 09, 2012 18:34:14 David Piepgrass wrote:
 I guess D does not have 'mutable' (like C++) to override const on
 methods?
No, const is actually const. You can't modify anything which is const through that reference (or pointer) to that data. Casting away const and mutating something is undefined behavior. This provides much stronger guarantees and better enables compiler optimizations. More importantly, it's required for immutabel, since a const variable could actually be immutable underneath, in which case, depending on how it was constructed (e.g. it was put in ROM), it could result in a segfault if you tried to mutate it after casting away const. http://stackoverflow.com/questions/4219600/logical-const-in-d So, const gives you much greater benefits in D than in C++, but it also comes at a much higher cost. And it comes to a bit of a head in Object, because certain function _must_ be const in Object in order to work with const (namely opEquals, opCmp, toHash, and toString), but they _can't_ be const for certain schemes (e.g. caching) to work. The open question is how to fix this (or if it even can be fixed). But the current plan is to make all 4 of those functions be safe pure const nothrow, and that _will_ make certain schemes effectively infeasible. - Jonathan M Davis
Jul 09 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/9/12 12:34 PM, David Piepgrass wrote:
 I guess D does not have 'mutable' (like C++) to override const on
 methods?
It doesn't, which makes life difficult for certain idioms. On the upside, you can assume more than C++ does about immutable data.
 Caching anything slow-to-compute is my typical use case, and I
 know a hashtable design where the getter will move whatever value at
 finds to the front of a hash collision chain.
Yah, that won't be easy to implement. We have a couple of designs in mind for "mutable", but didn't get to it. Regarding advantages, think of this - if an immutable instance of your bring-to-front hashtable were used by multiple threads it would fail because it's "lying" about its being immutable. For now just don't use const for what ain't.
 Oh, and this is interesting, I implemented a B+tree-like data structure*

 it copy-on-write. In order to clone in O(1), the children are not marked
 as frozen until later, when someone actually wants to mutate one of the
 copies. A user can also make the tree immutable in O(1) time and freely
 make mutable copies of it. This use case is pretty complex, so if I port
 this to D, I'd probably just cast away const/immutable where necessary.


 *it's actually way fancier than that, I should really write a
 CodeProject article on it.

 Of course, the trouble is, you can call any const method on an immutable
 object, so any const method that mutates needs to be thread safe. Many
 uses of C++ 'mutable' are thread-safe (e.g. most platforms guarantee
 atomic pointer-size writes, right? So two threads can cache the same int
 or two equivalent class instances, and it doesn't matter who wins)...
 but many other cases are not (e.g. the hashtable).

 This is not a solved problem, is it. Ideas?
One idea we're considering is to plant a pointer to mutable data together with the mutex pointer in classes. Andrei
Jul 09 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/09/2012 07:49 PM, Andrei Alexandrescu wrote:
 On 7/9/12 12:34 PM, David Piepgrass wrote:
 I guess D does not have 'mutable' (like C++) to override const on
 methods?
It doesn't, which makes life difficult for certain idioms. On the upside, you can assume more than C++ does about immutable data.
 Caching anything slow-to-compute is my typical use case, and I
 know a hashtable design where the getter will move whatever value at
 finds to the front of a hash collision chain.
Yah, that won't be easy to implement. We have a couple of designs in mind for "mutable", but didn't get to it. Regarding advantages, think of this - if an immutable instance of your bring-to-front hashtable were used by multiple threads it would fail because it's "lying" about its being immutable. For now just don't use const for what ain't.
This is very inconvenient if the root of the class hierarchy uses it.
Jul 09 2012
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Jul 09, 2012 at 08:07:22PM +0200, Timon Gehr wrote:
 On 07/09/2012 07:49 PM, Andrei Alexandrescu wrote:
On 7/9/12 12:34 PM, David Piepgrass wrote:
I guess D does not have 'mutable' (like C++) to override const on
methods?
It doesn't, which makes life difficult for certain idioms. On the upside, you can assume more than C++ does about immutable data.
Caching anything slow-to-compute is my typical use case, and I know
a hashtable design where the getter will move whatever value at
finds to the front of a hash collision chain.
Yah, that won't be easy to implement. We have a couple of designs in mind for "mutable", but didn't get to it. Regarding advantages, think of this - if an immutable instance of your bring-to-front hashtable were used by multiple threads it would fail because it's "lying" about its being immutable. For now just don't use const for what ain't.
This is very inconvenient if the root of the class hierarchy uses it.
[...] I'm wondering if it makes any sense to have _also_ have non-const versions of things like toString, for objects that want to implement caching. So in contexts where const is not important, you can have caching, network access, whatever you want, but for core language stuff that needs to assume const, everything will still work (just a little slower). I'm thinking of the case where some objects might be kept in ROM, say, in which case you can't cache within the class, even if you wanted to. But for other instances of the class that are outside ROM, you can be free to use the non-const, caching, network-accessing version of toString to your heart's content. After all, we have inout for a reason; here's a case where we need to do the opposite of inout (have both a const and non-const version of a method). Or am I just spouting nonsense again? ;-) T -- Help a man when he is in trouble and he will remember you when he is in trouble again.
Jul 09 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, July 09, 2012 11:53:05 H. S. Teoh wrote:
 I'm wondering if it makes any sense to have _also_ have non-const
 versions of things like toString, for objects that want to implement
 caching. So in contexts where const is not important, you can have
 caching, network access, whatever you want, but for core language stuff
 that needs to assume const, everything will still work (just a little
 slower).
That works as long as the const version still works. As soon as you _need_ to mutate in order to do opEquals, opCmp, toString, or toHash (as can happen with lazy loading schemes), it doesn't work. Basic caching should work with that though. - Jonathan M Davis
Jul 09 2012
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
David Piepgrass:

 This use case is pretty complex, so if I port this to D, I'd 
 probably just cast away const/immutable where necessary.
You are not the first person that says similar things. So D docs need to stress more than casting away const/immutable in D is rather more dangerous than doing the same thing in C++. Bye, bearophile
Jul 09 2012
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Tuesday, 10 July 2012 at 01:41:29 UTC, bearophile wrote:
 David Piepgrass:

 This use case is pretty complex, so if I port this to D, I'd 
 probably just cast away const/immutable where necessary.
You are not the first person that says similar things. So D docs need to stress more than casting away const/immutable in D is rather more dangerous than doing the same thing in C++.
Depends on what you are trying to do I suppose. The only mutation I would use is a new object (and cast away const to do a bulk copy which is otherwise annoying to do). If something is const(ant), it means you don't intend to change it. Perhaps this is a bad example: Let's say a class/struct is a book with Page protectors signifying 'const(ant)'. You promise to return the book to the library without making any changes; Although you promised you wouldn't make changes, you still take the Page protectors off and make make notes on the outer edges or make adjustments in the text, then return the book. Is this wise? This isn't C++. If something shouldn't change, then don't change it god damn it. If it needs to change it isn't const(ant) and shouldn't suggest it is. If the functions are const(ant) that you need to use, make non-const(ant) versions (or ensure the original(s) are unmodified) and/or disable the original ones (if you can/need, I think?). Seriously, it's not that hard a concept. I guess if something doesn't port well from C++ then redesign it. Some things done in C++ are hacks due to the language's limitations and faults. I hope I'm not rambling too much.
Jul 09 2012
parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
On Tuesday, 10 July 2012 at 02:43:05 UTC, Era Scarecrow wrote:
 On Tuesday, 10 July 2012 at 01:41:29 UTC, bearophile wrote:
 David Piepgrass:
 This use case is pretty complex, so if I port this to D, I'd 
 probably just cast away const/immutable where necessary.
You are not the first person that says similar things. So D docs need to stress more than casting away const/immutable in D is rather more dangerous than doing the same thing in C++.
...
  Let's say a class/struct is a book with Page protectors 
 signifying 'const(ant)'. You promise to return the book to the 
 library without making any changes; Although you promised you 
 wouldn't make changes, you still take the Page protectors off 
 and make make notes on the outer edges or make adjustments in 
 the text, then return the book.

  Is this wise? This isn't C++. If something shouldn't change, 
 then don't change it god damn it. If it needs to change it 
 isn't const(ant) and shouldn't suggest it is.
The difficulty, in case you missed it, is that somebody else (the Object class) says that certain functions are const, but in certain cases we really, really want to mutate something, either for efficiency or because "that's just how the data structure works". If a data structure needs to mutate itself when read, yeah, maybe its functions should not be marked const, but quite often the "const" is inherited from Object or some interface that (quite reasonably, it would seem) expects functions that /read stuff/ to be const. And yet we can't drop const from Object or such interfaces, because there is other code elsewhere that /needs/ const to be there. So far I have no solution to the dilemma in mind, btw. But the idea someone had of providing two (otherwise identical) functions, one const and one non-const, feels like a kludge to me, and note that anybody with an object would expect to be able to call the const version on any Object.
 Seriously, it's not that hard a concept. I guess if something 
 doesn't port well from C++ then redesign it. Some things done 
 in C++ are hacks due to the language's limitations and faults.
My particular data structure (a complex beast) contains a mutable tree of arbitrary size, which the user can convert to a conceptually immutable tree in O(1) time by calling Clone(). This marks a flag in the root node that says "read-only! do not change" and shares the root between the clones. At this point it should be safe to cast the clone to immutable. However, the original, mutable-typed version still exists. As the user requests changes to the mutable copy in the future, parts of the tree are duplicated to avoid changing the immutable nodes, with one exception: the read-only flag in various parts of the original, immutable tree will gradually be set to true. In this case, I don't think the D type system could do anything to help ensure that I don't modify the original tree that is supposed to be immutable. Since the static type of internal references must either be all mutable or all immutable, they will be typed mutable in the mutable copy, and immutable in the immutable copy, even though the two copies are sharing the same memory. And one flag, the read-only flag, must be mutable in this data structure, at least the transition from false->true must happen *after* the immutable copy is created; otherwise, Clone() would have to run in O(N) time, to mark every node read-only. This fact, however, does not affect the immutable copy in any way.
Jul 10 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 05:39:45PM +0200, David Piepgrass wrote:
[...]
 The difficulty, in case you missed it, is that somebody else (the
 Object class) says that certain functions are const, but in certain
 cases we really, really want to mutate something, either for
 efficiency or because "that's just how the data structure works". If
 a data structure needs to mutate itself when read, yeah, maybe its
 functions should not be marked const, but quite often the "const" is
 inherited from Object or some interface that (quite reasonably, it
 would seem) expects functions that /read stuff/ to be const.
 
 And yet we can't drop const from Object or such interfaces, because
 there is other code elsewhere that /needs/ const to be there.
 
 So far I have no solution to the dilemma in mind, btw. But the idea
 someone had of providing two (otherwise identical) functions, one
 const and one non-const, feels like a kludge to me, and note that
 anybody with an object would expect to be able to call the const
 version on any Object.
I think the trouble comes from conflating logical const with actual, bitwise, memory-representation const. Logical const is what C++ provides (or tries to, anyway). It's a way of saying that the object will not _visibly_ change, but says nothing about its underlying representation. So the object may be changing state internally all the time, but to the outside world it looks like it's not changing. D's const, however, is a _physical_ const. It's saying that the underlying representation of the object will not change, and therefore it will not visibly change either. This is a much stronger guarantee, but unfortunately, that also narrows its scope. It cannot handle the case where the object needs to change internally while still retaining the outward appearance of non-change. Conceptually, there shouldn't be any problem: if your object is one of those that changes internally but not visibly, then in D's viewpoint it's the same as a mutable object (which it is, physically speaking). However, the trouble comes when parts of the D runtime need certain guarantees, for example, a built-in hash function may expect that taking the hash of an object shouldn't change its state. Logically speaking, it's OK for the object's toHash method to change it (say, by caching a value that takes a long time to compute). But the D runtime wants to give _guarantees_ that nothing unexpected will happen. And so it requires toHash to be const. That way, even a rogue object method will not be able to change it (not without breaking the type system, anyway), and the runtime will be able to give strong guarantees that yes, literally _nothing_ will cause unexpected mutation to the object when you call its toHash method. But requiring toHash to be const means that you cannot cache the results of an expensive computation, and so certain things that would work with a logical const system don't work in D. So the bottom line boils down to, we want logical const for some objects, but D doesn't have logical const. Imagining that it does only breaks the type system and any guarantees the language provides. Whether we _should_ have logical const in D is, of course, something to be discussed, but the main objection against that is that it's not enforceable. What constitutes a "non-visible" change of state? It's not possible to tell without solving the halting problem, unfortunately. An object's state can be extremely complex, with only a certain subset of state changes being visible. There's no feasible way for the compiler to figure this out automatically, and so you end up with the C++ const, which can be cast away anytime, anyday, and therefore is pretty much useless except in theory. All it takes is for _one_ function to be const incorrect, and you have a hole in which supposedly immutable objects get changed when they aren't supposed to. [...]

 particular data structure (a complex beast) contains a mutable tree of
 arbitrary size, which the user can convert to a conceptually immutable
 tree in O(1) time by calling Clone(). This marks a flag in the root
 node that says "read-only! do not change" and shares the root between
 the clones. At this point it should be safe to cast the clone to
 immutable. However, the original, mutable-typed version still exists.
 As the user requests changes to the mutable copy in the future, parts
 of the tree are duplicated to avoid changing the immutable nodes, with
 one exception: the read-only flag in various parts of the original,
 immutable tree will gradually be set to true.
[...] Yeah, this is logical const. Unfortunately, D doesn't have logical const. T -- In a world without fences, who needs Windows and Gates? -- Christian Surchi
Jul 10 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/10/2012 06:45 PM, H. S. Teoh wrote:
 Yeah, this is logical const. Unfortunately, D doesn't have logical
 const.
Then why on earth is druntime acting as if it does?
Jul 10 2012
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote:
 On 07/10/2012 06:45 PM, H. S. Teoh wrote:
Yeah, this is logical const. Unfortunately, D doesn't have logical
const.
Then why on earth is druntime acting as if it does?
Y'know, this brings up an interesting question. Do methods like toString _need_ to be const? That is, _physical_ const? Or are we unconsciously conflating physical const with logical const here? Yes, certain runtime operations need to be able to work with const methods, but I wonder if those required const methods really belong to a core set of more primitive operations that guarantee physical const, and perhaps shouldn't be conflated with logical operations like "convert this object to a string representation", which _may_ require caching, etc.? Or perhaps application code want to be defining their own non-const versions of certain methods so that they can do whatever they need to do with logical const, without worrying about breaking physical const-ness. I'm starting to think that D's hardline approach to const is clashing with the principle of information hiding. Users of a class shouldn't _need_ to know if an object is caching the value of toString, toHash, or whatever it is. What they care for is that the object doesn't visibly change, that is, logical const. Binary const implies logical const, but the implication doesn't work the other way round. While it's nice to have binary const (strong, enforceable guarantee), it breaks encapsulation: just because a class needs to do caching, means its methods can't be const, and this is a visible (and viral, no less) change in its external API. What should just be an implementation detail has become a visible difference to the outside world -- encapsulation is broken. I don't know how to remedy this. It's clear that physical const does have its value -- it's necessary to properly support immutable, allows putting data in ROM, etc.. But it's also clear that something is missing from the picture. Implementation details are leaking past object APIs, caching and other abstractions can't work with const, etc., and that's not a good thing. T -- Doubt is a self-fulfilling prophecy.
Jul 10 2012
parent reply Don Clugston <dac nospam.com> writes:
On 10/07/12 19:13, H. S. Teoh wrote:
 On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote:
 On 07/10/2012 06:45 PM, H. S. Teoh wrote:
 Yeah, this is logical const. Unfortunately, D doesn't have logical
 const.
Then why on earth is druntime acting as if it does?
Y'know, this brings up an interesting question. Do methods like toString _need_ to be const? That is, _physical_ const? Or are we unconsciously conflating physical const with logical const here? Yes, certain runtime operations need to be able to work with const methods, but I wonder if those required const methods really belong to a core set of more primitive operations that guarantee physical const, and perhaps shouldn't be conflated with logical operations like "convert this object to a string representation", which _may_ require caching, etc.? Or perhaps application code want to be defining their own non-const versions of certain methods so that they can do whatever they need to do with logical const, without worrying about breaking physical const-ness. I'm starting to think that D's hardline approach to const is clashing with the principle of information hiding. Users of a class shouldn't _need_ to know if an object is caching the value of toString, toHash, or whatever it is. What they care for is that the object doesn't visibly change, that is, logical const. Binary const implies logical const, but the implication doesn't work the other way round. While it's nice to have binary const (strong, enforceable guarantee), it breaks encapsulation: just because a class needs to do caching, means its methods can't be const, and this is a visible (and viral, no less) change in its external API. What should just be an implementation detail has become a visible difference to the outside world -- encapsulation is broken. I don't know how to remedy this. It's clear that physical const does have its value -- it's necessary to properly support immutable, allows putting data in ROM, etc.. But it's also clear that something is missing from the picture. Implementation details are leaking past object APIs, caching and other abstractions can't work with const, etc., and that's not a good thing. T
I think you're right. Something I wonder about, though, is how many different use cases are we dealing with? Suppose we had a caching solution (you could think of it as cached, but it could be done in a library). The user would need to provide a const, pure function which returns the same value that is stored in the cache. This is enforceable. The only way to write to the cache, is by calling the function. How far would that take us? I don't think there are many use cases for logically pure, apart from caching, but I have very little idea about logical const.
Jul 11 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 3:58 AM, Don Clugston wrote:
 Something I wonder about, though, is how many different use cases are we
 dealing with?

 Suppose we had a caching solution (you could think of it as  cached, but
 it could be done in a library). The user would need to provide a const,
 pure function which returns the same value that is stored in the cache.
 This is enforceable. The only way to write to the cache, is by calling
 the function.

 How far would that take us? I don't think there are many use cases for
 logically pure, apart from caching, but I have very little idea about
 logical const.
I think a caching solution would cover most valid needs and indeed would be checkable. We can even try its usability with a library-only solution. The idea is to plant a mixin inside the object that defines a static hashtable mapping addresses of objects to cached values of the desired types. The destructor of the object removes the address of the current object from the hash (if there). Given that the hashtable is global, it doesn't obey the regular rules for immutability, so essentially each object has access to a private stash of unbounded size. The cost of getting to the stash is proportional to the number of objects within the thread that make use of that stash. Sample usage: class Circle { private double radius; private double circumferenceImpl() const { return radius * 2 * pi; } mixin Cached!(double, "circumference", circumferenceImpl); ... } auto c = new const(Circle); double len1 = c.circumference; double len2 = c.circumference; Upon the first use of property c.circumference, Lazy computes the value by calling this.circumferenceImpl() and stashes it in the hash. The second call just does a hash lookup. In this example searching the hash may actually take longer than computing the thing, but I'm just proving the concept. If this is a useful artifact, Walter had an idea a while ago that we can have the compiler help by using the per-object monitor pointer instead of the static hashtable. Right now the pointer points to a monitor object, but it could point to a little struct containing e.g. a Monitor and a void*, which opens the way to O(1) access to unbounded cached data. The compiler would then "understand" to not consider that date regular field accesses, and not make assumptions about them being immutable. Any takers for Cached? It would be good to assess its level of usefulness first. Andrei
Jul 11 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 8:55 AM, Andrei Alexandrescu wrote:
 The cost of getting to the stash is
 proportional to the number of objects within the thread that make use of
 that stash.
Oops, that would be with linear search :o). Andrei
Jul 11 2012
prev sibling next sibling parent "Roman D. Boiko" <rb d-coding.com> writes:
On Wednesday, 11 July 2012 at 12:55:39 UTC, Andrei Alexandrescu 
wrote:
 Any takers for Cached? It would be good to assess its level of 
 usefulness first.
As for me, lazy computation (with caching) would likely be the last feature I really miss in D.
Jul 11 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 02:55 PM, Andrei Alexandrescu wrote:
 ...
 If this is a useful artifact, Walter had an idea a while ago that we can
 have the compiler help by using the per-object monitor pointer instead
 of the static hashtable. Right now the pointer points to a monitor
 object, but it could point to a little struct containing e.g. a Monitor
 and a void*, which opens the way to O(1) access to unbounded cached
 data. The compiler would then "understand" to not consider that date
 regular field accesses, and not make assumptions about them being
 immutable.
The additional indirection has no impact. This is 'mutable' in disguise.
 Any takers for Cached? It would be good to assess its level of
 usefulness first.


 Andrei
Well, it is inefficient.
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 10:33 AM, Timon Gehr wrote:
 Any takers for Cached? It would be good to assess its level of
 usefulness first.


 Andrei
Well, it is inefficient.
The idea here is to assess functionality provided in order to decide whether to go down this route or not. Andrei
Jul 11 2012
next sibling parent "Roman D. Boiko" <rb d-coding.com> writes:
On Wednesday, 11 July 2012 at 14:42:43 UTC, Andrei Alexandrescu 
wrote:
 On 7/11/12 10:33 AM, Timon Gehr wrote:
 Any takers for Cached? It would be good to assess its level of
 usefulness first.


 Andrei
Well, it is inefficient.
The idea here is to assess functionality provided in order to decide whether to go down this route or not. Andrei
Probably using a separate thread would be better for voting, since this one already has many branches of ideas.
Jul 11 2012
prev sibling parent "Roman D. Boiko" <rb d-coding.com> writes:
On Wednesday, 11 July 2012 at 14:42:43 UTC, Andrei Alexandrescu 
wrote:
 On 7/11/12 10:33 AM, Timon Gehr wrote:
 Any takers for Cached? It would be good to assess its level of
 usefulness first.


 Andrei
Well, it is inefficient.
The idea here is to assess functionality provided in order to decide whether to go down this route or not. Andrei
Probably using a separate thread would be better for voting or discussing, since this one already has many branches of ideas.
Jul 11 2012
prev sibling parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
 Suppose we had a caching solution (you could think of it as 
  cached, but
 it could be done in a library). The user would need to provide 
 a const,
 pure function which returns the same value that is stored in 
 the cache.
 This is enforceable. The only way to write to the cache, is by 
 calling
 the function.

 How far would that take us? I don't think there are many use 
 cases for
 logically pure, apart from caching, but I have very little 
 idea about
 logical const.
I think a caching solution would cover most valid needs and indeed would be checkable. We can even try its usability with a library-only solution. The idea is to plant a mixin inside the object that defines a static hashtable mapping addresses of objects to cached values of the desired types. The destructor of the object removes the address of the current object from the hash (if there). Given that the hashtable is global, it doesn't obey the regular rules for immutability, so essentially each object has access to a private stash of unbounded size. The cost of getting to the stash is proportional to the number of objects within the thread that make use of that stash.
Uh, it better not be proportional. Hashtable gives us O(1), one hopes.
 Sample usage:

 class Circle {
     private double radius;
     private double circumferenceImpl() const {
         return radius * 2 * pi;
     }
     mixin Cached!(double, "circumference", circumferenceImpl);
     ...
 }

 auto c = new const(Circle);
Aside: what's the difference between this and new immutable(Circle)?
 double len1 = c.circumference;
 double len2 = c.circumference;

 Upon the first use of property c.circumference, Lazy computes 
 the value by calling this.circumferenceImpl() and stashes it in 
 the hash. The second call just does a hash lookup.

 In this example searching the hash may actually take longer 
 than computing the thing, but I'm just proving the concept.

 If this is a useful artifact, Walter had an idea a while ago 
 that we can have the compiler help by using the per-object 
 monitor pointer instead of the static hashtable. Right now the 
 pointer points to a monitor object, but it could point to a 
 little struct containing e.g. a Monitor and a void*, which 
 opens the way to O(1) access to unbounded cached data. The 
 compiler would then "understand" to not consider that date 
 regular field accesses, and not make assumptions about them 
 being immutable.

 Any takers for Cached? It would be good to assess its level of 
 usefulness first.
I like this idea, and I suspect it could be used to implement not just caching but lazy immutable data structures. Except that I don't see why Cached!(...) needs to physically separate the mutable state from the rest of the object. I mean, I see that Cached!(...) would have to cast away immutable (break the type system) in order to put mutable state in an immutable object, but if we set aside the current type system for a moment, *in principle* what's the big deal if the mutable state is physically located within the object? In many cases you can save significant time and memory by avoiding all that hashtable management, and performance Nazis like me will want that speed (when it comes to standard libraries, I demand satisfaction). Now, I recognize and respect the benefits of transitive immutability: 1. safe multithreading 2. allowing compiler optimizations that are not possible in C++ 3. ability to store compile-time immutable literals in ROM (3) does indeed require mutable state to be stored separately, but it doesn't seem like a common use case (and there is a workaround), and I don't see how (1) and (2) are necessarily broken. As a separate question, do you think it possible to implement Cached!(...) to access an immutable field by casting away immutable, without screwing up (1) and (2)?
Jul 11 2012
parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
 Except that I don't see why Cached!(...) needs to physically 
 separate the mutable state from the rest of the object. I mean, 
 I see that Cached!(...) would have to cast away immutable 
 (break the type system) in order to put mutable state in an 
 immutable object, but if we set aside the current type system 
 for a moment, *in principle* what's the big deal if the mutable 
 state is physically located within the object? In many cases 
 you can save significant time and memory by avoiding all that 
 hashtable management, and performance Nazis like me will want 
 that speed (when it comes to standard libraries, I demand 
 satisfaction).

 Now, I recognize and respect the benefits of transitive 
 immutability:
 1. safe multithreading
 2. allowing compiler optimizations that are not possible in C++
 3. ability to store compile-time immutable literals in ROM

 (3) does indeed require mutable state to be stored separately, 
 but it doesn't seem like a common use case (and there is a 
 workaround), and I don't see how (1) and (2) are necessarily 
 broken.
I must be tired. Regarding (1), right after posting this I remembered the difference between caching to a "global" hashtable and storing the cached value directly within the object: the hashtable is thread-local, but the object itself may be shared between threads. So that's a pretty fundamental difference. Even so, if Cached!(...) puts mutable state directly in the object, fast synchronization mechanisms could be used to ensure that two threads don't step on each other, if they both compute the cached value at the same time. If the cached value is something simple like a hashcode, an atomic write should suffice. And both threads should compute the same result so it doesn't matter who wins.
Jul 11 2012
parent travert phare.normalesup.org (Christophe Travert) writes:
"David Piepgrass" , dans le message (digitalmars.D:172009), a écrit :
 Now, I recognize and respect the benefits of transitive 
 immutability:
 1. safe multithreading
 2. allowing compiler optimizations that are not possible in C++
 3. ability to store compile-time immutable literals in ROM

 (3) does indeed require mutable state to be stored separately, 
 but it doesn't seem like a common use case (and there is a 
 workaround), and I don't see how (1) and (2) are necessarily 
 broken.
I must be tired. Regarding (1), right after posting this I remembered the difference between caching to a "global" hashtable and storing the cached value directly within the object: the hashtable is thread-local, but the object itself may be shared between threads. So that's a pretty fundamental difference. Even so, if Cached!(...) puts mutable state directly in the object, fast synchronization mechanisms could be used to ensure that two threads don't step on each other, if they both compute the cached value at the same time. If the cached value is something simple like a hashcode, an atomic write should suffice. And both threads should compute the same result so it doesn't matter who wins.
Yes. It is possible to write a library solution to compute a cached value by casting away const in a safe manner, even in a multithreaded environment. The limitation is that, if I'm not mistaken, this library solution cannot ensure it is not immutable (and potentially placed in ROM) when it is const, making the cast undefined.
Jul 12 2012
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
On Jul 11, 2012, at 12:58 AM, Don Clugston <dac nospam.com> wrote:

 On 10/07/12 19:13, H. S. Teoh wrote:
 On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote:
 On 07/10/2012 06:45 PM, H. S. Teoh wrote:
 Yeah, this is logical const. Unfortunately, D doesn't have logical
 const.
=20
=20 Then why on earth is druntime acting as if it does?
=20 Y'know, this brings up an interesting question. Do methods like toString _need_ to be const? That is, _physical_ const? Or are we unconsciously conflating physical const with logical const here? =20 Yes, certain runtime operations need to be able to work with const methods, but I wonder if those required const methods really belong to a core set of more primitive operations that guarantee physical const, and perhaps shouldn't be conflated with logical operations like "convert this object to a string representation", which _may_ require caching, etc.? =20 Or perhaps application code want to be defining their own non-const versions of certain methods so that they can do whatever they need to do with logical const, without worrying about breaking physical const-ness. =20 I'm starting to think that D's hardline approach to const is clashing with the principle of information hiding. Users of a class shouldn't _need_ to know if an object is caching the value of toString, toHash, or whatever it is. What they care for is that the object doesn't visibly change, that is, logical const. Binary const implies logical const, but the implication doesn't work the other way round. While it's nice to have binary const (strong, enforceable guarantee), it breaks encapsulation: just because a class needs to do caching, means its methods can't be const, and this is a visible (and viral, no less) change in its external API. What should just be an implementation detail has become a visible difference to the outside world -- encapsulation is broken. =20 I don't know how to remedy this. It's clear that physical const does have its value -- it's necessary to properly support immutable, allows putting data in ROM, etc.. But it's also clear that something is missing from the picture. Implementation details are leaking past object APIs, caching and other abstractions can't work with const, etc., and that's not a good thing. =20 =20 T
=20 I think you're right. Something I wonder about, though, is how many different use cases are we d=
ealing with?
=20
 Suppose we had a caching solution (you could think of it as  cached, but i=
t could be done in a library). The user would need to provide a const, pure f= unction which returns the same value that is stored in the cache.
 This is enforceable. The only way to write to the cache, is by calling the=
function.
=20
 How far would that take us? I don't think there are many use cases for log=
ically pure, apart from caching, but I have very little idea about logical c= onst. Lazy loading, which I suppose is a type of caching. Use of synchronization p= rimitives should be allowed as well. We sidestep this because the synchroniz= ed statement sidesteps const restrictions.=20=
Jul 11 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, July 10, 2012 10:13:57 H. S. Teoh wrote:
 On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote:
 On 07/10/2012 06:45 PM, H. S. Teoh wrote:
Yeah, this is logical const. Unfortunately, D doesn't have logical
const.
Then why on earth is druntime acting as if it does?
Y'know, this brings up an interesting question. Do methods like toString _need_ to be const? That is, _physical_ const? Or are we unconsciously conflating physical const with logical const here? Yes, certain runtime operations need to be able to work with const methods, but I wonder if those required const methods really belong to a core set of more primitive operations that guarantee physical const, and perhaps shouldn't be conflated with logical operations like "convert this object to a string representation", which _may_ require caching, etc.?
For a member function to be called on a const object, that function must be const. Whether it's logical const or physical const is irrelevant as far as that goes. As such, opEquals, opCmp, toHash, and toString all need to be const on Object, or it will be impossible for const Objects to work properly. Ideally, we'd alsa have a way to make it possible to have objects which aren't const and can't be const use those functions (which given physical constness obviously requires a separate function - be it an overload or an entirely separate function), but without those functions being const, const objects don't work. Of greater debate is whether opEquals, opCmp, toString, and toHash on structs need to be const. Aside from druntime functions wanting to be to take their arguments as const, I don't see really see that as being necessary (though Walter wanst to require that they all be safe const pure nothrow regardless of whether they're classes or structs), and since druntime probably has to templatize the functions which would take const anyway (and it can use inout or templatize them anyway if it doesn't need to), I wouldn't expect that much in druntime would require const. It should work with it, but it shouldn't need it. So, I don't think that structs really need to have those functions be const. Classes is where it's a big problem - because of inheritance. - Jonathan M Davis
Jul 10 2012
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 02:04:04PM -0400, Jonathan M Davis wrote:
 On Tuesday, July 10, 2012 10:13:57 H. S. Teoh wrote:
[...]
 Y'know, this brings up an interesting question. Do methods like
 toString _need_ to be const? That is, _physical_ const? Or are we
 unconsciously conflating physical const with logical const here?
 
 Yes, certain runtime operations need to be able to work with const
 methods, but I wonder if those required const methods really belong
 to a core set of more primitive operations that guarantee physical
 const, and perhaps shouldn't be conflated with logical operations
 like "convert this object to a string representation", which _may_
 require caching, etc.?
For a member function to be called on a const object, that function must be const. Whether it's logical const or physical const is irrelevant as far as that goes. As such, opEquals, opCmp, toHash, and toString all need to be const on Object, or it will be impossible for const Objects to work properly.
Yes, they have to be const, but by doing so, we are implicitly forcing physical constness on all of them (because that's the only const D knows). The question is whether this is the way we should go.
 Ideally, we'd alsa have a way to make it possible to have objects
 which aren't const and can't be const use those functions (which given
 physical constness obviously requires a separate function - be it an
 overload or an entirely separate function), but without those
 functions being const, const objects don't work.
Which is why I suggested in another post to have both const and non-const variants of the methods. But that isn't a good solution either, because what if some objects simply can't have const versions of those methods? Plus, it leads to needless code duplication -- even if you implement a non-const toString method, you still need to also implement a const toString method because people will expect to be able to call the const method.
 Of greater debate is whether opEquals, opCmp, toString, and toHash on
 structs need to be const. Aside from druntime functions wanting to be
 to take their arguments as const, I don't see really see that as being
 necessary (though Walter wanst to require that they all be  safe const
 pure nothrow regardless of whether they're classes or structs), and
 since druntime probably has to templatize the functions which would
 take const anyway (and it can use inout or templatize them anyway if
 it doesn't need to), I wouldn't expect that much in druntime would
 require const. It should work with it, but it shouldn't need it. So, I
 don't think that structs really need to have those functions be const.
 
 Classes is where it's a big problem - because of inheritance.
[...] I think hidden somewhere in this is an unconscious conflation of physical const with logical const. Take toHash, for example. Why does it need to be const? The naïve assumption would be, well, we're taking the hash of some field values, and that shouldn't require changing anything, so yeah, it's a const method. However, that doesn't fully cover all the use cases of toHash. For one thing, hashes _don't_ need to be based on every single field in the struct/object. I can easily decide, in my custom toHash function, to only compute the hash value based on two out of 5 fields in my struct (perhaps only those two fields matter for whatever I'm using the hash value for). So I don't care what the value of the other fields are. In particular, if the hash value is expensive to compute, I want to be able to cache the computed value in one of the other fields. So here's a hidden assumption, that toHash must be const -- it must be logical const, yes, but that is in no way equivalent to physical const. In this case, I can't use toHash at all, because it doesn't permit caching, even though its computed value is based only on the unchanged fields. Or, to take this point further, what I _really_ mean is that if my struct is: struct S { string x,y; // hash computed on these values hash_t cache; int p,q; // not used by toHash } then my toHash method really is expecting this struct: struct logical_const_S { const(string) x,y; hash_t cache; int p,q; // these can be const or not, we don't care } AFAIK, D currently doesn't allow implicit conversion from S to logical_const_S. If it did, and if there was a simple way to express this in the method signature of toHash, then I bet a lot of the complaints about const in druntime will go away, because then we'd have a way of doing caching or whatever it is people feel is indispensible, *without* breaking D's const system. Now to bring this to my other point: the conversion S -> logical_const_S would allow, to some limited extent, a non-leaky object API (and by leaky I mean breaks encapsulation). Currently, if I declare toHash as a const method, it means that I guarantee the object won't mutate in that method, not even mutation that *still retains the same logical value*. But the user of my class doesn't -- and shouldn't -- care about that. As long as the public methods of the class do not exhibit any visible change, then I should have the freedom to mutate whatever I like inside a logical const method. For example, say I have this base class: class B { private string x, y; hash_t toHash() { // compute hash value based on x and y } string xGetter() const { return x; } string yGetter() const { return y; } } Now say I have a derived class: class D : B { bool cached = false; hash_t hash_cache; override hash_t toHash() { if (!cached) { hash_cache = /* expensive computation */ cached = true; } else { return hash_cache; } } } What I _really_ want to be able to do, is to declare D.toHash() as taking this class instead: class logical_const_D { private const(string) x, y; bool cached = false; hash_t hash_cache; ... } This class has const versions of the fields inherited from B, but _mutable_ versions of cached and hash_cache. Such an object can still be used with B.xGetter and B.yGetter, because as far as _they're_ concerned, the object is still const. More importantly, if the language allows implicit conversion from D to logical_const_D, (since mutable x can implicitly convert to const x, and ditto for y), then the definition of logical_const_D doesn't have to be public. Thus, I can declare my class D something like this: class D : B { private: // define logical_const_D here public: hash_t toHash() logical_const_D { ... } } The end-user doesn't need to know what logical_const_D is; if he has an object of type D that can implicitly convert to logical_const_D, then he can use toHash() on it. If he has an immutable(D), then he can't use toHash() (because immutable(bool) and immutable(hash_t) can't implicitly convert to bool and hash_t). This way, we preserve the type system, *and* allow a caching implementation of toHash, *and* preserve encapsulation (user doesn't need to know which fields actually get changed by toHash -- that's an implementation detail). T -- Being able to learn is a great learning; being able to unlearn is a greater learning.
Jul 10 2012
prev sibling next sibling parent reply Geoffrey Biggs <geoffrey.biggs aist.go.jp> writes:
On 10 July 2012 01:02, Timon Gehr <timon.gehr gmx.ch> wrote:
 1.

 Most code that gives amortized complexity guarantees, eg:

 interface Map(K, V){
     V opIndex(K k) const;
     // ...
 }

 class SplayTree(K, V) : Map!(K, V) {
     // ???
 }

 2.

 - hash table
 - opApply compacts the table if it is occupied too sparsely, in order
   to speed up further iteration.
 - toString iterates over all key/value pairs by the means of opApply.

 Clearly, toString cannot be const in this setup.

 3.

 Often, objects can cache derived properties to speed up the code. With
 'const-correctness' in place, such an optimization is not transparent
 nor doable in a modular way.



 IME, it rather helps code quality than harm it.
I am not talking about code quality. I am talking about code maintainability, extensibility and performance.
Although I don't know what D's goals with const are, in C++, const-correctness is often considered from the perspective of outside the interface. A const member function guarantees that, from the point of view of an entity using the object, its state will not change. This does not guarantee that data stored in the object is not changing. This allows for things like storing cached values. Unfortunately, implementing this is more difficult than it might sound, e.g. in cases that the const keyword can allow (which you should). See here for more information: http://www.parashift.com/c++-faq-lite/mutable-data-members.html Geoff
Jul 09 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/10/2012 01:57 AM, Geoffrey Biggs wrote:
 On 10 July 2012 01:02, Timon Gehr<timon.gehr gmx.ch>  wrote:
 ...
 2.

 - hash table
 - opApply compacts the table if it is occupied too sparsely, in order
    to speed up further iteration.
 - toString iterates over all key/value pairs by the means of opApply.

 Clearly, toString cannot be const in this setup.

 ...
 I am not talking about code quality. I am talking about code
 maintainability, extensibility and performance.
Although I don't know what D's goals with const are, in C++, const-correctness is often considered from the perspective of outside the interface. A const member function guarantees that, from the point of view of an entity using the object, its state will not change. This does not guarantee that data stored in the object is not changing. This allows for things like storing cached values. Unfortunately, implementing this is more difficult than it might sound, e.g. in cases that the const keyword can allow (which you should). See here for more information: http://www.parashift.com/c++-faq-lite/mutable-data-members.html Geoff
Exactly! I don't think that the term 'const correctness' actually has a meaning in D. It is a C++ term.
Jul 09 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
I don't think that is a big issue.

Either the value isn't accessed often, and then it make no sense to 
cache it, or it is accessed often and it make sense to precalculate the 
cached value when building the object.

Doing the calculation at that point avoid synchronization cost involved 
otherwise.

People here talk about the atomicity of a pointer write. This is true, 
but the object pointed isn't guaranteed to be committed to memory, so 
synchronization is required anyway.
Jul 11 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 02:58 PM, deadalnix wrote:
 I don't think that is a big issue.

 Either the value isn't accessed often, and then it make no sense to
 cache it, or it is accessed often and it make sense to precalculate the
 cached value when building the object.
And sometimes it is not known in advance if it will be accessed at all. And this is somewhere in a class that seems unrelated to the object that wants to compute a string representation/hash value/comparison. It is not the simple cases that are an issue.
Jul 11 2012
parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 16:04, Timon Gehr wrote:
 On 07/11/2012 02:58 PM, deadalnix wrote:
 I don't think that is a big issue.

 Either the value isn't accessed often, and then it make no sense to
 cache it, or it is accessed often and it make sense to precalculate the
 cached value when building the object.
And sometimes it is not known in advance if it will be accessed at all. And this is somewhere in a class that seems unrelated to the object that wants to compute a string representation/hash value/comparison. It is not the simple cases that are an issue.
Arguably, this is a software design problem. I never encountered a case where I need to do such thing and never encountered something similar in code except code that demonstrate trick or subverting language features/librairies in unexpected ways (which is always fun to do, but should never ends up in production or be promoted by a language/a lib).
Jul 11 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 16:16, deadalnix wrote:
 On 11/07/2012 16:04, Timon Gehr wrote:
 On 07/11/2012 02:58 PM, deadalnix wrote:
 I don't think that is a big issue.

 Either the value isn't accessed often, and then it make no sense to
 cache it, or it is accessed often and it make sense to precalculate the
 cached value when building the object.
And sometimes it is not known in advance if it will be accessed at all. And this is somewhere in a class that seems unrelated to the object that wants to compute a string representation/hash value/comparison. It is not the simple cases that are an issue.
Arguably, this is a software design problem. I never encountered a case where I need to do such thing and never encountered something similar in code except code that demonstrate trick or subverting language features/librairies in unexpected ways (which is always fun to do, but should never ends up in production or be promoted by a language/a lib).
By the way, I'd be happy to be proven false, but I think the issue is overstated here.
Jul 11 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 04:16 PM, deadalnix wrote:
 On 11/07/2012 16:16, deadalnix wrote:
 On 11/07/2012 16:04, Timon Gehr wrote:
 On 07/11/2012 02:58 PM, deadalnix wrote:
 I don't think that is a big issue.

 Either the value isn't accessed often, and then it make no sense to
 cache it, or it is accessed often and it make sense to precalculate the
 cached value when building the object.
And sometimes it is not known in advance if it will be accessed at all. And this is somewhere in a class that seems unrelated to the object that wants to compute a string representation/hash value/comparison. It is not the simple cases that are an issue.
Arguably, this is a software design problem. I never encountered a case where I need to do such thing and never encountered something similar in code except code that demonstrate trick or subverting language features/librairies in unexpected ways (which is always fun to do, but should never ends up in production or be promoted by a language/a lib).
By the way, I'd be happy to be proven false, but I think the issue is overstated here.
Frankly, there is no real issue. It is always possible to sidestep the built-in syntax-sugared library-supported functionality. The inefficiencies caused by the unused vtable slots are likely neglegible. It's just not something one wants to do nor encourage to do.
Jul 11 2012
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 04:16 PM, deadalnix wrote:
 On 11/07/2012 16:04, Timon Gehr wrote:
 On 07/11/2012 02:58 PM, deadalnix wrote:
 I don't think that is a big issue.

 Either the value isn't accessed often, and then it make no sense to
 cache it, or it is accessed often and it make sense to precalculate the
 cached value when building the object.
And sometimes it is not known in advance if it will be accessed at all. And this is somewhere in a class that seems unrelated to the object that wants to compute a string representation/hash value/comparison. It is not the simple cases that are an issue.
Arguably, this is a software design problem.
Abstraction is a software design problem?
 I never encountered a case where I need to do such thing
This means it will bite you in expectedly the next 48 hours. I have some experience with making statements like this.
 and never encountered something similar in
 code except code that demonstrate trick or subverting language
 features/librairies in unexpected ways (which is always fun to do, but
 should never ends up in production or be promoted by a language/a lib).
I'll be happy to inspect eg. SDC as soon as it reliably compiles D code.
Jul 11 2012
prev sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Monday, 9 July 2012 at 14:59:31 UTC, H. S. Teoh wrote:
 On Mon, Jul 09, 2012 at 01:44:24PM +0200, Timon Gehr wrote:
 On 07/09/2012 08:37 AM, Adam Wilson wrote:
Object is now const-correct throughout D. This has been a 
dream for
many of you. Today it is a reality.
PITA. Forced const can severely harm a code base that wants to be flexible -- it leaks implementation details and is infectuous.
[...] Can you give an explicit example of code that is harmed by const correctness? IME, it rather helps code quality than harm it. Besides, since everything converts to const, it doesn't harm as much code as one might imagine (most code will be unchanged except where it matters -- which IMO is a good thing). But YMMV. T
This is like the third time I bring up this example around this topic, but forcing const harms wrappers of state-machine style interfaces, which many C libraries use. Here's LuaObject.opEquals from LuaD: /** * Compare this object to another with Lua's equality semantics. * Also returns false if the two objects are in different Lua states. */ bool opEquals(T : LuaObject)(ref T o) trusted { if(o.state != this.state) return false; push(); o.push(); scope(success) lua_pop(state, 2); return lua_equal(state, -1, -2); } This works because LuaObject is currently a struct, but that's an irrelevant detail, it could just as well be a class in a different scenario. This opEquals is only logically constant, not bitwise constant, hence it must not be const; similar to the caching scenario. Just trying to demonstrate that the issue is bigger than just caching - it's any logically constant comparison function.
Jul 09 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/9/2012 3:27 PM, Jakob Ovrum wrote:
 This opEquals is only logically constant, not bitwise constant, hence it must
 not be const; similar to the caching scenario. Just trying to demonstrate that
 the issue is bigger than just caching - it's any logically constant comparison
 function.
I understand, but the downside of not making these functions const is it will torpedo the use of functional style programming in D. A straightforward workaround is to use PIMPL to encapsulate the logical const stuff, and then cast the reference to const to use inside the opEquals.
Jul 10 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 12:05 AM, Walter Bright wrote:
 On 7/9/2012 3:27 PM, Jakob Ovrum wrote:
 This opEquals is only logically constant, not bitwise constant, hence
 it must
 not be const; similar to the caching scenario. Just trying to
 demonstrate that
 the issue is bigger than just caching - it's any logically constant
 comparison
 function.
I understand, but the downside of not making these functions const is it will torpedo the use of functional style programming in D.
Making these functions const will torpedo the use of OO style programming in D. OO is all about data encapsulation, aliasing and mutation. Also consider that: - Functional style programs use structs and delegates, not class objects. Classes are an OO abstraction. - The most well-known functional programming language, Haskell, mutates existing state a great deal at runtime, since almost _everything_ is lazily initialized. I have written some Haskell-style D code, and could not use the 'const', 'immutable' or 'pure' qualifiers. - what actually harms functional style programming in D the most is the lack of tail calls. (I have some other gripes, but this is the most important one.) - D is multi-paradigm. A program can be functional style in many ways, and still mutate state. (Functional programming languages have to invent idioms and syntactic sugar to provide an abstraction for mutation. The compiler has to detect those and optimize them into mutation.)
 A straightforward workaround is to use PIMPL to encapsulate the logical
 const stuff, and then cast the reference to const to use inside the
 opEquals.
I didn't get that.
Jul 10 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 3:49 PM, Timon Gehr wrote:
 I understand, but the downside of not making these functions const is it
 will torpedo the use of functional style programming in D.
Making these functions const will torpedo the use of OO style programming in D.
No, it won't, there are simple workarounds.
 OO is all about data encapsulation, aliasing and
 mutation.

 Also consider that:

 - Functional style programs use structs and delegates, not class
    objects. Classes are an OO abstraction.

 - The most well-known functional programming language, Haskell, mutates
    existing state a great deal at runtime, since almost _everything_ is
    lazily initialized.  I have written some Haskell-style D code, and
    could not use the 'const', 'immutable' or 'pure' qualifiers.

 - what actually harms functional style programming in D the most is the
    lack of tail calls. (I have some other gripes, but this is the most
    important one.)

 - D is multi-paradigm. A program can be functional style in many ways,
    and still mutate state. (Functional programming languages have to
    invent idioms and syntactic sugar to provide an abstraction for
    mutation. The compiler has to detect those and optimize them into
    mutation.)

 A straightforward workaround is to use PIMPL to encapsulate the logical
 const stuff, and then cast the reference to const to use inside the
 opEquals.
I didn't get that.
PIMPL means pointer to implementation. Your const struct is just a wrapper around a pointer, and that pointer can be cast to mutable before calling member functions on it. Or: bool opEquals(const Object p) const { Object q = cast() p; Object r = cast() this; return r.non_const_equals(q); } Of course, you'd have to make this trusted.
Jul 10 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 01:10 AM, Walter Bright wrote:
 On 7/10/2012 3:49 PM, Timon Gehr wrote:
 I understand, but the downside of not making these functions const is it
 will torpedo the use of functional style programming in D.
Making these functions const will torpedo the use of OO style programming in D.
No, it won't, there are simple workarounds.
In safe code there are none. There are also simple workarounds for the functional style programming case: just do not annotate.
 OO is all about data encapsulation, aliasing and
 mutation.

 Also consider that:

 - Functional style programs use structs and delegates, not class
 objects. Classes are an OO abstraction.

 - The most well-known functional programming language, Haskell, mutates
 existing state a great deal at runtime, since almost _everything_ is
 lazily initialized. I have written some Haskell-style D code, and
 could not use the 'const', 'immutable' or 'pure' qualifiers.

 - what actually harms functional style programming in D the most is the
 lack of tail calls. (I have some other gripes, but this is the most
 important one.)

 - D is multi-paradigm. A program can be functional style in many ways,
 and still mutate state. (Functional programming languages have to
 invent idioms and syntactic sugar to provide an abstraction for
 mutation. The compiler has to detect those and optimize them into
 mutation.)
I consider those points important.
 A straightforward workaround is to use PIMPL to encapsulate the logical
 const stuff, and then cast the reference to const to use inside the
 opEquals.
I didn't get that.
PIMPL means pointer to implementation. Your const struct is just a wrapper around a pointer, and that pointer can be cast to mutable before calling member functions on it. Or: bool opEquals(const Object p) const { Object q = cast() p; Object r = cast() this; return r.non_const_equals(q); } Of course, you'd have to make this trusted.
It is not safe to do that. Marking it trusted would be a lie. We cannot build a type system on the premise that any OO code will break it and at the same time claim that it provides actual guarantees.
Jul 10 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 4:29 PM, Timon Gehr wrote:
 On 07/11/2012 01:10 AM, Walter Bright wrote:
 On 7/10/2012 3:49 PM, Timon Gehr wrote:
 I understand, but the downside of not making these functions const is it
 will torpedo the use of functional style programming in D.
Making these functions const will torpedo the use of OO style programming in D.
No, it won't, there are simple workarounds.
In safe code there are none.
Right. That's what I meant when you have to add trusted.
 There are also simple workarounds for the functional style programming case:
just do not annotate.
Functional means a guarantee against mutability. Faith-based functional programming is not functional programming.
 I consider those points important.
So do I. But all language design involves tradeoffs. I believe the advantages of const toHash etc. outweigh the disadvantage of less straightforward memoization.
 Of course, you'd have to make this  trusted.
It is not safe to do that. Marking it trusted would be a lie. We cannot build a type system on the premise that any OO code will break it and at the same time claim that it provides actual guarantees.
Consider that in the light of what logical const actually is - it offers no guarantees whatsoever. At least with trusted the code inspector knows that there's something unusual going on there that needs to be manually checked.
Jul 10 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 02:03 AM, Walter Bright wrote:
 On 7/10/2012 4:29 PM, Timon Gehr wrote:
 On 07/11/2012 01:10 AM, Walter Bright wrote:
 On 7/10/2012 3:49 PM, Timon Gehr wrote:
 I understand, but the downside of not making these functions const
 is it
 will torpedo the use of functional style programming in D.
Making these functions const will torpedo the use of OO style programming in D.
No, it won't, there are simple workarounds.
In safe code there are none.
Right. That's what I meant when you have to add trusted.
 There are also simple workarounds for the functional style programming
 case: just do not annotate.
Functional means a guarantee against mutability.
Only guaranteed referential transparency is necessary. Eg, 'fun' in the following code snippet has this property. Even though it mutates global state and calls a function that does not have the property. int x = 2; int fun(){ auto y = x; x = 3; auto r = gun(); x = y; return r; } auto gun(){ return arbitrary_pure_computation(x); }
 Faith-based functional programming is not functional programming.
This program is functional-style: http://pastebin.com/Vx4hXvaT With some template/static assert/information hiding magic, it could be adapted so that any pure-annotated code using the Lazy types for all arguments&returns provides referential transparency guarantees.
 I consider those points important.
So do I. But all language design involves tradeoffs. I believe the advantages of const toHash etc. outweigh the disadvantage of less straightforward memoization.
I use unbounded data structures. Those have to be initialized lazily and therefore the non-constness of their methods invades the entire code base which is written in OO and functional styles (and a good portion of Metaprogramming to remove the boilerplate). Most methods that would be considered const cannot be, because they may trigger extensions of the unbounded data structures somewhere down the road.
 Of course, you'd have to make this  trusted.
It is not safe to do that. Marking it trusted would be a lie. We cannot build a type system on the premise that any OO code will break it and at the same time claim that it provides actual guarantees.
Consider that in the light of what logical const actually is - it offers no guarantees whatsoever.
The issue is that the guarantees need to apply to objects to which they cannot apply if they are forced to implement functions that provide the guarantees.
 At least with  trusted the code inspector
 knows that there's something unusual going on there that needs to be
 manually checked.
There will be non-trivial bugs that the code inspector won't see. Most bugs are caught by testing or trying to formally prove correctness.
Jul 10 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 03:04 AM, Timon Gehr wrote:
 On 07/11/2012 02:03 AM, Walter Bright wrote:
 ...
 So do I. But all language design involves tradeoffs. I believe the
 advantages of const toHash etc. outweigh the disadvantage of less
 straightforward memoization.
I use unbounded data structures. Those have to be initialized lazily and therefore the non-constness of their methods invades the entire code base which is written in OO and functional styles (and a good portion of Metaprogramming to remove the boilerplate). Most methods that would be considered const cannot be, because they may trigger extensions of the unbounded data structures somewhere down the road.
(conceptually, the structures are immutable and infinite.)
Jul 10 2012
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 6:07 PM, Timon Gehr wrote:
 (conceptually, the structures are immutable and infinite.)
I understand, and those are useful constructs. Something has to give somewhere, and I believe that the future lies with provable constructs, not with faith based programming.
Jul 10 2012
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 6:07 PM, Timon Gehr wrote:
 (conceptually, the structures are immutable and infinite.)
What a minute - how are you doing toHash for an infinite structure? The only way you could is by doing toHash on the initial condition for it. And that could be made const. Same for opEquals.
Jul 10 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 04:30 AM, Walter Bright wrote:
 On 7/10/2012 6:07 PM, Timon Gehr wrote:
 (conceptually, the structures are immutable and infinite.)
What a minute - how are you doing toHash for an infinite structure? The only way you could is by doing toHash on the initial condition for it. And that could be made const. Same for opEquals.
I am not. The structure is used to obtain unique representations for values. Other parts of the code query the structure for the representations of values. That query cannot be const because the structure has to remember the representation it gives out if it has never been requested before. Values can be compared for equality by simple reference comparison that way, which speeds up the code tremendously.
Jul 10 2012
parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 7:42 PM, Timon Gehr wrote:
 On 07/11/2012 04:30 AM, Walter Bright wrote:
 On 7/10/2012 6:07 PM, Timon Gehr wrote:
 (conceptually, the structures are immutable and infinite.)
What a minute - how are you doing toHash for an infinite structure? The only way you could is by doing toHash on the initial condition for it. And that could be made const. Same for opEquals.
I am not. The structure is used to obtain unique representations for values. Other parts of the code query the structure for the representations of values. That query cannot be const because the structure has to remember the representation it gives out if it has never been requested before. Values can be compared for equality by simple reference comparison that way, which speeds up the code tremendously.
How about create a new instance of a struct when a new representation is requested?
Jul 10 2012
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 6:04 PM, Timon Gehr wrote:
 Functional means a guarantee against mutability.
Only guaranteed referential transparency is necessary. Eg, 'fun' in the following code snippet has this property. Even though it mutates global state and calls a function that does not have the property. int x = 2; int fun(){ auto y = x; x = 3; auto r = gun(); x = y; return r; } auto gun(){ return arbitrary_pure_computation(x); }
Purity also means not reading mutable global state. Also, it is not practical for a compiler to ensure that x has the same value upon exit as entry if it is being assigned to.
 I use unbounded data structures. Those have to be initialized lazily
 and therefore the non-constness of their methods invades the entire
 code base which is written in OO and functional styles (and a good
 portion of Metaprogramming to remove the boilerplate). Most methods
 that would be considered const cannot be, because they may trigger
 extensions of the unbounded data structures somewhere down the road.
You do have another option - don't use toHash, opEquals, etc., with those structures.
 At least with  trusted the code inspector
 knows that there's something unusual going on there that needs to be
 manually checked.
There will be non-trivial bugs that the code inspector won't see. Most bugs are caught by testing or trying to formally prove correctness.
I cannot reconcile that with a desire for logical constness, which is completely uncheckable. Anyhow, the point of trusted is to notify the maintainer that "here be dragons". Logical const doesn't even go that far - it's purely ornamental.
Jul 10 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 03:14 AM, Walter Bright wrote:
 On 7/10/2012 6:04 PM, Timon Gehr wrote:
 Functional means a guarantee against mutability.
Only guaranteed referential transparency is necessary. Eg, 'fun' in the following code snippet has this property. Even though it mutates global state and calls a function that does not have the property. int x = 2; int fun(){ auto y = x; x = 3; auto r = gun(); x = y; return r; } auto gun(){ return arbitrary_pure_computation(x); }
Purity also means not reading mutable global state. Also, it is not practical for a compiler to ensure that x has the same value upon exit as entry if it is being assigned to.
It could in principle ensure it by providing a language feature that carries out this operation. When the abstraction level gets higher, stuff usually gets much simpler for a verifier.
 I use unbounded data structures. Those have to be initialized lazily
 and therefore the non-constness of their methods invades the entire
 code base which is written in OO and functional styles (and a good
 portion of Metaprogramming to remove the boilerplate). Most methods
 that would be considered const cannot be, because they may trigger
 extensions of the unbounded data structures somewhere down the road.
You do have another option - don't use toHash, opEquals, etc., with those structures.
With the entire code base. The structures are fundamental. What can be const today will call into a method that uses one of those tomorrow. But why should I sprinkle my code with 'const' anyway? Almost none of its classes will actually be instantiated with the immutable qualifier.
 At least with  trusted the code inspector
 knows that there's something unusual going on there that needs to be
 manually checked.
There will be non-trivial bugs that the code inspector won't see. Most bugs are caught by testing or trying to formally prove correctness.
I cannot reconcile that with a desire for logical constness, which is completely uncheckable.
I do not desire logical const as a language feature. But conservative type systems are not good for everything. The root of the class hierarchy needs to be good for everything. Object is not an adequate root any more.
 Anyhow, the point of  trusted is to notify the maintainer that "here be
 dragons". Logical const doesn't even go that far - it's purely ornamental.
I see. I was under the impression that the point of trusted was to be able to subvert the type system without actually subverting its guarantees.
Jul 10 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But conservative
 type systems are not good for everything. The root of the class
 hierarchy needs to be good for everything. Object is not an adequate
 root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
Jul 10 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 04:02 AM, Andrei Alexandrescu wrote:
 On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But conservative
 type systems are not good for everything. The root of the class
 hierarchy needs to be good for everything. Object is not an adequate
 root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
It is promising that they never will in any derived class with no obvious way out for no benefit that I struggle with.
Jul 10 2012
prev sibling next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu 
wrote:
 On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But 
 conservative
 type systems are not good for everything. The root of the class
 hierarchy needs to be good for everything. Object is not an 
 adequate
 root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
It's more likely to go down like this: programmer attempts to write his opEquals (or toString etc) within the restrictions of const, but fails due to the requirements of the implementation (which can easily go beyond simple performance measures like caching, as demonstrated). The programmer then writes his own mutable member function and neglects opEquals altogether. If the programmer is real nice, he/she will write a throwing opEquals stub.
Jul 10 2012
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jul 11, 2012 at 04:59:28AM +0200, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu
 wrote:
On 7/10/12 9:45 PM, Timon Gehr wrote:
I do not desire logical const as a language feature. But
conservative type systems are not good for everything. The root of
the class hierarchy needs to be good for everything. Object is not
an adequate root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
It's more likely to go down like this: programmer attempts to write his opEquals (or toString etc) within the restrictions of const, but fails due to the requirements of the implementation (which can easily go beyond simple performance measures like caching, as demonstrated). The programmer then writes his own mutable member function and neglects opEquals altogether. If the programmer is real nice, he/she will write a throwing opEquals stub.
This is exactly what I was saying. All that beautiful, pristine, perfect infrastructure we're building in druntime eventually just gets sidestepped, because it is unable to cater for what the programmer needs, and so the programmer ends up reimplementing his own infrastructure, over and over again. I can't see how that is beneficial. T -- What doesn't kill me makes me stranger.
Jul 10 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 12:59 AM, H. S. Teoh wrote:
 On Wed, Jul 11, 2012 at 04:59:28AM +0200, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu
 wrote:
 On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But
 conservative type systems are not good for everything. The root of
 the class hierarchy needs to be good for everything. Object is not
 an adequate root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
It's more likely to go down like this: programmer attempts to write his opEquals (or toString etc) within the restrictions of const, but fails due to the requirements of the implementation (which can easily go beyond simple performance measures like caching, as demonstrated). The programmer then writes his own mutable member function and neglects opEquals altogether. If the programmer is real nice, he/she will write a throwing opEquals stub.
This is exactly what I was saying. All that beautiful, pristine, perfect infrastructure we're building in druntime eventually just gets sidestepped, because it is unable to cater for what the programmer needs, and so the programmer ends up reimplementing his own infrastructure, over and over again. I can't see how that is beneficial.
How often do you need memoization? It's not even recognized by this mailer's editor. Andrei
Jul 11 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 02:40 PM, Andrei Alexandrescu wrote:
 On 7/11/12 12:59 AM, H. S. Teoh wrote:
 On Wed, Jul 11, 2012 at 04:59:28AM +0200, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu
 wrote:
 On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But
 conservative type systems are not good for everything. The root of
 the class hierarchy needs to be good for everything. Object is not
 an adequate root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
It's more likely to go down like this: programmer attempts to write his opEquals (or toString etc) within the restrictions of const, but fails due to the requirements of the implementation (which can easily go beyond simple performance measures like caching, as demonstrated). The programmer then writes his own mutable member function and neglects opEquals altogether. If the programmer is real nice, he/she will write a throwing opEquals stub.
This is exactly what I was saying. All that beautiful, pristine, perfect infrastructure we're building in druntime eventually just gets sidestepped, because it is unable to cater for what the programmer needs, and so the programmer ends up reimplementing his own infrastructure, over and over again. I can't see how that is beneficial.
How often do you need memoization?
Once is sufficient. It will invade the code base because const is transitive.
 It's not even recognized by this mailer's editor.

 Andrei
'lazy computation' is.
Jul 11 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 10:59 PM, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote:
 On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But conservative
 type systems are not good for everything. The root of the class
 hierarchy needs to be good for everything. Object is not an adequate
 root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
It's more likely to go down like this: programmer attempts to write his opEquals (or toString etc) within the restrictions of const, but fails due to the requirements of the implementation (which can easily go beyond simple performance measures like caching, as demonstrated). The programmer then writes his own mutable member function and neglects opEquals altogether. If the programmer is real nice, he/she will write a throwing opEquals stub.
I gave evidence on a large, high quality C++ codebase that the use of mutable (which is the solution of choice for memoization, caching, and lazy computation) is extremely scarce. What evidence do you have for your prediction? Andrei
Jul 11 2012
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 02:39 PM, Andrei Alexandrescu wrote:
 On 7/10/12 10:59 PM, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote:
 On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But conservative
 type systems are not good for everything. The root of the class
 hierarchy needs to be good for everything. Object is not an adequate
 root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target? Andrei
It's more likely to go down like this: programmer attempts to write his opEquals (or toString etc) within the restrictions of const, but fails due to the requirements of the implementation (which can easily go beyond simple performance measures like caching, as demonstrated). The programmer then writes his own mutable member function and neglects opEquals altogether. If the programmer is real nice, he/she will write a throwing opEquals stub.
I gave evidence on a large, high quality C++ codebase that the use of mutable (which is the solution of choice for memoization, caching, and lazy computation) is extremely scarce.
Unlike in D, in C++ programmers have a choice of declaring a method const or not const. Also, what percentage of the usages of 'const' would disappear, without changing the code in any other way, if - const was transitive? - all usages of mutable disappeared? - the social graph was infinite? - any combination of the above? The data given so far is not evidence relevant to this discussion. (it is not evidence in the traditional sense anyway, because statements about a closed source code base cannot be validated by a third party.)
 What evidence do you have for your prediction?
Attempts to impose restrictions without conceivable benefit always go wrong at some point. I'd claim this to be general knowledge. Almost nobody has responded to the RawObject proposal. Jacob Carlborg has brought my attention to this: http://www.ruby-doc.org/core-1.9.3/BasicObject.html I still think that would be the best solution.
Jul 11 2012
prev sibling parent reply "Max Samukha" <maxsamukha gmail.com> writes:
On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei Alexandrescu 
wrote:

 I gave evidence on a large, high quality C++ codebase that the 
 use of mutable (which is the solution of choice for 
 memoization, caching, and lazy computation) is extremely scarce.

 What evidence do you have for your prediction?


 Andrei
Qt codebase (large, high quality) has around 1500 'mutable' keyword occurrences. The actual number of mutable declarations must be lower, but they are quite common. 'Extremely scarce' obviously does not apply there. Also, the ratio of occurrences of mutable vs const keywords is not a good metric for estimating logical const prevalence.
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 11:11 AM, Max Samukha wrote:
 On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei Alexandrescu wrote:

 I gave evidence on a large, high quality C++ codebase that the use of
 mutable (which is the solution of choice for memoization, caching, and
 lazy computation) is extremely scarce.

 What evidence do you have for your prediction?


 Andrei
Qt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
How many lines total? Andrei
Jul 11 2012
next sibling parent reply "Max Samukha" <maxsamukha gmail.com> writes:
On Wednesday, 11 July 2012 at 15:28:41 UTC, Andrei Alexandrescu 
wrote:
 On 7/11/12 11:11 AM, Max Samukha wrote:
 On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei 
 Alexandrescu wrote:

 I gave evidence on a large, high quality C++ codebase that 
 the use of
 mutable (which is the solution of choice for memoization, 
 caching, and
 lazy computation) is extremely scarce.

 What evidence do you have for your prediction?


 Andrei
Qt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
How many lines total? Andrei
1512. Obtained by grepping the src directory of Qt 4.8.0, so the number includes duplicates, comments, etc. I think that even if the actual number is lower by an order, some 100 types using mutable still do not qualify for 'extremely scarce'. If I have time (which is unlikely), I will probably analyse the codebase for the actual number.
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 11:50 AM, Max Samukha wrote:
 On Wednesday, 11 July 2012 at 15:28:41 UTC, Andrei Alexandrescu wrote:
 On 7/11/12 11:11 AM, Max Samukha wrote:
 On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei Alexandrescu wrote:

 I gave evidence on a large, high quality C++ codebase that the use of
 mutable (which is the solution of choice for memoization, caching, and
 lazy computation) is extremely scarce.

 What evidence do you have for your prediction?


 Andrei
Qt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
How many lines total? Andrei
1512. Obtained by grepping the src directory of Qt 4.8.0, so the number includes duplicates, comments, etc. I think that even if the actual number is lower by an order, some 100 types using mutable still do not qualify for 'extremely scarce'. If I have time (which is unlikely), I will probably analyse the codebase for the actual number.
I was asking about total number of C++ code lines. In our codebase we have one use of "mutable" per 7659 lines of C++ code (as grep and wc count). Andrei
Jul 11 2012
parent "Max Samukha" <maxsamukha gmail.com> writes:
On Wednesday, 11 July 2012 at 15:57:51 UTC, Andrei Alexandrescu 
wrote:
 On 7/11/12 11:50 AM, Max Samukha wrote:
 On Wednesday, 11 July 2012 at 15:28:41 UTC, Andrei 
 Alexandrescu wrote:
 On 7/11/12 11:11 AM, Max Samukha wrote:
 On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei 
 Alexandrescu wrote:

 I gave evidence on a large, high quality C++ codebase that 
 the use of
 mutable (which is the solution of choice for memoization, 
 caching, and
 lazy computation) is extremely scarce.

 What evidence do you have for your prediction?


 Andrei
Qt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
How many lines total? Andrei
1512. Obtained by grepping the src directory of Qt 4.8.0, so the number includes duplicates, comments, etc. I think that even if the actual number is lower by an order, some 100 types using mutable still do not qualify for 'extremely scarce'. If I have time (which is unlikely), I will probably analyse the codebase for the actual number.
I was asking about total number of C++ code lines. In our codebase we have one use of "mutable" per 7659 lines of C++ code (as grep and wc count). Andrei
Sorry, I misunderstood the question. As I said earlier, the relative number of lines with 'mutable' is mostly irrelevant. What is more relevant is the ratio of types defining at least one mutable member to the total number of types.
Jul 11 2012
prev sibling parent reply Caligo <iteronvexor gmail.com> writes:
About 2 million in C++.

source:
http://www.ohloh.net/p/qt/analyses/latest/languages_summary


On Wed, Jul 11, 2012 at 10:28 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 How many lines total?

 Andrei
Jul 11 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 11:33 AM, Caligo wrote:
 About 2 million in C++.

 source:
 http://www.ohloh.net/p/qt/analyses/latest/languages_summary
I agree that 1500 occurrences of "mutable" in 2 million lines of C++ is quite large. Andrei
Jul 11 2012
prev sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu 
wrote:
 On 7/10/12 9:45 PM, Timon Gehr wrote:
 I do not desire logical const as a language feature. But 
 conservative type systems are not good for everything. The 
 root of the class hierarchy needs to be good for everything. 
 Object is not an adequate root any more.
How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target?
I remember offering a suggested structure for cached hashing for const/immutable data (mostly a test/example)... If you follow a similar setup you could get the 'logical const' while following the language rules; Namely the mutable state is separate from the const state. Course it's sorta a wrapper, but that's better than breaking the type system or trying to do something else equally questionable. It went something like... struct CachedHash(T) { T value; uint hash; alias value this; uint toHash() { if (!hash) hash = value.toHash(); return hash; } }
Jul 11 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 9:14 PM, Walter Bright wrote:
 Anyhow, the point of  trusted is to notify the maintainer that "here be
 dragons".
I think that's not representing trusted quite accurately. There's no dragon there. trusted means "the code is correct but not mechanically checkable". Casting away const is NOT correct. Andrei
Jul 10 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 6:53 PM, Andrei Alexandrescu wrote:
 On 7/10/12 9:14 PM, Walter Bright wrote:
 Anyhow, the point of  trusted is to notify the maintainer that "here be
 dragons".
I think that's not representing trusted quite accurately. There's no dragon there. trusted means "the code is correct but not mechanically checkable". Casting away const is NOT correct.
You're right in that it would break immutable args passed. PIMPL is a better option.
Jul 10 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 10:22 PM, Walter Bright wrote:
 On 7/10/2012 6:53 PM, Andrei Alexandrescu wrote:
 On 7/10/12 9:14 PM, Walter Bright wrote:
 Anyhow, the point of  trusted is to notify the maintainer that "here be
 dragons".
I think that's not representing trusted quite accurately. There's no dragon there. trusted means "the code is correct but not mechanically checkable". Casting away const is NOT correct.
You're right in that it would break immutable args passed. PIMPL is a better option.
I am pretty sure your approach with PIMPL is also undefined, so we may as well stop suggesting it as a viable possibility. Consider: immutable(T) t1 = create(); int x = t1.x; const(T) t2 = t2; t2.method(); assert(x == t1.x); // the compiler should be able to assume this For the entire immutable thing to work, the compiler must have an iron-clad guarantee that transitively-accessed data members in t1 will preserve before and after the call to t2.method(). Your proposed cast, if ever defined, essentially breaks that guarantee. If that is down, the entire notion of immutable breaks down. Please do not propose pimpl and cast anymore. This is important. Thanks. Andrei
Jul 11 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 8:03 PM, Walter Bright wrote:
 On 7/10/2012 4:29 PM, Timon Gehr wrote:
 On 07/11/2012 01:10 AM, Walter Bright wrote:
 On 7/10/2012 3:49 PM, Timon Gehr wrote:
 I understand, but the downside of not making these functions const
 is it
 will torpedo the use of functional style programming in D.
Making these functions const will torpedo the use of OO style programming in D.
No, it won't, there are simple workarounds.
In safe code there are none.
Right. That's what I meant when you have to add trusted.
That's undefined, not trusted. Andrei
Jul 10 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-07-11 02:03, Walter Bright wrote:

 So do I. But all language design involves tradeoffs. I believe the
 advantages of const toHash etc. outweigh the disadvantage of less
 straightforward memoization.
Can't there be non-const versions of these methods as well? -- /Jacob Carlborg
Jul 11 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 7:10 PM, Walter Bright wrote:
 PIMPL means pointer to implementation. Your const struct is just a
 wrapper around a pointer, and that pointer can be cast to mutable before
 calling member functions on it.

 Or:

 bool opEquals(const Object p) const
 {
 Object q = cast() p;
 Object r = cast() this;
 return r.non_const_equals(q);
 }

 Of course, you'd have to make this  trusted.
s/make this trusted/never use this/ Andrei
Jul 10 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 6:05 PM, Walter Bright wrote:
 A straightforward workaround is to use PIMPL to encapsulate the logical
 const stuff, and then cast the reference to const to use inside the
 opEquals.
s/straightforward/awful/ Andrei
Jul 10 2012
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 01:19:16 UTC, Andrei Alexandrescu 
wrote:
 On 7/10/12 6:05 PM, Walter Bright wrote:
 A straightforward workaround is to use PIMPL to encapsulate 
 the logical
 const stuff, and then cast the reference to const to use 
 inside the
 opEquals.
s/straightforward/awful/ Andrei
Honestly, I think the ideal would be to give people the alternative of having a mutable opEquals etc. These methods only need to be logically constant (of course, they don't *need* to be even that, to the joy of operator overloading abusers worldwide), which means no help from the compiler, as it should be - just plain mutable, with the programmer providing the guarantee. There was work in this direction but I understand it was ripe with issues of its own, however I don't understand how any other path could even be considered when it's just moving from one end of the scale to the other. Yes, Object *needs* to work with const, this is imperative. But does it need to compromise on implementations relying on mutability? I was hoping the answer was "no". Obviously such an override would not work with const references, but some classes really don't lean themselves to immutability at all, alleviating the need for const. I can also imagine that if both were allowed, someone would leverage the ability to use caching in the mutable overload, then falling back to eager computation in the const overload.
Jul 10 2012
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, July 11, 2012 03:30:43 Jakob Ovrum wrote:
 Honestly, I think the ideal would be to give people the
 alternative of having a mutable opEquals etc. These methods only
 need to be logically constant (of course, they don't *need* to be
 even that, to the joy of operator overloading abusers worldwide),
 which means no help from the compiler, as it should be - just
 plain mutable, with the programmer providing the guarantee.
 
 There was work in this direction but I understand it was ripe
 with issues of its own, however I don't understand how any other
 path could even be considered when it's just moving from one end
 of the scale to the other.
 
 Yes, Object *needs* to work with const, this is imperative. But
 does it need to compromise on implementations relying on
 mutability? I was hoping the answer was "no". Obviously such an
 override would not work with const references, but some classes
 really don't lean themselves to immutability at all, alleviating
 the need for const. I can also imagine that if both were allowed,
 someone would leverage the ability to use caching in the mutable
 overload, then falling back to eager computation in the const
 overload.
Yeah. It seems to me that a reasonable approach would be to give Object two versions of opEquals, opCmp, toString, and toHash - a const and non-const version of each. The non-const version then calls the const version. So, the normal thing to do is have your class work with const and override the const version. If the non-const version on Object gets called, then the const version in the derived class gets called, and if the derived type is used directly, then it'll always use the const version, because that's what the derived type has. On the other hand, for a class which doesn't work with const, it does this: 1. Override the non-const versions of opEquals, opCmp, toString, and toHash, giving them the implementations which you want. 2. Override the const versions of opEquals, opCmp, toString, and toHash and make their bodies assert(0). That way, those 4 functions can be used normally as long as the object isn't assigned to a const reference, and if it is, then you get an Error. So, as long as the programmer completely avoids const with that class, it works just fine. And as long as the druntime functions which use those 4 functions do not require const (which is easy to do with either templates or inout), it should be perfectly possible to use such a class without it ever being const or ever needing to be const. In addition, these may be possible (untested, so I'm not 100%) sure to make it harder to construct a const or immutable instance of the derived class: 1. disable const and immutable versions of the class' constructors. 2. Create an alias this to an disabled function which does an implicit conversion to const (probably won't work, but we could also probably make it possible if it doesn't currently work). 3. Create opCasts to const and immutable which are disabled. If all of those work, then the only way to get a const reference to the derived class is if it's used with a base class reference (which would probably only be Object given what the derived class is doing). So, it then takes a bit of work to be able to have and use an object which cannot be const, but it's very doable, and a concise set of instructions on dlang.org would make it easy and straightforward to know what to do to create a class which can't be const. Maybe I'm missing something, but I don't see any reason why this can't work. In addition, I see no reason to require that opEquals, opCmp, toString, or toHash be const, pure, safe, _or_ nothrow for structs. As long as the druntime functions which use those functions are appropriately templated (which is pretty much required for them to work with structs, since they don't all derive from a single type), the constness, pureness, safeness, and nothrowness of those functions in druntime can depend on whether the struct being used with them has those functions as const, pure, safe, and/or nothrow. So, with that, we can have const work wonderfully without requiring it, even if it does take a bit of work to get around it with classes. So, what am I missing here? Why doesn't this work? Or has Walter just not properly considered this option? - Jonathan M Davis
Jul 10 2012
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 02:33:53 UTC, Jonathan M Davis 
wrote:
 -snip-

 So, with that, we can have const work wonderfully without 
 requiring it, even
 if it does take a bit of work to get around it with classes. 
 So, what am I
 missing here? Why doesn't this work? Or has Walter just not 
 properly
 considered this option?

 - Jonathan M Davis
This is exactly the kind of balance I am hoping we can implement. I think Hara Kenji suggested something very similar at one point, but it had some kind of problem with it. It would be awesome if Kenji could provide some insight on that. I think his proposal is somewhere on Github, I'll have a look.
Jul 10 2012
parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 02:55:33 UTC, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 02:33:53 UTC, Jonathan M Davis 
 wrote:
 -snip-

 So, with that, we can have const work wonderfully without 
 requiring it, even
 if it does take a bit of work to get around it with classes. 
 So, what am I
 missing here? Why doesn't this work? Or has Walter just not 
 properly
 considered this option?

 - Jonathan M Davis
This is exactly the kind of balance I am hoping we can implement. I think Hara Kenji suggested something very similar at one point, but it had some kind of problem with it. It would be awesome if Kenji could provide some insight on that. I think his proposal is somewhere on Github, I'll have a look.
The relevant discussion containing the proposal is here: https://github.com/D-Programming-Language/phobos/pull/262 I was wrong; I don't see anyone having found any flaws with the final proposal. And while the proposal wasn't explicitly accepted nor rejected, it doesn't look like it found its way into the final pull request: https://github.com/D-Programming-Language/druntime/pull/72
Jul 10 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 10:33 PM, Jonathan M Davis wrote:
 Yeah. It seems to me that a reasonable approach would be to give Object two
 versions of opEquals, opCmp, toString, and toHash - a const and non-const
 version of each. The non-const version then calls the const version. So, the
 normal thing to do is have your class work with const and override the const
 version. If the non-const version on Object gets called, then the const
 version in the derived class gets called, and if the derived type is used
 directly, then it'll always use the const version, because that's what the
 derived type has.

 On the other hand, for a class which doesn't work with const, it does this:

 1. Override the non-const versions of opEquals, opCmp, toString, and toHash,
 giving them the implementations which you want.

 2. Override the const versions of opEquals, opCmp, toString, and toHash and
 make their bodies assert(0).

 That way, those 4 functions can be used normally as long as the object isn't
 assigned to a const reference, and if it is, then you get an Error.
I was a long-time proponent of this. It's less exciting than it may seem actually. (a) Classes that work with const just fine incur one extra virtual call. I think this can be avoided by having the compiler plant the same pointer for the const and non-const version in the vtable. (b) Classes that can't do as little as one of these four operations without mutating the object are completely excluded from the immutability system, even if they'd otherwise benefit from it. Even those that don't "care" they need to actively _work_ on not caring, which doesn't sit well. So I don't see this as a viable solution to people who are fine with const, but would like to use e.g. some lazy computation. Andrei
Jul 11 2012
parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu 
wrote:
 I was a long-time proponent of this. It's less exciting than it 
 may seem actually.

 (a) Classes that work with const just fine incur one extra 
 virtual call. I think this can be avoided by having the 
 compiler plant the same pointer for the const and non-const 
 version in the vtable.

 (b) Classes that can't do as little as one of these four 
 operations without mutating the object are completely excluded 
 from the immutability system, even if they'd otherwise benefit 
 from it. Even those that don't "care" they need to actively 
 _work_ on not caring, which doesn't sit well.

 So I don't see this as a viable solution to people who are fine 
 with const, but would like to use e.g. some lazy computation.


 Andrei
This solution is not for allowing people to use lazy computation in their const overrides, it's for allowing people to still use opEquals, toString etc. even if their implementations cannot and should not be const. e.g. the LuaD function I posted earlier - it has nothing to do with caching or lazy computation, it's just that it's only logically constant and cannot ever be bitwise constant due to the underlying API. Immutable instances of such structures are next to useless, as every member function except for a single getter function uses mutation.
Jul 11 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 17:03, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu wrote:
 I was a long-time proponent of this. It's less exciting than it may
 seem actually.

 (a) Classes that work with const just fine incur one extra virtual
 call. I think this can be avoided by having the compiler plant the
 same pointer for the const and non-const version in the vtable.

 (b) Classes that can't do as little as one of these four operations
 without mutating the object are completely excluded from the
 immutability system, even if they'd otherwise benefit from it. Even
 those that don't "care" they need to actively _work_ on not caring,
 which doesn't sit well.

 So I don't see this as a viable solution to people who are fine with
 const, but would like to use e.g. some lazy computation.


 Andrei
This solution is not for allowing people to use lazy computation in their const overrides, it's for allowing people to still use opEquals, toString etc. even if their implementations cannot and should not be const.
In this case, they have function that does something else than compare test for equality, etc . . . The overload make no sense here in the first place, and the fact that const break such thing isn't a problem. This is the other way around, the fact that const break such a practice is good.
Jul 11 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 05:27 PM, deadalnix wrote:
 On 11/07/2012 17:03, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu wrote:
 I was a long-time proponent of this. It's less exciting than it may
 seem actually.

 (a) Classes that work with const just fine incur one extra virtual
 call. I think this can be avoided by having the compiler plant the
 same pointer for the const and non-const version in the vtable.

 (b) Classes that can't do as little as one of these four operations
 without mutating the object are completely excluded from the
 immutability system, even if they'd otherwise benefit from it. Even
 those that don't "care" they need to actively _work_ on not caring,
 which doesn't sit well.

 So I don't see this as a viable solution to people who are fine with
 const, but would like to use e.g. some lazy computation.


 Andrei
This solution is not for allowing people to use lazy computation in their const overrides, it's for allowing people to still use opEquals, toString etc. even if their implementations cannot and should not be const.
In this case, they have function that does something else than compare test for equality, etc . . . The overload make no sense here in the first place, and the fact that const break such thing isn't a problem. This is the other way around, the fact that const break such a practice is good.
I think this posts roots in a misunderstanding of 'cannot and should not be const'.
Jul 11 2012
parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 17:49, Timon Gehr wrote:
 On 07/11/2012 05:27 PM, deadalnix wrote:
 On 11/07/2012 17:03, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu wrote:
 I was a long-time proponent of this. It's less exciting than it may
 seem actually.

 (a) Classes that work with const just fine incur one extra virtual
 call. I think this can be avoided by having the compiler plant the
 same pointer for the const and non-const version in the vtable.

 (b) Classes that can't do as little as one of these four operations
 without mutating the object are completely excluded from the
 immutability system, even if they'd otherwise benefit from it. Even
 those that don't "care" they need to actively _work_ on not caring,
 which doesn't sit well.

 So I don't see this as a viable solution to people who are fine with
 const, but would like to use e.g. some lazy computation.


 Andrei
This solution is not for allowing people to use lazy computation in their const overrides, it's for allowing people to still use opEquals, toString etc. even if their implementations cannot and should not be const.
In this case, they have function that does something else than compare test for equality, etc . . . The overload make no sense here in the first place, and the fact that const break such thing isn't a problem. This is the other way around, the fact that const break such a practice is good.
I think this posts roots in a misunderstanding of 'cannot and should not be const'.
If it cannot and should not be const, it isn't a comparison or an equality test, it is another operation altogether that is performed.
Jul 11 2012
parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 15:59:21 UTC, deadalnix wrote:
 If it cannot and should not be const, it isn't a comparison or 
 an equality test, it is another operation altogether that is 
 performed.
That's bullshit. You've seen the example I raised. How can you tell me it's not an equality test?
Jul 11 2012
parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 19:32, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 15:59:21 UTC, deadalnix wrote:
 If it cannot and should not be const, it isn't a comparison or an
 equality test, it is another operation altogether that is performed.
That's bullshit. You've seen the example I raised. How can you tell me it's not an equality test?
To be fair, I know nothing about LUA. But you are talking here about discussing with another language, so, by definition, don't have the same semantic than D. You'll have to do unsafe things at jointures point in such case, I seems obvious to me.
Jul 11 2012
parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 17:47:41 UTC, deadalnix wrote:
 To be fair, I know nothing about LUA.
It's "Lua".
 But you are talking here about discussing with another 
 language, so, by definition, don't have the same semantic than 
 D.
This is irrelevant. The Lua API is a C library like any other. Feel free to imagine that the example is one of a class mapping to data in a database, and the equality test includes accessing the database.
 You'll have to do unsafe things at jointures point in such 
 case, I seems obvious to me.
No, I don't have to do this, your assertion is baseless. The LuaD library is fairly complete (though there's still a lot of room for higher-level features), and the example posted works fine and will work fine for the time being because the aggregate is a struct, not a class, and will remain so. I don't have to subvert the type system in that module at all.
Jul 11 2012
parent deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 19:58, Jakob Ovrum wrote:
 No, I don't have to do this, your assertion is baseless. The LuaD
 library is fairly complete (though there's still a lot of room for
 higher-level features), and the example posted works fine and will work
 fine for the time being because the aggregate is a struct, not a class,
 and will remain so. I don't have to subvert the type system in that
 module at all.
I don't say you have to. I say, you may need to. Because the language you are interfacing with have different semantics.
Jul 11 2012
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jul 11, 2012 at 05:27:01PM +0200, deadalnix wrote:
 On 11/07/2012 17:03, Jakob Ovrum wrote:
[...]
This solution is not for allowing people to use lazy computation in
their const overrides, it's for allowing people to still use
opEquals, toString etc. even if their implementations cannot and
should not be const.
In this case, they have function that does something else than compare test for equality, etc . . . The overload make no sense here in the first place, and the fact that const break such thing isn't a problem. This is the other way around, the fact that const break such a practice is good.
It *is* a problem when you're talking about abstractions. If I have million node binary trees and I'm testing for equality, I'd like to be able to cache the results. But being forced to use const means I can't cache anything. And this isn't just about caching; if my tree is partially stored in the database, and I have a DB connection object in my tree class, then I can't use opEquals because I can't modify the DB state (which is impractical if I have to actually use it to make DB queries -- the DB engine may have to cache DB pages, etc.). Any abstraction of opEquals beyond the bitwise level cannot be implemented. In other words, druntime's opEquals is useless in this case. Since const is viral, as soon as one class deep in your class hierarchy needs non-const, your entire class hierarchy is out of luck. Users will have to sidestep the issue by implementing their own version of equality comparison. Which means they have to implement their own version of AA's, because built-in AA's use opEquals, and they have to implement their own version of array comparisons for the same reason, etc.. They will essentially have to rebuild the entire infrastructure provided in druntime. So every user and every library will have their own infrastructure for non-const comparisons, and it will be a major nightmare trying to interoperate between them. All because the single standard, druntime, insists that comparisons must be bitwise const. Sure, it's a good thing, from a theoretical ivory-tower point of view. It's also impractical for non-trivial applications. T -- Many open minds should be closed for repairs. -- K5 user
Jul 11 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 12:30 PM, H. S. Teoh wrote:
 It *is* a problem when you're talking about abstractions. If I have
 million node binary trees and I'm testing for equality, I'd like to be
 able to cache the results. But being forced to use const means I can't
 cache anything. And this isn't just about caching; if my tree is
 partially stored in the database, and I have a DB connection object in
 my tree class, then I can't use opEquals because I can't modify the DB
 state (which is impractical if I have to actually use it to make DB
 queries -- the DB engine may have to cache DB pages, etc.). Any
 abstraction of opEquals beyond the bitwise level cannot be implemented.
How about the static (h|c)ache?? Andrei
Jul 11 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 12:30 PM, H. S. Teoh wrote:
 It *is* a problem when you're talking about abstractions. If I have
 million node binary trees and I'm testing for equality, I'd like to be
 able to cache the results. But being forced to use const means I can't
 cache anything. And this isn't just about caching; if my tree is
 partially stored in the database, and I have a DB connection object in
 my tree class, then I can't use opEquals because I can't modify the DB
 state (which is impractical if I have to actually use it to make DB
 queries -- the DB engine may have to cache DB pages, etc.). Any
 abstraction of opEquals beyond the bitwise level cannot be implemented.
How about the static hash/cache? Andrei
Jul 11 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:
 On 7/11/12 12:30 PM, H. S. Teoh wrote:
 It *is* a problem when you're talking about abstractions. If I have
 million node binary trees and I'm testing for equality, I'd like to be
 able to cache the results. But being forced to use const means I can't
 cache anything. And this isn't just about caching; if my tree is
 partially stored in the database, and I have a DB connection object in
 my tree class, then I can't use opEquals because I can't modify the DB
 state (which is impractical if I have to actually use it to make DB
 queries -- the DB engine may have to cache DB pages, etc.). Any
 abstraction of opEquals beyond the bitwise level cannot be implemented.
How about the static hash/cache? Andrei
Will break horribly as soon as it is discovered that the methods should be pure as well.
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 1:04 PM, Timon Gehr wrote:
 On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:
 On 7/11/12 12:30 PM, H. S. Teoh wrote:
 It *is* a problem when you're talking about abstractions. If I have
 million node binary trees and I'm testing for equality, I'd like to be
 able to cache the results. But being forced to use const means I can't
 cache anything. And this isn't just about caching; if my tree is
 partially stored in the database, and I have a DB connection object in
 my tree class, then I can't use opEquals because I can't modify the DB
 state (which is impractical if I have to actually use it to make DB
 queries -- the DB engine may have to cache DB pages, etc.). Any
 abstraction of opEquals beyond the bitwise level cannot be implemented.
How about the static hash/cache? Andrei
Will break horribly as soon as it is discovered that the methods should be pure as well.
I don't think they should be pure. Do you have reasons to think otherwise? Andrei
Jul 11 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 19:46, Andrei Alexandrescu wrote:
 On 7/11/12 1:04 PM, Timon Gehr wrote:
 On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:
 On 7/11/12 12:30 PM, H. S. Teoh wrote:
 It *is* a problem when you're talking about abstractions. If I have
 million node binary trees and I'm testing for equality, I'd like to be
 able to cache the results. But being forced to use const means I can't
 cache anything. And this isn't just about caching; if my tree is
 partially stored in the database, and I have a DB connection object in
 my tree class, then I can't use opEquals because I can't modify the DB
 state (which is impractical if I have to actually use it to make DB
 queries -- the DB engine may have to cache DB pages, etc.). Any
 abstraction of opEquals beyond the bitwise level cannot be implemented.
How about the static hash/cache? Andrei
Will break horribly as soon as it is discovered that the methods should be pure as well.
I don't think they should be pure. Do you have reasons to think otherwise? Andrei
I think they should. Comparing the same object 2 time should definitively have the same result back, otherwise things will go horribly wrong soon enough.
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 1:49 PM, deadalnix wrote:
 I think they should. Comparing the same object 2 time should
 definitively have the same result back, otherwise things will go
 horribly wrong soon enough.
Yah, but e.g. a comparison may log something. Andrei
Jul 11 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jul 11, 2012 at 01:50:23PM -0400, Andrei Alexandrescu wrote:
 On 7/11/12 1:49 PM, deadalnix wrote:
I think they should. Comparing the same object 2 time should
definitively have the same result back, otherwise things will go
horribly wrong soon enough.
Yah, but e.g. a comparison may log something.
[...] I think it's a given that purity is out the window once debugging is involved. (I assume comparison logging is only for debugging, otherwise you must have a very very strange use for comparison operators.) T -- "Holy war is an oxymoron." -- Lazarus Long
Jul 11 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 2:05 PM, H. S. Teoh wrote:
 On Wed, Jul 11, 2012 at 01:50:23PM -0400, Andrei Alexandrescu wrote:
 On 7/11/12 1:49 PM, deadalnix wrote:
 I think they should. Comparing the same object 2 time should
 definitively have the same result back, otherwise things will go
 horribly wrong soon enough.
Yah, but e.g. a comparison may log something.
[...] I think it's a given that purity is out the window once debugging is involved.
Logging is not debugging. Andrei
Jul 11 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 07:46 PM, Andrei Alexandrescu wrote:
 On 7/11/12 1:04 PM, Timon Gehr wrote:
 On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:
 On 7/11/12 12:30 PM, H. S. Teoh wrote:
 It *is* a problem when you're talking about abstractions. If I have
 million node binary trees and I'm testing for equality, I'd like to be
 able to cache the results. But being forced to use const means I can't
 cache anything. And this isn't just about caching; if my tree is
 partially stored in the database, and I have a DB connection object in
 my tree class, then I can't use opEquals because I can't modify the DB
 state (which is impractical if I have to actually use it to make DB
 queries -- the DB engine may have to cache DB pages, etc.). Any
 abstraction of opEquals beyond the bitwise level cannot be implemented.
How about the static hash/cache? Andrei
Will break horribly as soon as it is discovered that the methods should be pure as well.
I don't think they should be pure. Do you have reasons to think otherwise? Andrei
I think they should be pure as much as I think they should be const. The reasoning is analogous for both. We have immutable class instances that want to provide the methods => make them const. We have pure functions that want to provide the methods => make them pure. This reasoning is not taking into account the whole picture because We have implementations of those methods that want to use non-const methods in their implementation. (eg. database connection) We have implementation of those methods that want to use non-pure methods in their implementation (eg. database connection, logging)
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 2:03 PM, Timon Gehr wrote:
 I think they should be pure as much as I think they should be const.

 The reasoning is analogous for both.

 We have immutable class instances that want to provide the methods =>
 make them const.

 We have pure functions that want to provide the methods => make them pure.

 This reasoning is not taking into account the whole picture because

 We have implementations of those methods that want to use non-const
 methods in their implementation. (eg. database connection)

 We have implementation of those methods that want to use non-pure
 methods in their implementation (eg. database connection, logging)
The essential difference is there's no "pure" object, so there's no motivation to plant "pure" on its methods. Andrei
Jul 11 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 08:17 PM, Andrei Alexandrescu wrote:
 On 7/11/12 2:03 PM, Timon Gehr wrote:
 I think they should be pure as much as I think they should be const.

 The reasoning is analogous for both.

 We have immutable class instances that want to provide the methods =>
 make them const.

 We have pure functions that want to provide the methods => make them
 pure.
oops. I meant to write "We have pure functions that want to use the methods."
 This reasoning is not taking into account the whole picture because

 We have implementations of those methods that want to use non-const
 methods in their implementation. (eg. database connection)

 We have implementation of those methods that want to use non-pure
 methods in their implementation (eg. database connection, logging)
The essential difference is there's no "pure" object, so there's no motivation to plant "pure" on its methods. Andrei
auto foo(Object o)pure{ // o is a "pure" object here }
Jul 11 2012
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:
 I don't think they should be pure. Do you have reasons to think otherwise?
As I understand it, Walter's current plan is to require that opEquals, opCmp, toString, and toHash be safe const pure nothrow - for both classes and structs. - Jonathan M Davis
Jul 11 2012
parent reply travert phare.normalesup.org (Christophe Travert) writes:
"Jonathan M Davis" , dans le message (digitalmars.D:172005), a écrit :
 On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:
 I don't think they should be pure. Do you have reasons to think otherwise?
As I understand it, Walter's current plan is to require that opEquals, opCmp, toString, and toHash be safe const pure nothrow - for both classes and structs.
And is the plan add each tag one by one, breaking codes in many places each time ?
Jul 12 2012
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, July 12, 2012 07:31:02 Christophe Travert wrote:
 "Jonathan M Davis" , dans le message (digitalmars.D:172005), a =C3=A9=
crit :
 On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:
 I don't think they should be pure. Do you have reasons to think
 otherwise?
=20 As I understand it, Walter's current plan is to require that opEqua=
ls,
 opCmp, toString, and toHash be  safe const pure nothrow - for both
 classes and structs.
=20 And is the plan add each tag one by one, breaking codes in many place=
s
 each time ?
I don't know. Implementation issues have prevented it from happening ye= t. I=20 believe that some additional requirements were already added (e.g. toHa= sh on=20 classes now requires safe IIRC), but for the most part, it's simply pl= anned,=20 and I don't know that it's been fully sorted out how it's going to be r= olled=20 out. However, based on Andrei's new thread, "All right, all right! Interim d= ecision=20 regarding qualified Object methods," it looks like this thread convince= d Andrei=20 and Walter to completely revisit how all of this works - including whet= her=20 Object even needs these functions, thanks to your post on that. So, the= whole=20 rollout of making those functions have to be safe const pure nothrow h= as now=20 presumably been tabled if not outright scrapped. - Jonathan M Davis
Jul 12 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 18:30, H. S. Teoh wrote:
 On Wed, Jul 11, 2012 at 05:27:01PM +0200, deadalnix wrote:
 On 11/07/2012 17:03, Jakob Ovrum wrote:
[...]
 This solution is not for allowing people to use lazy computation in
 their const overrides, it's for allowing people to still use
 opEquals, toString etc. even if their implementations cannot and
 should not be const.
In this case, they have function that does something else than compare test for equality, etc . . . The overload make no sense here in the first place, and the fact that const break such thing isn't a problem. This is the other way around, the fact that const break such a practice is good.
It *is* a problem when you're talking about abstractions. If I have million node binary trees and I'm testing for equality, I'd like to be able to cache the results. But being forced to use const means I can't cache anything. And this isn't just about caching; if my tree is partially stored in the database, and I have a DB connection object in my tree class, then I can't use opEquals because I can't modify the DB state (which is impractical if I have to actually use it to make DB queries -- the DB engine may have to cache DB pages, etc.). Any abstraction of opEquals beyond the bitwise level cannot be implemented.
Either the structure is mutable, and you have to recompute anyway, or the structure is trully const and thing can be computed up-front.
Jul 11 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 11:03 AM, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu wrote:
 I was a long-time proponent of this. It's less exciting than it may
 seem actually.

 (a) Classes that work with const just fine incur one extra virtual
 call. I think this can be avoided by having the compiler plant the
 same pointer for the const and non-const version in the vtable.

 (b) Classes that can't do as little as one of these four operations
 without mutating the object are completely excluded from the
 immutability system, even if they'd otherwise benefit from it. Even
 those that don't "care" they need to actively _work_ on not caring,
 which doesn't sit well.

 So I don't see this as a viable solution to people who are fine with
 const, but would like to use e.g. some lazy computation.


 Andrei
This solution is not for allowing people to use lazy computation in their const overrides, it's for allowing people to still use opEquals, toString etc. even if their implementations cannot and should not be const. e.g. the LuaD function I posted earlier - it has nothing to do with caching or lazy computation, it's just that it's only logically constant and cannot ever be bitwise constant due to the underlying API. Immutable instances of such structures are next to useless, as every member function except for a single getter function uses mutation.
I think I'll find it rather difficult to get behind modeling arbitrary escapes from immutability. If you want classes, you buy into a certain contract with inherent rights and constraints (starting with using references). It's a given, and again I find it unreasonable to ask that classes allow for arbitrary customization. Your LuaD example goes like this: /** * Compare this object to another with Lua's equality semantics. * Also returns false if the two objects are in different Lua states. */ bool opEquals(T : LuaObject)(ref T o) trusted { if(o.state != this.state) return false; push(); o.push(); scope(success) lua_pop(state, 2); return lua_equal(state, -1, -2); } You may be able to make this work by putting the state associated with push() and pop() in the Cache subsystem. If all else breaks, have opEquals assert(false, "Call bool lua_equals()"). We can't really model every possible design. Andrei
Jul 11 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 05:27 PM, Andrei Alexandrescu wrote:
 We can't really model every possible design.
If it cannot be modelled, it is not a possible design.
Jul 11 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 12:12 PM, Timon Gehr wrote:
 On 07/11/2012 05:27 PM, Andrei Alexandrescu wrote:
 We can't really model every possible design.
If it cannot be modelled, it is not a possible design.
There is if it can be modeled in a less constrained language. Andrei
Jul 11 2012
prev sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 15:27:55 UTC, Andrei Alexandrescu 
wrote:

 I think I'll find it rather difficult to get behind modeling 
 arbitrary escapes from immutability. If you want classes, you 
 buy into a certain contract with inherent rights and 
 constraints (starting with using references). It's a given, and 
 again I find it unreasonable to ask that classes allow for 
 arbitrary customization.
Some classes don't lend themselves to immutability. Let's take something obvious like a class object representing a dataset in a database. How is an immutable instance of such a class useful? The solution to this problem is obvious and right there for us to use, the only thing arbitrary here is the limitation we put on classes by not using it.
 Your LuaD example goes like this:

     /**
      * Compare this object to another with Lua's equality 
 semantics.
      * Also returns false if the two objects are in different 
 Lua states.
      */
     bool opEquals(T : LuaObject)(ref T o)  trusted
     {
         if(o.state != this.state)
             return false;

         push();
         o.push();
         scope(success) lua_pop(state, 2);

         return lua_equal(state, -1, -2);
     }

 You may be able to make this work by putting the state 
 associated with push() and pop() in the Cache subsystem.
Sounds like the kind of workaround people would accompany with a nasty comment.
 If all else breaks, have opEquals assert(false, "Call bool 
 lua_equals()").
This is exactly what I was talking about earlier. opEquals failed me for no good reason, so I'm begrudgingly going to leave an assert behind and give up, writing my own method. As mentioned, this has far-reaching consequences for anything that relies on opEquals.
 We can't really model every possible design.
No, but apparently we can model this one quite readily.
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take something
 obvious like a class object representing a dataset in a database. How is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Jul 11 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 19:49, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take something
 obvious like a class object representing a dataset in a database. How is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Did you saw the proposal of feep/tgehr on #d ? It basically state that you can overload a const method with a non const one if : - You don't mutate any data that belong to the parent. - You are prevented to create any immutable instance of that classe or any subclasse.
Jul 11 2012
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jul 11, 2012 at 08:01:44PM +0200, deadalnix wrote:
 On 11/07/2012 19:49, Andrei Alexandrescu wrote:
On 7/11/12 1:40 PM, Jakob Ovrum wrote:
Some classes don't lend themselves to immutability. Let's take something
obvious like a class object representing a dataset in a database. How is
an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Did you saw the proposal of feep/tgehr on #d ? It basically state that you can overload a const method with a non const one if : - You don't mutate any data that belong to the parent. - You are prevented to create any immutable instance of that classe or any subclasse.
+1, very good, I like this idea! It parallels my idea about the base class being const but not the derived class, except that this is a much better implementation (no need for new syntax, fits in with the existing type system). So basically the const in const(BaseClass) applies only to the members inherited from BaseClass, and the derived class's members are allowed to mutate. T -- "Hi." "'Lo."
Jul 11 2012
parent "Roman D. Boiko" <rb d-coding.com> writes:
On Wednesday, 11 July 2012 at 18:10:04 UTC, H. S. Teoh wrote:
 On Wed, Jul 11, 2012 at 08:01:44PM +0200, deadalnix wrote:
...
 Did you saw the proposal of feep/tgehr on #d ?
 
 It basically state that you can overload a const method with a 
 non
 const one if :
  - You don't mutate any data that belong to the parent.
  - You are prevented to create any immutable instance of that 
 classe
 or any subclasse.
+1, very good, I like this idea!
It is a trade-off, but relatively nice one, IMO.
Jul 11 2012
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 2:01 PM, deadalnix wrote:
 On 11/07/2012 19:49, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take something
 obvious like a class object representing a dataset in a database. How is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Did you saw the proposal of feep/tgehr on #d ? It basically state that you can overload a const method with a non const one if : - You don't mutate any data that belong to the parent. - You are prevented to create any immutable instance of that classe or any subclasse.
Haven't seen that, but on first look it seems promising. Andrei
Jul 11 2012
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 11 Jul 2012 14:01:44 -0400, deadalnix <deadalnix gmail.com> wrote:

 On 11/07/2012 19:49, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take  
 something
 obvious like a class object representing a dataset in a database. How  
 is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Did you saw the proposal of feep/tgehr on #d ? It basically state that you can overload a const method with a non const one if : - You don't mutate any data that belong to the parent. - You are prevented to create any immutable instance of that classe or any subclasse.
I don't like this idea. It means you could not use pure functions to implicitly convert mutable class instances to immutable (something that should be possible today). It also seems to allow abuses. For example: class A { private int _x; public property x() const { return _x; } } class B : A { private int _x2; public property x() { return _x2++; } } Now I've completely changed the logistics of the x property so that it's essentially become mutable. In effect, I overrode the const piece of x completely to make it non-const without a cast, and anyone calling x() on an A instance cannot trust that it won't increment the effective value of x(). I think the solution to this overall problem is simply to make object.opEquals use the most derived types available for comparison. -Steve
Jul 11 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 20:21, Steven Schveighoffer wrote:
 On Wed, 11 Jul 2012 14:01:44 -0400, deadalnix <deadalnix gmail.com> wrote:

 On 11/07/2012 19:49, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take
 something
 obvious like a class object representing a dataset in a database.
 How is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Did you saw the proposal of feep/tgehr on #d ? It basically state that you can overload a const method with a non const one if : - You don't mutate any data that belong to the parent. - You are prevented to create any immutable instance of that classe or any subclasse.
I don't like this idea. It means you could not use pure functions to implicitly convert mutable class instances to immutable (something that should be possible today). It also seems to allow abuses. For example: class A { private int _x; public property x() const { return _x; } } class B : A { private int _x2; public property x() { return _x2++; } } Now I've completely changed the logistics of the x property so that it's essentially become mutable. In effect, I overrode the const piece of x completely to make it non-const without a cast, and anyone calling x() on an A instance cannot trust that it won't increment the effective value of x(). I think the solution to this overall problem is simply to make object.opEquals use the most derived types available for comparison. -Steve
I see no problem here. x isn't pure, so it isn't required to give back the same result each time it is used.
Jul 11 2012
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 11 Jul 2012 14:29:28 -0400, deadalnix <deadalnix gmail.com> wrote:

 On 11/07/2012 20:21, Steven Schveighoffer wrote:
 On Wed, 11 Jul 2012 14:01:44 -0400, deadalnix <deadalnix gmail.com>  
 wrote:

 On 11/07/2012 19:49, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take
 something
 obvious like a class object representing a dataset in a database.
 How is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Did you saw the proposal of feep/tgehr on #d ? It basically state that you can overload a const method with a non const one if : - You don't mutate any data that belong to the parent. - You are prevented to create any immutable instance of that classe or any subclasse.
I don't like this idea. It means you could not use pure functions to implicitly convert mutable class instances to immutable (something that should be possible today). It also seems to allow abuses. For example: class A { private int _x; public property x() const { return _x; } } class B : A { private int _x2; public property x() { return _x2++; } } Now I've completely changed the logistics of the x property so that it's essentially become mutable. In effect, I overrode the const piece of x completely to make it non-const without a cast, and anyone calling x() on an A instance cannot trust that it won't increment the effective value of x(). I think the solution to this overall problem is simply to make object.opEquals use the most derived types available for comparison. -Steve
I see no problem here. x isn't pure, so it isn't required to give back the same result each time it is used.
The expectation is that x belongs to the class, not elsewhere. In fact, the derived type does not change that expectation, it *does* belong to the class. In effect, with this proposal we have logic const, with a shitty implementation requirement (I have to derive to another class in order to achieve it). If we want to implement logical const, let's do it clearly and minimal. -Steve
Jul 11 2012
prev sibling next sibling parent "Roman D. Boiko" <rb d-coding.com> writes:
On Wednesday, 11 July 2012 at 18:21:24 UTC, Steven Schveighoffer 
wrote:
...
 It also seems to allow abuses.  For example:

 class A
 {
    private int _x;
    public  property x() const { return _x; }
 }

 class B : A
 {
    private int _x2;
    public  property x() { return _x2++; }
 }

 Now I've completely changed the logistics of the x property so 
 that it's essentially become mutable.  In effect, I overrode 
 the const piece of x completely to make it non-const without a 
 cast, and anyone calling x() on an A instance cannot trust that 
 it won't increment the effective value of x().

 I think the solution to this overall problem is simply to make 
 object.opEquals use the most derived types available for 
 comparison.

 -Steve
I thought about this example. You can have a const method returning different values anyway (instead of returning what base class' method would return), and A._x has not been mutated. From my point of view, it is just a different concept, but arguably not a worse one.
Jul 11 2012
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:


 It also seems to allow abuses.  For example:

 class A
 {
     private int _x;
     public  property x() const { return _x; }
 }

 class B : A
 {
     private int _x2;
     public  property x() { return _x2++; }
 }
Another abuse: const(B) b = new B; // auto bx = b.x; // oops, compiler error const(A) a = b; auto bx = a.x; -Steve
Jul 11 2012
parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 20:53, Steven Schveighoffer wrote:
 On Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:


 It also seems to allow abuses. For example:

 class A
 {
 private int _x;
 public  property x() const { return _x; }
 }

 class B : A
 {
 private int _x2;
 public  property x() { return _x2++; }
 }
Another abuse: const(B) b = new B; // auto bx = b.x; // oops, compiler error const(A) a = b; auto bx = a.x; -Steve
This shouldn't be a compiler error. An object of type B is 100% of time mutable now.
Jul 11 2012
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 11 Jul 2012 15:14:19 -0400, deadalnix <deadalnix gmail.com> wrote:

 On 11/07/2012 20:53, Steven Schveighoffer wrote:
 On Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:


 It also seems to allow abuses. For example:

 class A
 {
 private int _x;
 public  property x() const { return _x; }
 }

 class B : A
 {
 private int _x2;
 public  property x() { return _x2++; }
 }
Another abuse: const(B) b = new B; // auto bx = b.x; // oops, compiler error const(A) a = b; auto bx = a.x; -Steve
This shouldn't be a compiler error. An object of type B is 100% of time mutable now.
According to my code, b is const, and never was mutable. If you are saying that I should be able to call b.x, then this proposal is even worse than I thought! I have no idea what const means in this world. -Steve
Jul 11 2012
parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 21:45, Steven Schveighoffer wrote:
 On Wed, 11 Jul 2012 15:14:19 -0400, deadalnix <deadalnix gmail.com> wrote:

 On 11/07/2012 20:53, Steven Schveighoffer wrote:
 On Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer
 <schveiguy yahoo.com> wrote:


 It also seems to allow abuses. For example:

 class A
 {
 private int _x;
 public  property x() const { return _x; }
 }

 class B : A
 {
 private int _x2;
 public  property x() { return _x2++; }
 }
Another abuse: const(B) b = new B; // auto bx = b.x; // oops, compiler error const(A) a = b; auto bx = a.x; -Steve
This shouldn't be a compiler error. An object of type B is 100% of time mutable now.
According to my code, b is const, and never was mutable. If you are saying that I should be able to call b.x, then this proposal is even worse than I thought! I have no idea what const means in this world. -Steve
Const is a bridge between mutable and immutable world. The guarantee proposed with const is that it never mutate an immutable data. This is slightly different than how const is implemented actually, but still ensure that no immutable data is muted.
Jul 11 2012
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 11 Jul 2012 17:20:08 -0400, deadalnix <deadalnix gmail.com> wrote:

 Const is a bridge between mutable and immutable world. The guarantee  
 proposed with const is that it never mutate an immutable data. This is  
 slightly different than how const is implemented actually, but still  
 ensure that no immutable data is muted.
No. Const is a contract saying the function will not modify the data given to it via that parameter. It's useful when reasoning about code. When you can modify data passed via a const pointer the entire concept of const becomes convention. -Steve
Jul 12 2012
prev sibling parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
On Wednesday, 11 July 2012 at 18:21:24 UTC, Steven Schveighoffer 
wrote:
 On Wed, 11 Jul 2012 14:01:44 -0400, deadalnix 
 <deadalnix gmail.com> wrote:

 On 11/07/2012 19:49, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's 
 take something
 obvious like a class object representing a dataset in a 
 database. How is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes. Andrei
Did you saw the proposal of feep/tgehr on #d ? It basically state that you can overload a const method with a non const one if : - You don't mutate any data that belong to the parent. - You are prevented to create any immutable instance of that classe or any subclasse.
I don't like this idea. It means you could not use pure functions to implicitly convert mutable class instances to immutable (something that should be possible today).
I do like the idea. Please explain by example why a pure function could no longer convert mutable class instances to immutable? The proposal to restrict the use of immutable is only supposed to affect classes that specifically request it.
 It also seems to allow abuses.  For example:

 class A
 {
    private int _x;
    public  property x() const { return _x; }
 }

 class B : A
 {
    private int _x2;
    public  property x() { return _x2++; }
 }
I think you would have to mark B somehow to indicate that immutable(B) is now illegal, e.g. mutating class B : A { private int _x2; public property override x() { return _x2++; } }
 Now I've completely changed the logistics of the x property so 
 that it's essentially become mutable.
This kind of perversion is already possible when x() is const. x() is allowed to mutate and return a static or global variable.
Jul 11 2012
parent reply travert phare.normalesup.org (Christophe Travert) writes:
"David Piepgrass" , dans le message (digitalmars.D:172007), a écrit :
  mutating class B : A
 {
     private int _x2;
     public  property override x() { return _x2++; }
 }
A fun() pure; You can't cast the result of fun to immutable, because it may be a B instance.
Jul 12 2012
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 12 Jul 2012 03:18:51 -0400, Christophe Travert  =

<travert phare.normalesup.org> wrote:

 "David Piepgrass" , dans le message (digitalmars.D:172007), a =C3=A9cr=
it :
  mutating class B : A
 {
     private int _x2;
     public  property override x() { return _x2++; }
 }
A fun() pure; You can't cast the result of fun to immutable, because it may be a B instance.
Yes. To complete the point, since you have no idea what has derived fro= m = A, it can never be done for non-final objects. -Steve
Jul 12 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jul 11, 2012 at 01:49:53PM -0400, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
Some classes don't lend themselves to immutability. Let's take
something obvious like a class object representing a dataset in a
database. How is an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes.
[...] Yes, that's what we've been trying to say for the last, oh, 100 messages? ;-) T -- Дерево держитÑÑ ÐºÐ¾Ñ€Ð½Ñми, а человек - друзьÑми.
Jul 11 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/11/12 2:07 PM, H. S. Teoh wrote:
 On Wed, Jul 11, 2012 at 01:49:53PM -0400, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take
 something obvious like a class object representing a dataset in a
 database. How is an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes.
[...] Yes, that's what we've been trying to say for the last, oh, 100 messages? ;-)
I didn't see that, sorry. What's the closest quote? Andrei
Jul 11 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 08:18 PM, Andrei Alexandrescu wrote:
 On 7/11/12 2:07 PM, H. S. Teoh wrote:
 On Wed, Jul 11, 2012 at 01:49:53PM -0400, Andrei Alexandrescu wrote:
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take
 something obvious like a class object representing a dataset in a
 database. How is an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes.
[...] Yes, that's what we've been trying to say for the last, oh, 100 messages? ;-)
I didn't see that, sorry. What's the closest quote? Andrei
This is rather close, if put less eloquently: On 07/11/2012 03:45 AM, Timon Gehr wrote:
 But why should I sprinkle my code with 'const' anyway? Almost
 none of its classes will actually be instantiated with the immutable
 qualifier.
This expresses the desire to classify objects into those that can support the interface and those that cannot: On 07/09/2012 01:44 PM, Timon Gehr wrote:
 // object.di
 class RawObject { }
 class Object : RawObject { ... }

 // user.d
 class C { }             // inherits from Object
 class D : RawObject { } // this does not
Jul 11 2012
prev sibling parent travert phare.normalesup.org (Christophe Travert) writes:
Andrei Alexandrescu , dans le message (digitalmars.D:171945), a écrit :
 On 7/11/12 1:40 PM, Jakob Ovrum wrote:
 Some classes don't lend themselves to immutability. Let's take something
 obvious like a class object representing a dataset in a database. How is
 an immutable instance of such a class useful?
This is a good point. It seems we're subjecting all classes to certain limitations for the benefit of a subset of those classes.
Does Object really need to implement opEquals/opHash/... ? This is what limits all classes that do not want to have the same signature for opEquals. The problem is not only in the constness of the argument, but also in its purity, safety, and throwability (although the last two can be worked arround easily). You can compare struct, all having different opEquals/opHash/.... You can put them in AA too. You could do the same for classes. Use a templated system, and give the opportunity for the class to provide its own signature ? There may be code bloat. I mean, classes will suffer from the same code bloat as structs. Solutions can be found reduce this code bloat. Did I miss an issue that makes it mandatory for Object to implement opEquals and friends ? -- Christophe
Jul 11 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 03:30, Jakob Ovrum wrote:
 On Wednesday, 11 July 2012 at 01:19:16 UTC, Andrei Alexandrescu wrote:
 On 7/10/12 6:05 PM, Walter Bright wrote:
 A straightforward workaround is to use PIMPL to encapsulate the logical
 const stuff, and then cast the reference to const to use inside the
 opEquals.
s/straightforward/awful/ Andrei
Honestly, I think the ideal would be to give people the alternative of having a mutable opEquals etc. These methods only need to be logically constant (of course, they don't *need* to be even that, to the joy of operator overloading abusers worldwide), which means no help from the compiler, as it should be - just plain mutable, with the programmer providing the guarantee. There was work in this direction but I understand it was ripe with issues of its own, however I don't understand how any other path could even be considered when it's just moving from one end of the scale to the other. Yes, Object *needs* to work with const, this is imperative. But does it need to compromise on implementations relying on mutability? I was hoping the answer was "no". Obviously such an override would not work with const references, but some classes really don't lean themselves to immutability at all, alleviating the need for const. I can also imagine that if both were allowed, someone would leverage the ability to use caching in the mutable overload, then falling back to eager computation in the const overload.
The cached value can in fact rely outside the object. private string[const MyObject] cache; class MyObject { override string toString() { return cache.get(this, complexCalculationOfToStringWeNeedToCache); } ~this() { cache.remove(this); } } This really isn't a problem. This is even thread safe. cache can be made static into MyObject too. Actually, plenty of solutions exists.
Jul 11 2012
parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 13:55:30 UTC, deadalnix wrote:
 The cached value can in fact rely outside the object.
For the umpteenth time, I am not talking about caching.
Jul 11 2012
prev sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 11 July 2012 at 01:19:16 UTC, Andrei Alexandrescu 
wrote:
 On 7/10/12 6:05 PM, Walter Bright wrote:
 A straightforward workaround is to use PIMPL to encapsulate 
 the logical
 const stuff, and then cast the reference to const to use 
 inside the
 opEquals.
s/straightforward/awful/ Andrei
Honestly, I think the ideal would be to give people the alternative of having a mutable opEquals etc. These methods only need to be logically constant (of course, they don't *need* to be even that, to the joy of operator overloading abusers worldwide), which means no help from the compiler, as it should be - just plain mutable, with the programmer providing the guarantee. There was work in this direction but I understand it was ripe with issues of its own, however I don't understand how any other path could even be considered when it's just moving from one end of the scale to the other. Yes, Object *needs* to work with const, this is imperative. But does it need to compromise on implementations relying on mutability? I was hoping the answer was "no". Obviously such an override would not work with const references, but some classes really don't lean themselves to immutability at all, alleviating the need for const. I can also imagine that if both were allowed, someone would leverage the ability to use caching in the mutable overload, then falling back to eager computation in the const overload.
Jul 10 2012
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Jul 2012 07:44:24 -0400, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 07/09/2012 08:37 AM, Adam Wilson wrote:
 Object is now const-correct throughout D. This has been a dream for many
 of you. Today it is a reality.
PITA. Forced const can severely harm a code base that wants to be flexible -- it leaks implementation details and is infectuous. Any options planned to allow not inheriting all the cruft from Object if it is of no use? Eg: // object.di class RawObject { } class Object : RawObject { ... } // user.d class C { } // inherits from Object class D : RawObject { } // this does not
Two solutions: 1. object.opEquals(T : Object, U : Object) (T t, U u) which can use mutable opEquals for derived types 2. define a non-operator equality function, i.e. mutableEquals(). Can be attached to actual opEquals with struct wrapper. These are not insolvable problems. -Steve
Jul 11 2012
prev sibling next sibling parent "Eyyub" <eyyub.pangearaion gmail.com> writes:
Congrats !
Jul 09 2012
prev sibling next sibling parent mta`chrono <chrono mta-international.net> writes:
Great work!!! Thanks guys!
Jul 09 2012
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, July 10, 2012 12:00:59 H. S. Teoh wrote:
 I think hidden somewhere in this is an unconscious conflation of
 physical const with logical const.
I completely disagree at least as far as classes go. opEquals, opCmp, toString, and toHash must be _physically_ const, because they must work with physically const objects. There is _no_ way around that, and whether the actual internals of those functions could conceivably mutate something if they were logically const is irrelevant. The fact that D's const is physical const and that we must be able to have const object _mandates_ that those functions be const. There is no other option. There is no conflating of physical constness and logical constness there. It's purely a matter of making those functions callable by const objects (which happen to be physically const, because D's const is physical const, but even if D's const were logical const, the situation wouldn't change; those functions would still need to be const to work with const objects - it would just be logical const rather than physical const). We may be able to make it possible to use non-const objects with those functions as well via overloading or whatnot, but as far as classes go, it's an absolute requirement that those functions be const. And they also need to be safe, pure, and nothrow, or classes in general are similarly screwed with regards to those attributes. Structs is the _only_ place where having those functions being required to be const is arguably conflating logical const and physical const. That's not the case with classes at all. - Jonathan M Davis
Jul 10 2012
parent deadalnix <deadalnix gmail.com> writes:
On 10/07/2012 22:11, Jonathan M Davis wrote:
 On Tuesday, July 10, 2012 12:00:59 H. S. Teoh wrote:
 I think hidden somewhere in this is an unconscious conflation of
 physical const with logical const.
I completely disagree at least as far as classes go. opEquals, opCmp, toString, and toHash must be _physically_ const, because they must work with physically const objects. There is _no_ way around that, and whether the actual internals of those functions could conceivably mutate something if they were logically const is irrelevant.
That is pretty clear and I couldn't agree more.
Jul 11 2012
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 04:11:05PM -0400, Jonathan M Davis wrote:
 On Tuesday, July 10, 2012 12:00:59 H. S. Teoh wrote:
 I think hidden somewhere in this is an unconscious conflation of
 physical const with logical const.
I completely disagree at least as far as classes go. opEquals, opCmp, toString, and toHash must be _physically_ const, because they must work with physically const objects.
[...] Yes, this is because D only has physical const. But physical const breaks encapsulation, and precludes a variety of applications such as caching, objects accessed over the network, etc.. I think that's what this uproar is all about. Physical const is useful, and in many cases necessary, but I think we're deceiving ourselves if we imagine that physical const is the whole story. With the current state of affairs, the scope of const is greatly limited. If I want objects which cache hash values, I'm out of luck, I have to write my own non-const methods. If I want objects accessed over the network, I'm out of luck, I can't use const. If I'm writing a base class that _might_ have _one_ derived class that requires a non-const version of a method, I'm out of luck, I can't use const at all. If my class could conceivably be inherited by third party code, then I can't use const -- because otherwise I might preclude my customers from writing code that caches values. And when I can't use const, I have to write my own version of opEquals (and call it something else), my own convention for computing hash values, my own version of everything in druntime. All that elaborate infrastructure in druntime becomes practically worthless. It's all-or-nothing. If I'm OK with physical const, then all is fine and dandy. But as soon as one thing can't be const, I've to re-engineer my entire framework from ground up. Isn't there something we can do to improve this situation? T -- Sometimes the best solution to morale problems is just to fire all of the unhappy people. -- despair.com
Jul 10 2012
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Tuesday, 10 July 2012 at 21:18:18 UTC, H. S. Teoh wrote:
 If I'm OK with physical const, then all is fine and
 dandy.  But as soon as one thing can't be const, I've to 
 re-engineer my
 entire framework from ground up.

 Isn't there something we can do to improve this situation?


 T
I don't think the answer is to change D's const. D's const, unlike C++'s const, only exists to bridge immutable and mutable data. As soon as it becomes incompatible with immutable (which C++'s const very much is), it ceases to be purposeful. The problem arises when const is forced upon interfaces like toString and opEquals. we don't expect these functions to change observable state. In other words, we expect them to be logically constant, but not necessarily bitwise constant. That's not something the compiler can enforce, and it shouldn't try to. Other operators, e.g. opIndex, correctly leave the responsibility to the programmer. There's also the reality that you want toString, opEquals, etc. to work with immutable (run-time type) class instances, requiring compatibility with const (compile-time type) references. What I don't understand is why we've chosen const over mutable, when we should strive for allowing either and maybe even both. We've moved from one end of the scale to the opposite - our current situation is equally limiting to the previous situation. I think these changes were rushed; understandable considering the amount of pressure, but it just fixes one problem and creates an equally big new problem.
Jul 10 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 11:59:48PM +0200, Jakob Ovrum wrote:
 On Tuesday, 10 July 2012 at 21:18:18 UTC, H. S. Teoh wrote:
If I'm OK with physical const, then all is fine and dandy.  But as
soon as one thing can't be const, I've to re-engineer my entire
framework from ground up.

Isn't there something we can do to improve this situation?


T
I don't think the answer is to change D's const. D's const, unlike C++'s const, only exists to bridge immutable and mutable data. As soon as it becomes incompatible with immutable (which C++'s const very much is), it ceases to be purposeful.
I don't think we want to change physical (bitwise) const. It does have its uses, and it's necessary if you want immutable to work nicely with mutable. But if the current const is all we have, then IMO something is missing from the language.
 The problem arises when const is forced upon interfaces like toString
 and opEquals. we don't expect these functions to change observable
 state. In other words, we expect them to be logically constant, but
 not necessarily bitwise constant. That's not something the compiler
 can enforce, and it shouldn't try to. Other operators, e.g. opIndex,
 correctly leave the responsibility to the programmer.
Exactly, that's what I mean, we're using bitwise const in druntime in places where we actually intended _logical_ const. Bitwise const is a subset of logical const, and right now the subset is the only thing we have.
 There's also the reality that you want toString, opEquals, etc. to
 work with immutable (run-time type) class instances, requiring
 compatibility with const (compile-time type) references.
 
 What I don't understand is why we've chosen const over mutable, when
 we should strive for allowing either and maybe even both. We've moved
 from one end of the scale to the opposite - our current situation is
 equally limiting to the previous situation.
I suppose the thought was that since const subsumes both mutable and immutable, it would be the ideal middle ground. But it turns out not to be the case. (Or rather, it _is_ the case, just not quite what we'd imagined.)
 I think these changes were rushed; understandable considering the
 amount of pressure, but it just fixes one problem and creates an
 equally big new problem.
I don't think they were rushed. There's been a push for making druntime and Phobos const-correct for a while now. I don't think this change is a _mistake_ per se, but it does expose a flaw in the language: const is too limited in scope, and we need something else to fill in for use cases where const isn't good enough. T -- Why have vacation when you can work?? -- EC
Jul 10 2012
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 3:13 PM, H. S. Teoh wrote:
 I don't think they were rushed. There's been a push for making druntime
 and Phobos const-correct for a while now. I don't think this change is a
 _mistake_ per se, but it does expose a flaw in the language: const is
 too limited in scope, and we need something else to fill in for use
 cases where const isn't good enough.
There's a gigantic problem with logical const. It is completely unenforceable - it is documentation only. All the reasoning and mechanical guarantees that come with const, immutable, and purity fall apart.
Jul 10 2012
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 03:39:30PM -0700, Walter Bright wrote:
 On 7/10/2012 3:13 PM, H. S. Teoh wrote:
I don't think they were rushed. There's been a push for making
druntime and Phobos const-correct for a while now. I don't think this
change is a _mistake_ per se, but it does expose a flaw in the
language: const is too limited in scope, and we need something else
to fill in for use cases where const isn't good enough.
There's a gigantic problem with logical const. It is completely unenforceable - it is documentation only. All the reasoning and mechanical guarantees that come with const, immutable, and purity fall apart.
[...] What about the "partial const" idea I had? Given a class C which derives from a base class B, we may designate the base class B as const, which makes every inherited field const, while C itself may have mutable members. Then we can pass instances of C around as B references (it's OK to implicitly convert C to const(B), because the B part of C is actually const). As far as the user of the B references can tell, the object is const, even though the methods of C may be mutating C-specific members. So C could be a caching class, or whatever it is that needs mutation to work, and B is its "const part" which is guaranteed by the type system never to mutate. T -- Give me some fresh salted fish, please.
Jul 10 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 4:05 PM, H. S. Teoh wrote:
 On Tue, Jul 10, 2012 at 03:39:30PM -0700, Walter Bright wrote:
 On 7/10/2012 3:13 PM, H. S. Teoh wrote:
 I don't think they were rushed. There's been a push for making
 druntime and Phobos const-correct for a while now. I don't think this
 change is a _mistake_ per se, but it does expose a flaw in the
 language: const is too limited in scope, and we need something else
 to fill in for use cases where const isn't good enough.
There's a gigantic problem with logical const. It is completely unenforceable - it is documentation only. All the reasoning and mechanical guarantees that come with const, immutable, and purity fall apart.
[...] What about the "partial const" idea I had? Given a class C which derives from a base class B, we may designate the base class B as const, which makes every inherited field const, while C itself may have mutable members. Then we can pass instances of C around as B references (it's OK to implicitly convert C to const(B), because the B part of C is actually const). As far as the user of the B references can tell, the object is const, even though the methods of C may be mutating C-specific members. So C could be a caching class, or whatever it is that needs mutation to work, and B is its "const part" which is guaranteed by the type system never to mutate.
Logical const is still mutating const members.
Jul 10 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 04:05:51PM -0700, Walter Bright wrote:
 On 7/10/2012 4:05 PM, H. S. Teoh wrote:
[...]
What about the "partial const" idea I had?

Given a class C which derives from a base class B, we may designate
the base class B as const, which makes every inherited field const,
while C itself may have mutable members. Then we can pass instances
of C around as B references (it's OK to implicitly convert C to
const(B), because the B part of C is actually const). As far as the
user of the B references can tell, the object is const, even though
the methods of C may be mutating C-specific members.

So C could be a caching class, or whatever it is that needs mutation
to work, and B is its "const part" which is guaranteed by the type
system never to mutate.
Logical const is still mutating const members.
[...] Not in this case. The const(B) reference does not permit any of B's methods to mutate the members of B -- you cannot downcast a const(B) reference to a C reference. As far as the methods of B are concerned, the object is immutable. The interesting part is when C's methods override B's methods: those methods _can_ mutate the object, but not the members inherited from B. And this does not break immutability; you cannot cast const(B) to C, so if you start with immutable(B), it will remain immutable no matter what. You can't call C's methods from the const(B) reference, and you can't override B's methods with C's mutating methods without actually having a mutable C to begin with. T -- MAS = Mana Ada Sistem?
Jul 10 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 4:19 PM, H. S. Teoh wrote:
 On Tue, Jul 10, 2012 at 04:05:51PM -0700, Walter Bright wrote:
 On 7/10/2012 4:05 PM, H. S. Teoh wrote:
[...]
 What about the "partial const" idea I had?

 Given a class C which derives from a base class B, we may designate
 the base class B as const, which makes every inherited field const,
 while C itself may have mutable members. Then we can pass instances
 of C around as B references (it's OK to implicitly convert C to
 const(B), because the B part of C is actually const). As far as the
 user of the B references can tell, the object is const, even though
 the methods of C may be mutating C-specific members.

 So C could be a caching class, or whatever it is that needs mutation
 to work, and B is its "const part" which is guaranteed by the type
 system never to mutate.
Logical const is still mutating const members.
[...] Not in this case. The const(B) reference does not permit any of B's methods to mutate the members of B -- you cannot downcast a const(B) reference to a C reference. As far as the methods of B are concerned, the object is immutable. The interesting part is when C's methods override B's methods: those methods _can_ mutate the object, but not the members inherited from B. And this does not break immutability; you cannot cast const(B) to C, so if you start with immutable(B), it will remain immutable no matter what. You can't call C's methods from the const(B) reference, and you can't override B's methods with C's mutating methods without actually having a mutable C to begin with. T
If you've found a way to mutate const and have it stay const, then there's a hole in the typing system.
Jul 10 2012
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jul 10, 2012 at 04:58:06PM -0700, Walter Bright wrote:
 On 7/10/2012 4:19 PM, H. S. Teoh wrote:
On Tue, Jul 10, 2012 at 04:05:51PM -0700, Walter Bright wrote:
On 7/10/2012 4:05 PM, H. S. Teoh wrote:
[...]
Nqt in this case. The const(B) reference does not permit any of B's
methods to mutate the members of B -- you cannot downcast a const(B)
reference to a C reference. As far as the methods of B are concerned,
the object is immutable.

The interesting part is when C's methods override B's methods: those
methods _can_ mutate the object, but not the members inherited from
B.  And this does not break immutability; you cannot cast const(B) to
C, so if you start with immutable(B), it will remain immutable no
matter what.  You can't call C's methods from the const(B) reference,
and you can't override B's methods with C's mutating methods without
actually having a mutable C to begin with.


T
If you've found a way to mutate const and have it stay const, then there's a hole in the typing system.
The const in const(B) applies to the B portion of the object. Just because the C portion changes, doesn't violate the const-ness of the B portion. I know this isn't how the current type system works, but perhaps it can be made to work in a way that makes sense. T -- Real Programmers use "cat > a.out".
Jul 10 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 7:58 PM, Walter Bright wrote:
 If you've found a way to mutate const and have it stay const, then
 there's a hole in the typing system.
No. This is dogma devoid of substance. Maybe you found a way to initialize on first read, and suddenly there's a viable type system. Andrei
Jul 10 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 12:39 AM, Walter Bright wrote:
 On 7/10/2012 3:13 PM, H. S. Teoh wrote:
 I don't think they were rushed. There's been a push for making druntime
 and Phobos const-correct for a while now. I don't think this change is a
 _mistake_ per se, but it does expose a flaw in the language: const is
 too limited in scope, and we need something else to fill in for use
 cases where const isn't good enough.
There's a gigantic problem with logical const. It is completely unenforceable - it is documentation only. All the reasoning and mechanical guarantees that come with const, immutable, and purity fall apart.
Const is stronger than what is required to bridge the gap between mutable and immutable. It guarantees that a reference cannot be used to mutate the receiver regardless of whether or not the receiver is immutable underneath. That is unnecessary as far as immutable is concerned. It only needs to guarantee that the receiver does not change if it is immutable underneath.
Jul 10 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/10/2012 4:16 PM, Timon Gehr wrote:
 Const is stronger than what is required to bridge the gap between
 mutable and immutable. It guarantees that a reference cannot be used to
 mutate the receiver regardless of whether or not the receiver is
 immutable underneath. That is unnecessary as far as immutable is
 concerned. It only needs to guarantee that the receiver does not change
 if it is immutable underneath.
If you have a const function that accepts both mutable and immutable args, then that function *by definition* cannot tell if it received mutable or immutable args. Furthermore, a const function is saying it will not change, even if mutable data is passed to it. Everything falls apart once you allow "logical const" in. You'll be in the same boat as C++ const, which is faith-based programming rather than checkable programming.
Jul 10 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 01:57 AM, Walter Bright wrote:
 On 7/10/2012 4:16 PM, Timon Gehr wrote:
 Const is stronger than what is required to bridge the gap between
 mutable and immutable. It guarantees that a reference cannot be used to
 mutate the receiver regardless of whether or not the receiver is
 immutable underneath. That is unnecessary as far as immutable is
 concerned. It only needs to guarantee that the receiver does not change
 if it is immutable underneath.
If you have a const function that accepts both mutable and immutable args, then that function *by definition* cannot tell if it received mutable or immutable args.
I understand. The trick is to disallow creating immutable instances of a class which is allowed to mutate the receiver in const methods. This is essentially your proposal with the casts, but it is type safe. This removes the 'const' guarantees, but 'immutable' stays unaffected. Furthermore, functions with closures are type checked at their creation site and may violate const-transitivity without affecting 'immutable'.
 Furthermore, a const function is saying it will not change, even if
 mutable data is passed to it.
A const function is saying nothing at all. Only const pure functions provide any guarantees.
 Everything falls apart once you allow "logical const" in. You'll be in
 the same boat as C++ const, which is faith-based programming rather than
 checkable programming.
Casting away const because the object one is implementing cannot support it is faith-based programming. I for one would be satisfied if inheriting from object became optional: // object.di class RawObject /+ this is the root of the class hierarchy +/{ } class SynchronizableObject : RawObject { void* monitor; } class Object : SynchronizableObject { const { stuff } } // user code class NoCruft : RawObject { // ... } And if AAs would stop being overly zealous about the opEquals, toHash, opCmp signatures (why would it need opCmp? I have implemented my own hash table for now, because I cannot support opCmp.) The AA should just propagate the signatures to its methods. It will be safe to index an AA if the required members are safe etc.
Jul 10 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-07-11 02:20, Timon Gehr wrote:

 I for one would be satisfied if inheriting from object became optional:

 // object.di
 class RawObject /+ this is the root of the class hierarchy +/{ }
 class SynchronizableObject : RawObject { void* monitor; }
 class Object : SynchronizableObject {
 const { stuff }
 }

 // user code
 class NoCruft : RawObject {
 // ...
 }
Ruby 1.9 is doing something similar. class BasicObject end class Object < BasicObject end end end I wonder if the same can be done, with the now base class protection, in D: class RawObject : private Object {} class Foo : RawObject {} -- /Jacob Carlborg
Jul 11 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
On 11/07/2012 02:20, Timon Gehr wrote:
 On 07/11/2012 01:57 AM, Walter Bright wrote:
 On 7/10/2012 4:16 PM, Timon Gehr wrote:
 Const is stronger than what is required to bridge the gap between
 mutable and immutable. It guarantees that a reference cannot be used to
 mutate the receiver regardless of whether or not the receiver is
 immutable underneath. That is unnecessary as far as immutable is
 concerned. It only needs to guarantee that the receiver does not change
 if it is immutable underneath.
If you have a const function that accepts both mutable and immutable args, then that function *by definition* cannot tell if it received mutable or immutable args.
I understand. The trick is to disallow creating immutable instances of a class which is allowed to mutate the receiver in const methods. This is essentially your proposal with the casts, but it is type safe. This removes the 'const' guarantees, but 'immutable' stays unaffected. Furthermore, functions with closures are type checked at their creation site and may violate const-transitivity without affecting 'immutable'.
It is interesting. But is does transfers the constraint on const on constraint of not being const for children. This isn't a free win, and I'm not sure it worth it.
Jul 11 2012
prev sibling parent "SomeDude" <lovelydear mailmetrash.com> writes:
On Wednesday, 11 July 2012 at 00:20:32 UTC, Timon Gehr wrote:
 I for one would be satisfied if inheriting from object became 
 optional:

 // object.di
 class RawObject /+ this is the root of the class hierarchy +/{ }
 class SynchronizableObject : RawObject { void* monitor; }
 class Object : SynchronizableObject {
      const { stuff }
 }

 // user code
 class NoCruft : RawObject {
     // ...
 }
At first sight, I think this is an interesting idea. Maybe the class hierarchy needs to be rethought for more flexibility ?
Jul 15 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 7:57 PM, Walter Bright wrote:
 On 7/10/2012 4:16 PM, Timon Gehr wrote:
 Const is stronger than what is required to bridge the gap between
 mutable and immutable. It guarantees that a reference cannot be used to
 mutate the receiver regardless of whether or not the receiver is
 immutable underneath. That is unnecessary as far as immutable is
 concerned. It only needs to guarantee that the receiver does not change
 if it is immutable underneath.
If you have a const function that accepts both mutable and immutable args, then that function *by definition* cannot tell if it received mutable or immutable args.
It can if immutable(T) is invalid.
 Furthermore, a const function is saying it will not change, even if
 mutable data is passed to it.
Lazy computation does not produce detectable change.
 Everything falls apart once you allow "logical const" in. You'll be in
 the same boat as C++ const, which is faith-based programming rather than
 checkable programming.
Then please do not suggest pimpl and casting. That is UNDEFINED. If you want to ascribe any meaning to it, you ruin all the work we've done on immutability. Andrei
Jul 10 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 6:39 PM, Walter Bright wrote:
 On 7/10/2012 3:13 PM, H. S. Teoh wrote:
 I don't think they were rushed. There's been a push for making druntime
 and Phobos const-correct for a while now. I don't think this change is a
 _mistake_ per se, but it does expose a flaw in the language: const is
 too limited in scope, and we need something else to fill in for use
 cases where const isn't good enough.
There's a gigantic problem with logical const. It is completely unenforceable - it is documentation only. All the reasoning and mechanical guarantees that come with const, immutable, and purity fall apart.
I think we need to unlock that mindset lest we risk short-circuiting all reason by always (constantly, heh) falling back on dogma a la "logical const is unenforceable". This is a very damaging pattern. We can devise a number of solutions in which there's lazy computation inside const methods, while still preserving the current guarantees. The only issue with those is they complicate the definition and implementation of D. So the question remains whether the complication is worth it. I personally am not convinced. Sure, examples can be found if one looks real hard, but I think their importance is overstated when the community gets in the mood of proving their need. Fact is, the use of "mutable" in C++ is very scant even in codebases that use const religiously (like ours). A cursory search yielded 0.01% as many uses of "mutable" as "const" in our very large codebase. Andrei
Jul 10 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/11/2012 03:43 AM, Andrei Alexandrescu wrote:
 On 7/10/12 6:39 PM, Walter Bright wrote:
 On 7/10/2012 3:13 PM, H. S. Teoh wrote:
 I don't think they were rushed. There's been a push for making druntime
 and Phobos const-correct for a while now. I don't think this change is a
 _mistake_ per se, but it does expose a flaw in the language: const is
 too limited in scope, and we need something else to fill in for use
 cases where const isn't good enough.
There's a gigantic problem with logical const. It is completely unenforceable - it is documentation only. All the reasoning and mechanical guarantees that come with const, immutable, and purity fall apart.
I think we need to unlock that mindset lest we risk short-circuiting all reason by always (constantly, heh) falling back on dogma a la "logical const is unenforceable". This is a very damaging pattern. We can devise a number of solutions in which there's lazy computation inside const methods, while still preserving the current guarantees. The only issue with those is they complicate the definition and implementation of D. So the question remains whether the complication is worth it. I personally am not convinced. Sure, examples can be found if one looks real hard, but I think their importance is overstated when the community gets in the mood of proving their need. Fact is, the use of "mutable" in C++ is very scant even in codebases that use const religiously (like ours). A cursory search yielded 0.01% as many uses of "mutable" as "const" in our very large codebase. Andrei
This is unfortunately easily explained using the fact that const is not transitive in C++.
Jul 10 2012
prev sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Tuesday, 10 July 2012 at 22:12:35 UTC, H. S. Teoh wrote:

 I don't think we want to change physical (bitwise) const. It 
 does have
 its uses, and it's necessary if you want immutable to work 
 nicely with
 mutable. But if the current const is all we have, then IMO 
 something is
 missing from the language.
The purpose of the recent change is to allow operations like opEquals and toString on immutable class instances. If there is no immutable in the loop, you're not gaining anything by using const. Even if we had a qualifier for something like logical const - let's call it lconst - it wouldn't be compatible with the current immutable and thus not the current const. It would be a separate system. lconst references would be implicitly convertible to const references, but not the other way around, equal to the current issue of not having both mutable and const options for opEquals and friends. You'd have exactly the same problem.
 Exactly, that's what I mean, we're using bitwise const in 
 druntime in
 places where we actually intended _logical_ const. Bitwise 
 const is a
 subset of logical const, and right now the subset is the only 
 thing we
 have.
These changes are *not* to help programmers make methods like opEquals and toString follow previously implicit rules, they're here to make these methods work with const (yes, the bitwise const) and thus immutable.
 I suppose the thought was that since const subsumes both 
 mutable and
 immutable, it would be the ideal middle ground.  But it turns 
 out not to
 be the case. (Or rather, it _is_ the case, just not quite what 
 we'd
 imagined.)
We knew exactly what this meant for opEquals implementations that weren't bitwise const but otherwise completely reasonable. I remember posting about these issues months ago, and I've also posted in the relevant Github pull request.
 I don't think they were rushed. There's been a push for making 
 druntime
 and Phobos const-correct for a while now.
Yes, the discussion has existed ever since const was introduced. I said I think the proposed solution was rushed, as people had pointed out several issues with it that nobody were able to address.
 I don't think this change is a
 _mistake_ per se, but it does expose a flaw in the language:
 const is
 too limited in scope, and we need something else to fill in for 
 use
 cases where const isn't good enough.


 T
I think this is a red herring. The ultimate goal is to make these methods work with immutable class instances, which means the current const is exactly what we want to support. We just have to make sure to do it in a way that doesn't compromise on methods requiring mutability. I wish I had a good solution for that, but the fact that I don't have one doesn't make the problem any less real.
Jul 10 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/10/12 5:19 PM, H. S. Teoh wrote:
 Isn't there something we can do to improve this situation?
There is, only thing is there's no easy way (without adding some amount of complication to the language). Generally const(T) is a supertype of T and immutable(T), meaning it could originate from either. There is value in immutable objects that has been well discussed, which is incompatible with logical constness. We can change the language such as: a given type X has the option to declare "I want logical const and for that I'm giving up the possibility of creating immutable(X)". That keeps things proper for everybody - immutable still has the strong properties we know and love, and such types can actually use logical const. A number of refinements are as always possible. I'm not sure if I was very clear. In brief, logical constness breaks things only if the original object was immutable. Andrei
Jul 10 2012
parent travert phare.normalesup.org (Christophe Travert) writes:
Andrei Alexandrescu , dans le message (digitalmars.D:171828), a écrit :
 On 7/10/12 5:19 PM, H. S. Teoh wrote:
 
 There is value in immutable objects that has been well discussed, which 
 is incompatible with logical constness. We can change the language such 
 as: a given type X has the option to declare "I want logical const and 
 for that I'm giving up the possibility of creating immutable(X)". That 
 keeps things proper for everybody - immutable still has the strong 
 properties we know and love, and such types can actually use logical const.
 
 A number of refinements are as always possible.
I think this is a good idea, but for classes, inheritance is an issue. Example: class A { int a; int compute() const pure { return a; } final int fun() const pure { a.compute; // optimised out by the compiler return a.compute; } } class B : A { mutable int b; override int compute() const pure { if(!b) b = longComputation(a); // a += 1; // error, a is bitwise-const return b; // mutating and returning a mutable part at the // programmer's risk } } A.compute is bitwise const. However B.compute is logical const. A.fun is bitwise const, and can be optimised. But that is no longer true with a B instance. However, the compiler must be able to make those optimizations, otherwise all the power of const for any non final object is lost, because someone may derive a logical const class. This means the programmer is *responsible* for creating a logical const-behavior. This is a serious issue. Given the system-programming aspect of D, I would say the programmer should be allowed to do such a thing, taking the risk to have an undefined behavior. But with great caution. At least, it will be less dangerous than casting away const. Just providing a way to make it impossible to create an immutable instance of some classes would make it less dangerous to cast away constness. -- Christophe
Jul 11 2012
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, July 10, 2012 14:19:46 H. S. Teoh wrote:
 On Tue, Jul 10, 2012 at 04:11:05PM -0400, Jonathan M Davis wrote:
 On Tuesday, July 10, 2012 12:00:59 H. S. Teoh wrote:
 I think hidden somewhere in this is an unconscious conflation of
 physical const with logical const.
I completely disagree at least as far as classes go. opEquals, opCmp, toString, and toHash must be _physically_ const, because they must work with physically const objects.
 Isn't there something we can do to improve this situation?
There may be. It's an open question. For opEquals, it was suggested (by Steven IIRC) that we could make it so that the free function opEquals works with both const and non-const objects such that if your class defines a non-const opEquals, and you compare mutable instances of that class, it would use the non-const version. It already has to be templated anyway. And if we make it so that Object has both a const and non-const overload of opEquals, then the appropriate one will be used. Classes which can be const have their non-const opEquals call their const opEquals, and those that can't throw an Error from the const version if it's ever called. We could probably do the same thing with the other 3 functions, and there have been other, similar proposals. Presumably, we can work _something_ out, but how best to do it is still an open question. However, regardless of whether we can sort out making classes which can't be const work, there's no question that we must have const versions of opEquals, opCmp, toString, and toHash on Object, otherwise const is horribly broken. - Jonathan M Davis
Jul 10 2012