digitalmars.D - Congratulations to the D Team!
- Adam Wilson (54/54) Jul 08 2012 Ladies and Gentlemen,
- Jacob Carlborg (7/55) Jul 09 2012 Yeah, I wonder what the h*ll had happened when I woke up and had around
- Paulo Pinto (3/56) Jul 09 2012 And I just did a git checkout on Saturday! Time to do a refresh.
- Jonathan M Davis (13/18) Jul 09 2012 Andrei is the one who was doing most of the work by reviewing and mergin...
- Jacob Carlborg (4/9) Jul 09 2012 We want to have it like this everyday :)
- deadalnix (3/9) Jul 09 2012 I have to agree here, but it is not very practical. It would require
- Andrei Alexandrescu (7/24) Jul 09 2012 Giving me credit for this is like giving credit the cable for
- Jonathan M Davis (12/22) Jul 09 2012 It's a team effort, but the processing of pull requests has been a defin...
- Adam Wilson (12/42) Jul 09 2012 When I mentioned this post on IRC he specifically requested to be left o...
- Jonathan M Davis (6/10) Jul 09 2012 Ah. Okay. It just seemed really odd to me that you were singling people ...
- David Piepgrass (9/16) Jul 09 2012 Thanks for doing this! I haven't contributed yet, but it was
- Andrei Alexandrescu (5/8) Jul 09 2012 You definitely should get to the point where you can contribute to D via...
- Guillaume Chatelet (2/50) Jul 09 2012 Congrats :)
- d coder (5/5) Jul 09 2012 Congratulations.
- SomeDude (2/3) Jul 15 2012 +1 The D community is awesome, and is getting better everyday.
- Timon Gehr (12/14) Jul 09 2012 PITA. Forced const can severely harm a code base that wants to be
- H. S. Teoh (11/17) Jul 09 2012 [...]
- Timon Gehr (23/37) Jul 09 2012 1.
- David Piepgrass (24/61) Jul 09 2012 I guess D does not have 'mutable' (like C++) to override const on
- Jonathan M Davis (18/20) Jul 09 2012 No, const is actually const. You can't modify anything which is const th...
- Andrei Alexandrescu (11/33) Jul 09 2012 It doesn't, which makes life difficult for certain idioms. On the
- Timon Gehr (2/15) Jul 09 2012 This is very inconvenient if the root of the class hierarchy uses it.
- H. S. Teoh (20/41) Jul 09 2012 [...]
- Jonathan M Davis (6/12) Jul 09 2012 That works as long as the const version still works. As soon as you _nee...
- bearophile (6/8) Jul 09 2012 You are not the first person that says similar things. So D docs
- Era Scarecrow (22/28) Jul 09 2012 Depends on what you are trying to do I suppose. The only
- David Piepgrass (43/63) Jul 10 2012 The difficulty, in case you missed it, is that somebody else (the
- H. S. Teoh (51/79) Jul 10 2012 I think the trouble comes from conflating logical const with actual,
- Timon Gehr (2/4) Jul 10 2012 Then why on earth is druntime acting as if it does?
- H. S. Teoh (34/40) Jul 10 2012 Y'know, this brings up an interesting question. Do methods like toString
- Don Clugston (12/50) Jul 11 2012 I think you're right.
- Andrei Alexandrescu (40/50) Jul 11 2012 I think a caching solution would cover most valid needs and indeed would...
- Andrei Alexandrescu (3/6) Jul 11 2012 Oops, that would be with linear search :o).
- Roman D. Boiko (4/6) Jul 11 2012 As for me, lazy computation (with caching) would likely be the
- Timon Gehr (3/15) Jul 11 2012 Well, it is inefficient.
- Andrei Alexandrescu (4/10) Jul 11 2012 The idea here is to assess functionality provided in order to decide
- Roman D. Boiko (4/15) Jul 11 2012 Probably using a separate thread would be better for voting,
- Roman D. Boiko (4/15) Jul 11 2012 Probably using a separate thread would be better for voting or
- David Piepgrass (28/83) Jul 11 2012 Aside: what's the difference between this and new
- David Piepgrass (13/33) Jul 11 2012 I must be tired.
- travert phare.normalesup.org (Christophe Travert) (6/32) Jul 12 2012 Yes. It is possible to write a library solution to compute a cached
- Sean Kelly (10/64) Jul 11 2012 t could be done in a library). The user would need to provide a const, p...
- Jonathan M Davis (22/39) Jul 10 2012 For a member function to be called on a const object, that function must...
- H. S. Teoh (114/148) Jul 10 2012 Yes, they have to be const, but by doing so, we are implicitly forcing
- Geoffrey Biggs (13/35) Jul 09 2012 Although I don't know what D's goals with const are, in C++,
- Timon Gehr (3/29) Jul 09 2012 Exactly! I don't think that the term 'const correctness' actually
- deadalnix (9/9) Jul 11 2012 I don't think that is a big issue.
- Timon Gehr (5/9) Jul 11 2012 And sometimes it is not known in advance if it will be accessed at all.
- deadalnix (6/17) Jul 11 2012 Arguably, this is a software design problem. I never encountered a case
- deadalnix (3/21) Jul 11 2012 By the way, I'd be happy to be proven false, but I think the issue is
- Timon Gehr (5/27) Jul 11 2012 Frankly, there is no real issue. It is always possible to sidestep the
- Timon Gehr (5/24) Jul 11 2012 This means it will bite you in expectedly the next 48 hours. I have some
- Jakob Ovrum (26/46) Jul 09 2012 This is like the third time I bring up this example around this
- Walter Bright (5/9) Jul 10 2012 I understand, but the downside of not making these functions const is it...
- Timon Gehr (20/33) Jul 10 2012 Making these functions const will torpedo the use of OO style
- Walter Bright (13/38) Jul 10 2012 PIMPL means pointer to implementation. Your const struct is just a wrapp...
- Timon Gehr (7/54) Jul 10 2012 In @safe code there are none. There are also simple workarounds for the
- Walter Bright (9/27) Jul 10 2012 Functional means a guarantee against mutability. Faith-based functional
- Timon Gehr (29/63) Jul 10 2012 Only guaranteed referential transparency is necessary. Eg, 'fun' in the
- Timon Gehr (2/14) Jul 10 2012 (conceptually, the structures are immutable and infinite.)
- Walter Bright (4/5) Jul 10 2012 I understand, and those are useful constructs. Something has to give som...
- Walter Bright (5/6) Jul 10 2012 What a minute - how are you doing toHash for an infinite structure?
- Timon Gehr (8/14) Jul 10 2012 I am not. The structure is used to obtain unique representations for
- Walter Bright (2/20) Jul 10 2012 How about create a new instance of a struct when a new representation is...
- Walter Bright (10/34) Jul 10 2012 Purity also means not reading mutable global state. Also, it is not prac...
- Timon Gehr (16/54) Jul 10 2012 It could in principle ensure it by providing a language feature that
- Andrei Alexandrescu (4/8) Jul 10 2012 How about we consider just stiffening that upper lip and implement
- Timon Gehr (3/11) Jul 10 2012 It is promising that they never will in any derived class with no
- Jakob Ovrum (10/20) Jul 10 2012 It's more likely to go down like this: programmer attempts to
- H. S. Teoh (9/29) Jul 10 2012 This is exactly what I was saying. All that beautiful, pristine, perfect
- Andrei Alexandrescu (4/30) Jul 11 2012 How often do you need memoization? It's not even recognized by this
- Timon Gehr (4/35) Jul 11 2012 Once is sufficient. It will invade the code base because const is
- Andrei Alexandrescu (6/24) Jul 11 2012 I gave evidence on a large, high quality C++ codebase that the use of
- Timon Gehr (18/42) Jul 11 2012 Unlike in D, in C++ programmers have a choice of declaring a method
- Max Samukha (8/13) Jul 11 2012 Qt codebase (large, high quality) has around 1500 'mutable'
- Andrei Alexandrescu (3/15) Jul 11 2012 How many lines total?
- Max Samukha (8/29) Jul 11 2012 1512. Obtained by grepping the src directory of Qt 4.8.0, so the
- Andrei Alexandrescu (4/29) Jul 11 2012 I was asking about total number of C++ code lines. In our codebase we
- Max Samukha (6/46) Jul 11 2012 Sorry, I misunderstood the question. As I said earlier, the
- Caligo (5/8) Jul 11 2012 About 2 million in C++.
- Andrei Alexandrescu (4/7) Jul 11 2012 I agree that 1500 occurrences of "mutable" in 2 million lines of C++ is
- Era Scarecrow (20/27) Jul 11 2012 I remember offering a suggested structure for cached hashing for
- Andrei Alexandrescu (5/7) Jul 10 2012 I think that's not representing @trusted quite accurately. There's no
- Walter Bright (3/9) Jul 10 2012 You're right in that it would break immutable args passed.
- Andrei Alexandrescu (16/28) Jul 11 2012 I am pretty sure your approach with PIMPL is also undefined, so we may
- Andrei Alexandrescu (3/19) Jul 10 2012 That's @undefined, not @trusted.
- Jacob Carlborg (4/7) Jul 11 2012 Can't there be non-const versions of these methods as well?
- Andrei Alexandrescu (3/14) Jul 10 2012 s/make this @trusted/never use this/
- Andrei Alexandrescu (3/6) Jul 10 2012 s/straightforward/awful/
- Jakob Ovrum (21/29) Jul 10 2012 Honestly, I think the ideal would be to give people the
- Jonathan M Davis (49/70) Jul 10 2012 Yeah. It seems to me that a reasonable approach would be to give Object ...
- Jakob Ovrum (7/16) Jul 10 2012 This is exactly the kind of balance I am hoping we can implement.
- Jakob Ovrum (8/26) Jul 10 2012 The relevant discussion containing the proposal is here:
- Andrei Alexandrescu (14/29) Jul 11 2012 I was a long-time proponent of this. It's less exciting than it may seem...
- Jakob Ovrum (12/26) Jul 11 2012 This solution is not for allowing people to use lazy computation
- deadalnix (6/28) Jul 11 2012 In this case, they have function that does something else than compare
- Timon Gehr (3/34) Jul 11 2012 I think this posts roots in a misunderstanding of 'cannot and should not...
- deadalnix (3/38) Jul 11 2012 If it cannot and should not be const, it isn't a comparison or an
- Jakob Ovrum (3/6) Jul 11 2012 That's bullshit. You've seen the example I raised. How can you
- deadalnix (5/10) Jul 11 2012 To be fair, I know nothing about LUA.
- Jakob Ovrum (12/18) Jul 11 2012 This is irrelevant. The Lua API is a C library like any other.
- deadalnix (3/9) Jul 11 2012 I don't say you have to. I say, you may need to. Because the language
- H. S. Teoh (29/41) Jul 11 2012 It *is* a problem when you're talking about abstractions. If I have
- Andrei Alexandrescu (3/12) Jul 11 2012 How about the static (h|c)ache??
- Andrei Alexandrescu (3/12) Jul 11 2012 How about the static hash/cache?
- Timon Gehr (3/15) Jul 11 2012 Will break horribly as soon as it is discovered that the methods should
- Andrei Alexandrescu (3/20) Jul 11 2012 I don't think they should be pure. Do you have reasons to think otherwis...
- deadalnix (4/25) Jul 11 2012 I think they should. Comparing the same object 2 time should
- Andrei Alexandrescu (3/6) Jul 11 2012 Yah, but e.g. a comparison may log something.
- H. S. Teoh (9/15) Jul 11 2012 [...]
- Andrei Alexandrescu (3/13) Jul 11 2012 Logging is not debugging.
- Timon Gehr (11/32) Jul 11 2012 I think they should be pure as much as I think they should be const.
- Andrei Alexandrescu (4/14) Jul 11 2012 The essential difference is there's no "pure" object, so there's no
- Timon Gehr (6/27) Jul 11 2012 oops. I meant to write "We have pure functions that want to use the
- Jonathan M Davis (5/6) Jul 11 2012 As I understand it, Walter's current plan is to require that opEquals, o...
- travert phare.normalesup.org (Christophe Travert) (3/9) Jul 12 2012 And is the plan add each tag one by one, breaking codes in many places
- Jonathan M Davis (25/36) Jul 12 2012 ls,
- deadalnix (3/26) Jul 11 2012 Either the structure is mutable, and you have to recompute anyway, or
- Andrei Alexandrescu (25/52) Jul 11 2012 I think I'll find it rather difficult to get behind modeling arbitrary
- Timon Gehr (2/3) Jul 11 2012 If it cannot be modelled, it is not a possible design.
- Andrei Alexandrescu (3/8) Jul 11 2012 There is if it can be modeled in a less constrained language.
- Jakob Ovrum (16/43) Jul 11 2012 Some classes don't lend themselves to immutability. Let's take
- Andrei Alexandrescu (4/7) Jul 11 2012 This is a good point. It seems we're subjecting all classes to certain
- deadalnix (7/14) Jul 11 2012 Did you saw the proposal of feep/tgehr on #d ?
- H. S. Teoh (10/28) Jul 11 2012 +1, very good, I like this idea!
- Roman D. Boiko (3/14) Jul 11 2012 It is a trade-off, but relatively nice one, IMO.
- Andrei Alexandrescu (3/19) Jul 11 2012 Haven't seen that, but on first look it seems promising.
- Steven Schveighoffer (23/41) Jul 11 2012 I don't like this idea. It means you could not use pure functions to
- deadalnix (3/46) Jul 11 2012 I see no problem here. x isn't pure, so it isn't required to give back
- Steven Schveighoffer (9/64) Jul 11 2012 The expectation is that x belongs to the class, not elsewhere. In fact,...
- Roman D. Boiko (8/28) Jul 11 2012 On Wednesday, 11 July 2012 at 18:21:24 UTC, Steven Schveighoffer
- Steven Schveighoffer (8/19) Jul 11 2012 Another abuse:
- deadalnix (3/25) Jul 11 2012 This shouldn't be a compiler error. An object of type B is 100% of time
- Steven Schveighoffer (5/35) Jul 11 2012 According to my code, b is const, and never was mutable.
- deadalnix (5/42) Jul 11 2012 Const is a bridge between mutable and immutable world. The guarantee
- Steven Schveighoffer (6/10) Jul 12 2012 No. Const is a contract saying the function will not modify the data
- David Piepgrass (15/54) Jul 11 2012 I do like the idea. Please explain by example why a pure function
- travert phare.normalesup.org (Christophe Travert) (4/9) Jul 12 2012 A fun() pure;
- Steven Schveighoffer (7/16) Jul 12 2012 it :
- H. S. Teoh (7/14) Jul 11 2012 [...]
- Andrei Alexandrescu (3/14) Jul 11 2012 I didn't see that, sorry. What's the closest quote?
- Timon Gehr (6/30) Jul 11 2012 This is rather close, if put less eloquently:
- travert phare.normalesup.org (Christophe Travert) (17/24) Jul 11 2012 Does Object really need to implement opEquals/opHash/... ? This is what
- deadalnix (13/40) Jul 11 2012 The cached value can in fact rely outside the object.
- Jakob Ovrum (2/3) Jul 11 2012 For the umpteenth time, I am not talking about caching.
- Jakob Ovrum (21/29) Jul 10 2012 Honestly, I think the ideal would be to give people the
- Steven Schveighoffer (8/22) Jul 11 2012 Two solutions:
- Eyyub (1/1) Jul 09 2012 Congrats !
- mta`chrono (1/1) Jul 09 2012 Great work!!! Thanks guys!
- Jonathan M Davis (23/25) Jul 10 2012 I completely disagree at least as far as classes go. opEquals, opCmp,
- deadalnix (2/10) Jul 11 2012 That is pretty clear and I couldn't agree more.
- H. S. Teoh (28/35) Jul 10 2012 [...]
- Jakob Ovrum (22/28) Jul 10 2012 I don't think the answer is to change D's const. D's const,
- H. S. Teoh (21/52) Jul 10 2012 I don't think we want to change physical (bitwise) const. It does have
- Walter Bright (4/9) Jul 10 2012 There's a gigantic problem with logical const. It is completely unenforc...
- H. S. Teoh (16/28) Jul 10 2012 [...]
- Walter Bright (2/27) Jul 10 2012 Logical const is still mutating const members.
- H. S. Teoh (17/33) Jul 10 2012 [...]
- Walter Bright (3/34) Jul 10 2012 If you've found a way to mutate const and have it stay const, then there...
- H. S. Teoh (10/32) Jul 10 2012 The const in const(B) applies to the B portion of the object. Just
- Andrei Alexandrescu (4/6) Jul 10 2012 No. This is dogma devoid of substance. Maybe you found a way to
- Timon Gehr (7/17) Jul 10 2012 Const is stronger than what is required to bridge the gap between
- Walter Bright (8/14) Jul 10 2012 If you have a const function that accepts both mutable and immutable arg...
- Timon Gehr (28/43) Jul 10 2012 I understand. The trick is to disallow creating immutable instances of a...
- Jacob Carlborg (17/28) Jul 11 2012 Ruby 1.9 is doing something similar.
- deadalnix (4/24) Jul 11 2012 It is interesting. But is does transfers the constraint on const on
- SomeDude (3/15) Jul 15 2012 At first sight, I think this is an interesting idea. Maybe the
- Andrei Alexandrescu (7/22) Jul 10 2012 Lazy computation does not produce detectable change.
- Andrei Alexandrescu (16/26) Jul 10 2012 I think we need to unlock that mindset lest we risk short-circuiting all...
- Timon Gehr (3/31) Jul 10 2012 This is unfortunately easily explained using the fact that const is not
- Jakob Ovrum (31/62) Jul 10 2012 The purpose of the recent change is to allow operations like
- Andrei Alexandrescu (15/16) Jul 10 2012 There is, only thing is there's no easy way (without adding some amount
- travert phare.normalesup.org (Christophe Travert) (39/49) Jul 11 2012 I think this is a good idea, but for classes, inheritance is an issue.
- Jonathan M Davis (17/26) Jul 10 2012 There may be. It's an open question. For opEquals, it was suggested (by ...
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
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
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
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 futureI 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
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
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 DavisI have to agree here, but it is not very practical. It would require people full time on D.
Jul 09 2012
On 7/9/12 3:52 AM, Jonathan M Davis wrote:On Sunday, July 08, 2012 23:37:29 Adam Wilson wrote:Giving me credit for this is like giving credit the cable for electricity. I just let the great work of others flow.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.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. AndreiAliases 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 futureI 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.
Jul 09 2012
On Monday, July 09, 2012 10:01:25 Andrei Alexandrescu wrote:On 7/9/12 3:52 AM, Jonathan M Davis wrote: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 DavisOn Sunday, July 08, 2012 23:37:29 Adam Wilson wrote:Giving me credit for this is like giving credit the cable for electricity. I just let the great work of others flow.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.
Jul 09 2012
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: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/On 7/9/12 3:52 AM, Jonathan M Davis wrote: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 DavisOn Sunday, July 08, 2012 23:37:29 Adam Wilson wrote:mergingFirst 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 andmost 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.
Jul 09 2012
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
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
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
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
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
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
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
On Mon, Jul 09, 2012 at 01:44:24PM +0200, Timon Gehr wrote:On 07/09/2012 08:37 AM, Adam Wilson wrote:[...] 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.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.
Jul 09 2012
On 07/09/2012 05:00 PM, H. S. Teoh wrote:On Mon, Jul 09, 2012 at 01:44:24PM +0200, Timon Gehr 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.On 07/09/2012 08:37 AM, Adam Wilson wrote:[...] Can you give an explicit example of code that is harmed by const correctness?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.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
On Monday, 9 July 2012 at 16:02:38 UTC, Timon Gehr wrote:On 07/09/2012 05:00 PM, H. S. Teoh wrote: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?On Mon, Jul 09, 2012 at 01:44:24PM +0200, Timon Gehr 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.On 07/09/2012 08:37 AM, Adam Wilson wrote:[...] Can you give an explicit example of code that is harmed by const correctness?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.
Jul 09 2012
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
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
On 07/09/2012 07:49 PM, Andrei Alexandrescu wrote:On 7/9/12 12:34 PM, David Piepgrass wrote:This is very inconvenient if the root of the class hierarchy uses it.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.
Jul 09 2012
On Mon, Jul 09, 2012 at 08:07:22PM +0200, Timon Gehr wrote:On 07/09/2012 07:49 PM, Andrei Alexandrescu 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). 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.On 7/9/12 12:34 PM, David Piepgrass wrote:This is very inconvenient if the root of the class hierarchy uses it.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.
Jul 09 2012
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
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
On Tuesday, 10 July 2012 at 01:41:29 UTC, bearophile wrote:David Piepgrass: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.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++.
Jul 09 2012
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
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
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
On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote:On 07/10/2012 06:45 PM, 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.? 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.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
On 10/07/12 19:13, H. S. Teoh wrote:On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote: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.On 07/10/2012 06:45 PM, 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.? 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. TYeah, this is logical const. Unfortunately, D doesn't have logical const.Then why on earth is druntime acting as if it does?
Jul 11 2012
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
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
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
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. AndreiWell, it is inefficient.
Jul 11 2012
On 7/11/12 10:33 AM, Timon Gehr wrote:The idea here is to assess functionality provided in order to decide whether to go down this route or not. AndreiAny takers for Cached? It would be good to assess its level of usefulness first. AndreiWell, it is inefficient.
Jul 11 2012
On Wednesday, 11 July 2012 at 14:42:43 UTC, Andrei Alexandrescu wrote:On 7/11/12 10:33 AM, Timon Gehr wrote:Probably using a separate thread would be better for voting, since this one already has many branches of ideas.The idea here is to assess functionality provided in order to decide whether to go down this route or not. AndreiAny takers for Cached? It would be good to assess its level of usefulness first. AndreiWell, it is inefficient.
Jul 11 2012
On Wednesday, 11 July 2012 at 14:42:43 UTC, Andrei Alexandrescu wrote:On 7/11/12 10:33 AM, Timon Gehr wrote:Probably using a separate thread would be better for voting or discussing, since this one already has many branches of ideas.The idea here is to assess functionality provided in order to decide whether to go down this route or not. AndreiAny takers for Cached? It would be good to assess its level of usefulness first. AndreiWell, it is inefficient.
Jul 11 2012
Uh, it better not be proportional. Hashtable gives us O(1), one hopes.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);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
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
"David Piepgrass" , dans le message (digitalmars.D:172009), a écrit :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.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 12 2012
On Jul 11, 2012, at 12:58 AM, Don Clugston <dac nospam.com> wrote:On 10/07/12 19:13, H. S. Teoh wrote:ealing with?On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote:=20 I think you're right. Something I wonder about, though, is how many different use cases are we d=On 07/10/2012 06:45 PM, H. S. Teoh wrote:=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 TYeah, 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 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
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: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 DavisOn 07/10/2012 06:45 PM, 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.?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
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:[...]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.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.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
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.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 GeoffIME, it rather helps code quality than harm it.I am not talking about code quality. I am talking about code maintainability, extensibility and performance.
Jul 09 2012
On 07/10/2012 01:57 AM, Geoffrey Biggs wrote:On 10 July 2012 01:02, Timon Gehr<timon.gehr gmx.ch> wrote:Exactly! I don't think that the term 'const correctness' actually has a meaning in D. It is a C++ term.... 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
Jul 09 2012
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
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
On 11/07/2012 16:04, Timon Gehr wrote:On 07/11/2012 02:58 PM, deadalnix wrote: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).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
On 11/07/2012 16:16, deadalnix wrote:On 11/07/2012 16:04, Timon Gehr wrote:By the way, I'd be happy to be proven false, but I think the issue is overstated here.On 07/11/2012 02:58 PM, deadalnix wrote: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).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
On 07/11/2012 04:16 PM, deadalnix wrote:On 11/07/2012 16:16, deadalnix wrote: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.On 11/07/2012 16:04, Timon Gehr wrote:By the way, I'd be happy to be proven false, but I think the issue is overstated here.On 07/11/2012 02:58 PM, deadalnix wrote: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).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
On 07/11/2012 04:16 PM, deadalnix wrote:On 11/07/2012 16:04, Timon Gehr wrote:Abstraction is a software design problem?On 07/11/2012 02:58 PM, deadalnix wrote:Arguably, this is a software design problem.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.I never encountered a case where I need to do such thingThis 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
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: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.On 07/09/2012 08:37 AM, Adam Wilson wrote:[...] 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. TObject 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.
Jul 09 2012
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
On 07/11/2012 12:05 AM, Walter Bright wrote:On 7/9/2012 3:27 PM, Jakob Ovrum wrote: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.)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.I didn't get that.
Jul 10 2012
On 7/10/2012 3:49 PM, Timon Gehr wrote:No, it won't, there are simple workarounds.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.)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.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
On 07/11/2012 01:10 AM, Walter Bright wrote:On 7/10/2012 3:49 PM, Timon Gehr wrote:In safe code there are none. There are also simple workarounds for the functional style programming case: just do not annotate.No, it won't, there are simple workarounds.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.I consider those points important.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.)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.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.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
On 7/10/2012 4:29 PM, Timon Gehr wrote:On 07/11/2012 01:10 AM, Walter Bright wrote:Right. That's what I meant when you have to add trusted.On 7/10/2012 3:49 PM, Timon Gehr wrote:In safe code there are none.No, it won't, there are simple workarounds.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.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.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.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
On 07/11/2012 02:03 AM, Walter Bright wrote:On 7/10/2012 4:29 PM, Timon Gehr wrote: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); }On 07/11/2012 01:10 AM, Walter Bright wrote:Right. That's what I meant when you have to add trusted.On 7/10/2012 3:49 PM, Timon Gehr wrote:In safe code there are none.No, it won't, there are simple workarounds.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.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.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 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.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.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.Consider that in the light of what logical const actually is - it offers no guarantees whatsoever.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.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
On 07/11/2012 03:04 AM, Timon Gehr wrote:On 07/11/2012 02:03 AM, Walter Bright wrote:(conceptually, the structures are immutable and infinite.)... 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.
Jul 10 2012
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
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
On 07/11/2012 04:30 AM, Walter Bright wrote:On 7/10/2012 6:07 PM, Timon Gehr wrote: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.(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
On 7/10/2012 7:42 PM, Timon Gehr wrote:On 07/11/2012 04:30 AM, Walter Bright wrote:How about create a new instance of a struct when a new representation is requested?On 7/10/2012 6:07 PM, Timon Gehr wrote: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.(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
On 7/10/2012 6:04 PM, Timon Gehr wrote: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.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); }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.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.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
On 07/11/2012 03:14 AM, Walter Bright wrote:On 7/10/2012 6:04 PM, Timon Gehr wrote: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.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.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); }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.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.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.I cannot reconcile that with a desire for logical constness, which is completely uncheckable.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.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
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
On 07/11/2012 04:02 AM, Andrei Alexandrescu wrote:On 7/10/12 9:45 PM, Timon Gehr wrote:It is promising that they never will in any derived class with no obvious way out for no benefit that I struggle with.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
On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote:On 7/10/12 9:45 PM, Timon Gehr wrote: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 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
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: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.On 7/10/12 9:45 PM, Timon Gehr wrote: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 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
On 7/11/12 12:59 AM, H. S. Teoh wrote:On Wed, Jul 11, 2012 at 04:59:28AM +0200, Jakob Ovrum wrote:How often do you need memoization? It's not even recognized by this mailer's editor. AndreiOn Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote: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.On 7/10/12 9:45 PM, Timon Gehr wrote: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 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 11 2012
On 07/11/2012 02:40 PM, Andrei Alexandrescu wrote:On 7/11/12 12:59 AM, H. S. Teoh wrote:Once is sufficient. It will invade the code base because const is transitive.On Wed, Jul 11, 2012 at 04:59:28AM +0200, Jakob Ovrum wrote:How often do you need memoization?On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote: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.On 7/10/12 9:45 PM, Timon Gehr wrote: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 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? AndreiIt's not even recognized by this mailer's editor. Andrei'lazy computation' is.
Jul 11 2012
On 7/10/12 10:59 PM, Jakob Ovrum wrote:On Wednesday, 11 July 2012 at 02:02:52 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? AndreiOn 7/10/12 9:45 PM, Timon Gehr wrote: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 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 11 2012
On 07/11/2012 02:39 PM, Andrei Alexandrescu wrote:On 7/10/12 10:59 PM, Jakob Ovrum wrote: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.)On Wednesday, 11 July 2012 at 02:02:52 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.On 7/10/12 9:45 PM, Timon Gehr wrote: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 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? AndreiWhat 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
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? AndreiQt 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
On 7/11/12 11:11 AM, Max Samukha wrote:On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei Alexandrescu wrote:How many lines total? AndreiI 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? AndreiQt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
Jul 11 2012
On Wednesday, 11 July 2012 at 15:28:41 UTC, Andrei Alexandrescu wrote:On 7/11/12 11:11 AM, Max Samukha wrote: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.On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei Alexandrescu wrote:How many lines total? AndreiI 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? AndreiQt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
Jul 11 2012
On 7/11/12 11:50 AM, Max Samukha wrote:On Wednesday, 11 July 2012 at 15:28:41 UTC, Andrei Alexandrescu wrote: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). AndreiOn 7/11/12 11:11 AM, Max Samukha wrote: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.On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei Alexandrescu wrote:How many lines total? AndreiI 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? AndreiQt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
Jul 11 2012
On Wednesday, 11 July 2012 at 15:57:51 UTC, Andrei Alexandrescu wrote:On 7/11/12 11:50 AM, Max Samukha wrote: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.On Wednesday, 11 July 2012 at 15:28:41 UTC, Andrei Alexandrescu wrote: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). AndreiOn 7/11/12 11:11 AM, Max Samukha wrote: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.On Wednesday, 11 July 2012 at 12:39:03 UTC, Andrei Alexandrescu wrote:How many lines total? AndreiI 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? AndreiQt codebase (large, high quality) has around 1500 'mutable' keyword occurrences.
Jul 11 2012
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
On 7/11/12 11:33 AM, Caligo wrote:About 2 million in C++. source: http://www.ohloh.net/p/qt/analyses/latest/languages_summaryI agree that 1500 occurrences of "mutable" in 2 million lines of C++ is quite large. Andrei
Jul 11 2012
On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote:On 7/10/12 9:45 PM, Timon Gehr wrote: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; } }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?
Jul 11 2012
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
On 7/10/2012 6:53 PM, Andrei Alexandrescu wrote:On 7/10/12 9:14 PM, Walter Bright wrote:You're right in that it would break immutable args passed. PIMPL is a better option.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.
Jul 10 2012
On 7/10/12 10:22 PM, Walter Bright wrote:On 7/10/2012 6:53 PM, Andrei Alexandrescu wrote: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. AndreiOn 7/10/12 9:14 PM, Walter Bright wrote:You're right in that it would break immutable args passed. PIMPL is a better option.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.
Jul 11 2012
On 7/10/12 8:03 PM, Walter Bright wrote:On 7/10/2012 4:29 PM, Timon Gehr wrote:That's undefined, not trusted. AndreiOn 07/11/2012 01:10 AM, Walter Bright wrote:Right. That's what I meant when you have to add trusted.On 7/10/2012 3:49 PM, Timon Gehr wrote:In safe code there are none.No, it won't, there are simple workarounds.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.
Jul 10 2012
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
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
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
On Wednesday, 11 July 2012 at 01:19:16 UTC, Andrei Alexandrescu wrote:On 7/10/12 6:05 PM, Walter Bright 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.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
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
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 DavisThis 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
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: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-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 DavisThis 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
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
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. AndreiThis 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
On 11/07/2012 17:03, Jakob Ovrum wrote:On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu wrote: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 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. AndreiThis 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.
Jul 11 2012
On 07/11/2012 05:27 PM, deadalnix wrote:On 11/07/2012 17:03, Jakob Ovrum wrote:I think this posts roots in a misunderstanding of 'cannot and should not be const'.On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu wrote: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 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. AndreiThis 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.
Jul 11 2012
On 11/07/2012 17:49, Timon Gehr wrote:On 07/11/2012 05:27 PM, 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.On 11/07/2012 17:03, Jakob Ovrum wrote:I think this posts roots in a misunderstanding of 'cannot and should not be const'.On Wednesday, 11 July 2012 at 12:36:43 UTC, Andrei Alexandrescu wrote: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 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. AndreiThis 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.
Jul 11 2012
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
On 11/07/2012 19:32, Jakob Ovrum wrote:On Wednesday, 11 July 2012 at 15:59:21 UTC, deadalnix wrote: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.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
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
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
On Wed, Jul 11, 2012 at 05:27:01PM +0200, deadalnix wrote:On 11/07/2012 17:03, Jakob Ovrum 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. 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 userThis 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
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
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
On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:On 7/11/12 12:30 PM, H. S. Teoh wrote:Will break horribly as soon as it is discovered that the methods should be pure as well.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
On 7/11/12 1:04 PM, Timon Gehr wrote:On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:I don't think they should be pure. Do you have reasons to think otherwise? AndreiOn 7/11/12 12:30 PM, H. S. Teoh wrote:Will break horribly as soon as it is discovered that the methods should be pure as well.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
On 11/07/2012 19:46, Andrei Alexandrescu wrote:On 7/11/12 1:04 PM, Timon Gehr 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.On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:I don't think they should be pure. Do you have reasons to think otherwise? AndreiOn 7/11/12 12:30 PM, H. S. Teoh wrote:Will break horribly as soon as it is discovered that the methods should be pure as well.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
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
On Wed, Jul 11, 2012 at 01:50:23PM -0400, Andrei Alexandrescu wrote:On 7/11/12 1:49 PM, deadalnix wrote:[...] 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 LongI 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.
Jul 11 2012
On 7/11/12 2:05 PM, H. S. Teoh wrote:On Wed, Jul 11, 2012 at 01:50:23PM -0400, Andrei Alexandrescu wrote:Logging is not debugging. AndreiOn 7/11/12 1:49 PM, deadalnix wrote:[...] I think it's a given that purity is out the window once debugging is involved.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.
Jul 11 2012
On 07/11/2012 07:46 PM, Andrei Alexandrescu wrote:On 7/11/12 1:04 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)On 07/11/2012 06:45 PM, Andrei Alexandrescu wrote:I don't think they should be pure. Do you have reasons to think otherwise? AndreiOn 7/11/12 12:30 PM, H. S. Teoh wrote:Will break horribly as soon as it is discovered that the methods should be pure as well.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
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
On 07/11/2012 08:17 PM, Andrei Alexandrescu wrote:On 7/11/12 2:03 PM, Timon Gehr wrote:oops. I meant to write "We have pure functions that want to use the methods."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.auto foo(Object o)pure{ // o is a "pure" object here }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
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
"Jonathan M Davis" , dans le message (digitalmars.D:172005), a écrit :On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:And is the plan add each tag one by one, breaking codes in many places each time ?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.
Jul 12 2012
On Thursday, July 12, 2012 07:31:02 Christophe Travert wrote:"Jonathan M Davis" , dans le message (digitalmars.D:172005), a =C3=A9=crit :ls,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=sopCmp, 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=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
On 11/07/2012 18:30, H. S. Teoh wrote:On Wed, Jul 11, 2012 at 05:27:01PM +0200, deadalnix wrote:Either the structure is mutable, and you have to recompute anyway, or the structure is trully const and thing can be computed up-front.On 11/07/2012 17:03, Jakob Ovrum 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.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
On 7/11/12 11:03 AM, Jakob Ovrum wrote:On Wednesday, 11 July 2012 at 12:36:43 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. 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. AndreiI 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. AndreiThis 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
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
On 7/11/12 12:12 PM, Timon Gehr wrote:On 07/11/2012 05:27 PM, Andrei Alexandrescu wrote:There is if it can be modeled in a less constrained language. AndreiWe can't really model every possible design.If it cannot be modelled, it is not a possible design.
Jul 11 2012
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
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
On 11/07/2012 19:49, Andrei Alexandrescu wrote:On 7/11/12 1:40 PM, Jakob Ovrum 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.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
On Wed, Jul 11, 2012 at 08:01:44PM +0200, deadalnix wrote:On 11/07/2012 19:49, Andrei Alexandrescu wrote:+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."On 7/11/12 1:40 PM, Jakob Ovrum 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.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
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:...It is a trade-off, but relatively nice one, IMO.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!
Jul 11 2012
On 7/11/12 2:01 PM, deadalnix wrote:On 11/07/2012 19:49, Andrei Alexandrescu wrote:Haven't seen that, but on first look it seems promising. AndreiOn 7/11/12 1:40 PM, Jakob Ovrum 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.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
On Wed, 11 Jul 2012 14:01:44 -0400, deadalnix <deadalnix gmail.com> wrote:On 11/07/2012 19:49, Andrei Alexandrescu wrote: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. -SteveOn 7/11/12 1:40 PM, Jakob Ovrum 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.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
On 11/07/2012 20:21, Steven Schveighoffer wrote:On Wed, 11 Jul 2012 14:01:44 -0400, deadalnix <deadalnix gmail.com> wrote: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.On 11/07/2012 19:49, Andrei Alexandrescu wrote: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. -SteveOn 7/11/12 1:40 PM, Jakob Ovrum 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.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
On Wed, 11 Jul 2012 14:29:28 -0400, deadalnix <deadalnix gmail.com> wrote:On 11/07/2012 20:21, Steven Schveighoffer wrote: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. -SteveOn Wed, 11 Jul 2012 14:01:44 -0400, deadalnix <deadalnix gmail.com> wrote: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.On 11/07/2012 19:49, Andrei Alexandrescu wrote: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. -SteveOn 7/11/12 1:40 PM, Jakob Ovrum 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.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
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. -SteveI 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
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
On 11/07/2012 20:53, Steven Schveighoffer wrote:On Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer <schveiguy yahoo.com> wrote:This shouldn't be a compiler error. An object of type B is 100% of time mutable now.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
On Wed, 11 Jul 2012 15:14:19 -0400, deadalnix <deadalnix gmail.com> wrote:On 11/07/2012 20:53, Steven Schveighoffer wrote: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. -SteveOn Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer <schveiguy yahoo.com> wrote:This shouldn't be a compiler error. An object of type B is 100% of time mutable now.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
On 11/07/2012 21:45, Steven Schveighoffer wrote:On Wed, 11 Jul 2012 15:14:19 -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.On 11/07/2012 20:53, Steven Schveighoffer wrote: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. -SteveOn Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer <schveiguy yahoo.com> wrote:This shouldn't be a compiler error. An object of type B is 100% of time mutable now.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
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
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: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.On 11/07/2012 19:49, Andrei Alexandrescu wrote: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).On 7/11/12 1:40 PM, Jakob Ovrum 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.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. AndreiIt 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
"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
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 :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. -Stevemutating 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
On Wed, Jul 11, 2012 at 01:49:53PM -0400, Andrei Alexandrescu wrote:On 7/11/12 1:40 PM, Jakob Ovrum wrote:[...] Yes, that's what we've been trying to say for the last, oh, 100 messages? ;-) T -- Дерево держитÑÑ ÐºÐ¾Ñ€Ð½Ñми, а человек - друзьÑми.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.
Jul 11 2012
On 7/11/12 2:07 PM, H. S. Teoh wrote:On Wed, Jul 11, 2012 at 01:49:53PM -0400, Andrei Alexandrescu wrote:I didn't see that, sorry. What's the closest quote? AndreiOn 7/11/12 1:40 PM, Jakob Ovrum wrote:[...] Yes, that's what we've been trying to say for the last, oh, 100 messages? ;-)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.
Jul 11 2012
On 07/11/2012 08:18 PM, Andrei Alexandrescu wrote:On 7/11/12 2:07 PM, H. S. Teoh wrote:This is rather close, if put less eloquently: On 07/11/2012 03:45 AM, Timon Gehr wrote:On Wed, Jul 11, 2012 at 01:49:53PM -0400, Andrei Alexandrescu wrote:I didn't see that, sorry. What's the closest quote? AndreiOn 7/11/12 1:40 PM, Jakob Ovrum wrote:[...] Yes, that's what we've been trying to say for the last, oh, 100 messages? ;-)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.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
Andrei Alexandrescu , dans le message (digitalmars.D:171945), a écrit :On 7/11/12 1:40 PM, Jakob Ovrum wrote: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 ? -- ChristopheSome 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.
Jul 11 2012
On 11/07/2012 03:30, Jakob Ovrum wrote:On Wednesday, 11 July 2012 at 01:19:16 UTC, Andrei Alexandrescu wrote: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.On 7/10/12 6:05 PM, Walter Bright 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.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 11 2012
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
On Wednesday, 11 July 2012 at 01:19:16 UTC, Andrei Alexandrescu wrote:On 7/10/12 6:05 PM, Walter Bright 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.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
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: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. -SteveObject 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 11 2012
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
On 10/07/2012 22:11, Jonathan M Davis wrote:On Tuesday, July 10, 2012 12:00:59 H. S. Teoh wrote:That is pretty clear and I couldn't agree more.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.
Jul 11 2012
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:[...] 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.comI 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.
Jul 10 2012
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? TI 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
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: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.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? TI 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.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
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
On Tue, Jul 10, 2012 at 03:39:30PM -0700, Walter Bright wrote:On 7/10/2012 3:13 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. T -- Give me some fresh salted fish, please.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
On 7/10/2012 4:05 PM, H. S. Teoh wrote:On Tue, Jul 10, 2012 at 03:39:30PM -0700, Walter Bright wrote:Logical const is still mutating const members.On 7/10/2012 3:13 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.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
On Tue, Jul 10, 2012 at 04:05:51PM -0700, Walter Bright wrote:On 7/10/2012 4:05 PM, H. S. Teoh wrote:[...][...] 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?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
On 7/10/2012 4:19 PM, H. S. Teoh wrote:On Tue, Jul 10, 2012 at 04:05:51PM -0700, 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.On 7/10/2012 4:05 PM, H. S. Teoh wrote:[...][...] 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. TWhat 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
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: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".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. TIf 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
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
On 07/11/2012 12:39 AM, Walter Bright wrote:On 7/10/2012 3:13 PM, H. S. Teoh 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.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
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
On 07/11/2012 01:57 AM, Walter Bright wrote:On 7/10/2012 4:16 PM, Timon Gehr wrote: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'.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.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
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
On 11/07/2012 02:20, Timon Gehr wrote:On 07/11/2012 01:57 AM, Walter Bright wrote: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.On 7/10/2012 4:16 PM, Timon Gehr wrote: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'.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.
Jul 11 2012
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
On 7/10/12 7:57 PM, Walter Bright wrote:On 7/10/2012 4:16 PM, Timon Gehr wrote:It can if immutable(T) is invalid.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.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
On 7/10/12 6:39 PM, Walter Bright wrote:On 7/10/2012 3:13 PM, H. S. Teoh wrote: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. AndreiI 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
On 07/11/2012 03:43 AM, Andrei Alexandrescu wrote:On 7/10/12 6:39 PM, Walter Bright wrote:This is unfortunately easily explained using the fact that const is not transitive in C++.On 7/10/2012 3:13 PM, H. S. Teoh wrote: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. AndreiI 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
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. TI 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
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
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
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