digitalmars.D - Why are opEquals/opCmp arguments not in or const for Objects?
- Peter Williams (19/19) Mar 17 2013 The current signatures for opCmp/opEqual mean that code like:
- Timon Gehr (4/21) Mar 17 2013 Because not all valid implementations can be. They shouldn't be in
- Timon Gehr (2/29) Mar 17 2013
- Jonathan M Davis (11/16) Mar 17 2013 Yeah. It was agreed that opCmp, opEquals, toString, and toHash would be
- Stewart Gordon (11/27) Mar 17 2013 What are these "unnecessary issues with const" they cause?
- Jonathan M Davis (24/26) Mar 17 2013 Because const in D is physical const, not logical const. So, for instanc...
- Paulo Pinto (14/46) Mar 18 2013 Even on Java's case and as extent .NET they aren't strictly
-
Stewart Gordon
(13/29)
Mar 18 2013
- Timon Gehr (2/6) Mar 18 2013 That's a C++ term and it is not applicable to D.
- Jonathan M Davis (8/16) Mar 18 2013 I wouldn't go that far. I think that the term is still quite applicable ...
- Jonathan M Davis (12/45) Mar 18 2013 It doesn't work with pure as it forces you to put state outside of the o...
- Stewart Gordon (19/26) Mar 18 2013 Why can't it be used as a means of lazy initialization?
- Jonathan M Davis (15/32) Mar 18 2013 Because if one of the member variables hasn't been initialized yet, then...
- Stewart Gordon (23/30) Mar 18 2013 You miss the whole point of what I'm saying. Why can't some result
- Jonathan M Davis (19/25) Mar 18 2013 It _is_ impossible if it's in the type, because you can't modify const. ...
- Jakob Ovrum (6/13) Mar 18 2013 It's worth noting that including a standard interface (as in the
- Stewart Gordon (5/10) Mar 18 2013 Can you think a use case that can't be fulfilled by using an
- Peter Williams (6/52) Mar 18 2013 Am I right in thinking that removal of these methods from Object
- Stewart Gordon (9/13) Mar 18 2013 Yes, and that's indeed a potentially good reason to remove opCmp and
- Jonathan M Davis (21/25) Mar 18 2013 Well, IIRC, if when you override a function, and you give it a different...
- Stewart Gordon (22/37) Mar 19 2013 I've just been reminding myself of what
- Jonathan M Davis (15/21) Mar 19 2013 I expect that we'll figure out how to do that when we're ready to actual...
- deadalnix (4/42) Mar 21 2013 Wouldn't that trigger bunch of false warning on legitimate
- Jonathan M Davis (19/21) Mar 21 2013 I'm not sure that overriding a deprecated function triggers a deprecatio...
- Stewart Gordon (19/34) Mar 22 2013 The problem really is that the D runtime references these methods of
- Peter Williams (8/33) Mar 18 2013 Yes. I didn't mean to imply that all casting would go away just the
- Peter Williams (7/39) Mar 17 2013 Can't those (rare) implementations just do a non constant cast
- Jonathan M Davis (4/6) Mar 17 2013 No. In D, casting away const on an object and mutating it is undefined
- Stewart Gordon (10/12) Mar 17 2013 Known bug.
- Peter Williams (4/11) Mar 17 2013 I did look in bugs but failed to find this.
- Jonathan M Davis (6/8) Mar 20 2013 By the way, this is post where it was officially decided that we'd be wo...
The current signatures for opCmp/opEqual mean that code like: bool strictly_ordered(T)(in T[] list) { for (auto j = 1; j < list.length; j++) { if (list[j - 1] >= list[j]) return false; } return true; } will fail to compile if T is a class because opCmp cannot be called with a const argument. This restricts the quality of code that can be written by limiting legitimate use of in/const arguments to functions/methods. Trying to work around this problem by defining opCmp/opEquals for the class being used with in or const argument does not work as they are not recognised as an override of the Object methods. So my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?". Thanks Peter
Mar 17 2013
On 03/18/2013 12:19 AM, Peter Williams wrote:The current signatures for opCmp/opEqual mean that code like: bool strictly_ordered(T)(in T[] list) { for (auto j = 1; j < list.length; j++) { if (list[j - 1] >= list[j]) return false; } return true; } will fail to compile if T is a class because opCmp cannot be called with a const argument. This restricts the quality of code that can be written by limiting legitimate use of in/const arguments to functions/methods. Trying to work around this problem by defining opCmp/opEquals for the class being used with in or const argument does not work as they are not recognised as an override of the Object methods.Yes they are.So my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".Because not all valid implementations can be. They shouldn't be in Object anyway.
Mar 17 2013
On 03/18/2013 01:16 AM, Timon Gehr wrote:On 03/18/2013 12:19 AM, Peter Williams wrote:Actually you are right.The current signatures for opCmp/opEqual mean that code like: bool strictly_ordered(T)(in T[] list) { for (auto j = 1; j < list.length; j++) { if (list[j - 1] >= list[j]) return false; } return true; } will fail to compile if T is a class because opCmp cannot be called with a const argument. This restricts the quality of code that can be written by limiting legitimate use of in/const arguments to functions/methods. Trying to work around this problem by defining opCmp/opEquals for the class being used with in or const argument does not work as they are not recognised as an override of the Object methods.Yes they are.So my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".Because not all valid implementations can be. They shouldn't be in Object anyway.
Mar 17 2013
On Monday, March 18, 2013 01:20:40 Timon Gehr wrote:Yeah. It was agreed that opCmp, opEquals, toString, and toHash would be removed from Object, since they don't need to be there and cause unnecessary issues with const, but AFAIK, no work has been done yet to make that that work. I would of the things that would likely have to be finished first would be the refactoring of the built-in AAs so that they're templated types internally, and I'm sure that there are similar roadblocks that will need to be sorted out. Long term though, none of that needs to be on Object and should be left to derived classes to add with whatever constness (or safety or whatever) that is appropriate for them. - Jonathan M DavisSo my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".Because not all valid implementations can be. They shouldn't be in Object anyway.
Mar 17 2013
On 18/03/2013 00:34, Jonathan M Davis wrote:On Monday, March 18, 2013 01:20:40 Timon Gehr wrote:What are these "unnecessary issues with const" they cause? I agree with doing away with Object.opCmp as it doesn't make sense to pretend that artibrary objects can be ordered in relation to each other. Not sure about the others. But how would we go about this without causing far more disruption than making these methods const would?Yeah. It was agreed that opCmp, opEquals, toString, and toHash would be removed from Object, since they don't need to be there and cause unnecessary issues with const,So my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".Because not all valid implementations can be. They shouldn't be in Object anyway.but AFAIK, no work has been done yet to make that that work. I would of the things that would likely have to be finished first would be the refactoring of the built-in AAs so that they're templated types internally, and I'm sure that there are similar roadblocks that will need to be sorted out. Long term though, none of that needs to be on Object and should be left to derived classes to add with whatever constness (or safety or whatever) that is appropriate for them.Why would some class want to implement these methods in a way that alters the object? Stewart. -- My email address is valid but not my primary mailbox and not checked regularly. Please keep replies on the 'group where everybody may benefit.
Mar 17 2013
On Monday, March 18, 2013 00:53:52 Stewart Gordon wrote:Why would some class want to implement these methods in a way that alters the object?Because const in D is physical const, not logical const. So, for instance, const prevents caching. And it's quite possible that a type which really cared about efficiency would cache the calculated value for toHash. Make toHash const would make that impossible. Another possible problem would be lazy initialization. If opEquals is const, then lazy initialization becomes impossible. We've discussed this on a number of occasions, and it's clear that forcing these functions to be const is a major problem, and yet they do need to be const for them to work with const objects. What was finally decided during the last big discussion on this a few months back was that we would remove opEqulas, opCmp, toHash, and toString from Object. They don't need to be there. As long as everything in the runtime which deals with them is templated, then there's no technical reason why Object would need them. D isn't Java where we have containers of Object or anything like that. Putting them on Object just restricts us. So, once all of those functions are removed from Object, derived types can then define them with whatever attributes they want. The only thing you lose is the ability to compare Objects directly, which is not necessary in D and is arguably a bad idea anyway. The work on this conversion hasn't been done yet, and a transition plan will have to be put in place to minimize code breakage, but that's what was decided on as the solution to the issues with const and Object's functions. - Jonathan M Davis
Mar 17 2013
On Monday, 18 March 2013 at 01:05:25 UTC, Jonathan M Davis wrote:On Monday, March 18, 2013 00:53:52 Stewart Gordon wrote:Even on Java's case and as extent .NET they aren't strictly necessary. Nowadays I would say it was a bad design decision and the best way would have been to have created interfaces for those operations. The only benefit would be default implementations. But I imagine the language designers, like everyone else, had to build their knowledge about classes vs interfaces, and many other OO abstractions, so what now seems wrong was seen as right at the time. I remember thinking Object was an evolution over C++ way of doing things. -- PauloWhy would some class want to implement these methods in a way that alters the object?Because const in D is physical const, not logical const. So, for instance, const prevents caching. And it's quite possible that a type which really cared about efficiency would cache the calculated value for toHash. Make toHash const would make that impossible. Another possible problem would be lazy initialization. If opEquals is const, then lazy initialization becomes impossible. We've discussed this on a number of occasions, and it's clear that forcing these functions to be const is a major problem, and yet they do need to be const for them to work with const objects. What was finally decided during the last big discussion on this a few months back was that we would remove opEqulas, opCmp, toHash, and toString from Object. They don't need to be there. As long as everything in the runtime which deals with them is templated, then there's no technical reason why Object would need them. D isn't Java where we have containers of Object or anything like that. Putting them on Object just restricts us.
Mar 18 2013
On 18/03/2013 01:05, Jonathan M Davis wrote:On Monday, March 18, 2013 00:53:52 Stewart Gordon wrote:Look up std.functional.memoize.Why would some class want to implement these methods in a way that alters the object?Because const in D is physical const, not logical const. So, for instance, const prevents caching. And it's quite possible that a type which really cared about efficiency would cache the calculated value for toHash. Make toHash const would make that impossible. Another possible problem would be lazy initialization. If opEquals is const, then lazy initialization becomes impossible.We've discussed this on a number of occasions, and it's clear that forcing these functions to be const is a major problem, and yet they do need to be const for them to work with const objects. What was finally decided during the last big discussion on this a few months back was that we would remove opEqulas, opCmp, toHash, and toString from Object. They don't need to be there. As long as everything in the runtime which deals with them is templated, then there's no technical reason why Object would need them.<snip> That's true. It would even grant the benefit of being able to use opEquals, opCmp, toHash or some combination of them in the AA implementation, depending on which methods exist. But the drawback of this approach (compared with making Object const-correct) is that some library programmers will (continue to) neglect const-correctness when implementing these methods, just as Walter did. Either by simply forgetting to declare them const, by caching information wihtin the object instead of using memoize, or by inadvertently doing something else that changes the object's state. Stewart.
Mar 18 2013
On 03/18/2013 11:11 PM, Stewart Gordon wrote:... But the drawback of this approach (compared with making Object const-correct) is that some library programmers will (continue to) neglect const-correctnessThat's a C++ term and it is not applicable to D.
Mar 18 2013
On Monday, March 18, 2013 23:53:05 Timon Gehr wrote:On 03/18/2013 11:11 PM, Stewart Gordon wrote:I wouldn't go that far. I think that the term is still quite applicable to D, but given how much stricter D's const is, the situation is certainly quite different with regards to when it does and doesn't make sense to use const. But it's still best to make things const whenever they can be, which is the whole idea behind const-correctness. It's just that that happens less often in D than it does in C++. - Jonathan M Davis... But the drawback of this approach (compared with making Object const-correct) is that some library programmers will (continue to) neglect const-correctnessThat's a C++ term and it is not applicable to D.
Mar 18 2013
On Monday, March 18, 2013 22:11:49 Stewart Gordon wrote:On 18/03/2013 01:05, Jonathan M Davis wrote:It doesn't work with pure as it forces you to put state outside of the object, and it's only applicable to caching, not lazy initialization. In either case, by making opEquals const, it's impossible to have any state cached in the object or to have any state in the object which is lazily initialized.On Monday, March 18, 2013 00:53:52 Stewart Gordon wrote:Look up std.functional.memoize.Why would some class want to implement these methods in a way that alters the object?Because const in D is physical const, not logical const. So, for instance, const prevents caching. And it's quite possible that a type which really cared about efficiency would cache the calculated value for toHash. Make toHash const would make that impossible. Another possible problem would be lazy initialization. If opEquals is const, then lazy initialization becomes impossible.structs don't have any restrictions on them either with regards to opEquals and friends, and that works just fine. Given how strict D's const is, it just plain doesn't work to force it on people. Yes, it's great to use it as much as you reasonably can, but unlike C++, const comes at a very high cost in D, and it must be used with care. You just can't assume that it'll work in the general case. - Jonathan M DavisWe've discussed this on a number of occasions, and it's clear that forcing these functions to be const is a major problem, and yet they do need to be const for them to work with const objects. What was finally decided during the last big discussion on this a few months back was that we would remove opEqulas, opCmp, toHash, and toString from Object. They don't need to be there. As long as everything in the runtime which deals with them is templated, then there's no technical reason why Object would need them.<snip> That's true. It would even grant the benefit of being able to use opEquals, opCmp, toHash or some combination of them in the AA implementation, depending on which methods exist. But the drawback of this approach (compared with making Object const-correct) is that some library programmers will (continue to) neglect const-correctness when implementing these methods, just as Walter did. Either by simply forgetting to declare them const, by caching information wihtin the object instead of using memoize, or by inadvertently doing something else that changes the object's state.
Mar 18 2013
On 18/03/2013 23:06, Jonathan M Davis wrote:On Monday, March 18, 2013 22:11:49 Stewart Gordon wrote:<snip>Why can't it be used as a means of lazy initialization?Look up std.functional.memoize.It doesn't work with pure as it forces you to put state outside of the object, and it's only applicable to caching, not lazy initialization.In either case, by making opEquals const, it's impossible to have any state cached in the object or to have any state in the object which is lazily initialized.<snip> But that equally doesn't work with pure. But why does the state need to be physically in the object, anyway? The whole point of memoize is that it gets around the problem by storing the state outside the object. Moreover, memoize can be made to work with pure by having something like trusted that overrides purity instead of safety. Or by going the whole hog and building it into the language. In any case, if you find yourself having to prevent const objects from being compared for equality or whatever, then you need to rethink your strategy. Or at the very least, define a const version as well. By having the const version in Object, it doesn't prevent someone from additionally defining a non-const version that speeds up subsequent calculations by storing something extra in the object. This could possibly be used by the const version as well. Stewart.
Mar 18 2013
On Tuesday, March 19, 2013 00:26:40 Stewart Gordon wrote:On 18/03/2013 23:06, Jonathan M Davis wrote:Because if one of the member variables hasn't been initialized yet, then it can't be compared.On Monday, March 18, 2013 22:11:49 Stewart Gordon wrote:<snip>Why can't it be used as a means of lazy initialization?Look up std.functional.memoize.It doesn't work with pure as it forces you to put state outside of the object, and it's only applicable to caching, not lazy initialization.In any case, if you find yourself having to prevent const objects from being compared for equality or whatever, then you need to rethink your strategy. Or at the very least, define a const version as well. By having the const version in Object, it doesn't prevent someone from additionally defining a non-const version that speeds up subsequent calculations by storing something extra in the object. This could possibly be used by the const version as well.There _are_ types which _can't_ have const on opEquals, because they _need_ to be able to mutate state in opEquals (e.g a lazily initialized object). They may very well be logically const, but they're not physically const. Even if it's generally best practice for opEquals to be const, it's needless restrictive to require it. D is a systems language, and needs to be flexible and pragmatic. We do _not_ want to force const on people. D's const is far too restrictive to require it. It was already agreed upon by Walter and Andrei that we would get rid of opEquals, opCmp, toHash, and toString on Object. It's just a question of doing what needs to be done to make that possible and providing an appropriate transition path. - Jonathan M Davis
Mar 18 2013
On 19/03/2013 00:46, Jonathan M Davis wrote:On Tuesday, March 19, 2013 00:26:40 Stewart Gordon wrote:<snip>You miss the whole point of what I'm saying. Why can't some result stored in an out-of-object cache be used as a substitute for a member variable? <snip>Why can't it be used as a means of lazy initialization?Because if one of the member variables hasn't been initialized yet, then it can't be compared.There _are_ types which _can't_ have const on opEquals, because they _need_ to be able to mutate state in opEquals (e.g a lazily initialized object). They may very well be logically const, but they're not physically const.<snip> This lazily initialised data would be calculated from the data already in the object, right? So why can't it just calculate the equality directly from that data? Even if it is computationally expensive to determine whether two objects of a given class are equal, why does it absolutely need to put the intermediate data into the object? Why can't it just use local variables to hold this intermediate data? In extreme cases, even make mutable copies of the objects to be compared and call the non-const opEquals on those? What I'm suggesting may be inefficient in complicated cases, but that's different from being impossible, which is what you're basically saying. Moreover, all this talk seems hypothetical. I'm yet to see anybody post a real-world example of what is being talked about, let alone one that will affect the typical everyday D programmer. Having some examples would help put the problem in better perspective. Stewart.
Mar 18 2013
On Tuesday, March 19, 2013 01:31:23 Stewart Gordon wrote:What I'm suggesting may be inefficient in complicated cases, but that's different from being impossible, which is what you're basically saying.It _is_ impossible if it's in the type, because you can't modify const. Sure, in same cases, you can put the data outside the type, but that's often undesirable.Moreover, all this talk seems hypothetical. I'm yet to see anybody post a real-world example of what is being talked about, let alone one that will affect the typical everyday D programmer. Having some examples would help put the problem in better perspective.It's been discussed ad nauseum before. Go digging through old threads if you want more details. I'm just telling you want the plan is. Requiring opEquals and friends prevents certain idioms, and we have no reason to prevent those idioms - especially in a systems programming language. And every time that we've discussed making any of them const, a number of people have complained quite loudly. Some of them have real world code that would be negatively affected, but it's not mine, so I don't remember all of the nitty gritty details. It's been a big debate for some time how to resolve the problem given than some use cases require const and others require mutable. In the last big discussion, someone pointed out that opEquals and friends don't even need to be on Object in the first place, and that's what we decided on. And we don't really lose anything by removing them. Then, just like with structs, classes that want opEquals to be const can make it const, and those that don't want to, don't have to. - Jonathan M Davis
Mar 18 2013
On Monday, 18 March 2013 at 01:05:25 UTC, Jonathan M Davis wrote:So, once all of those functions are removed from Object, derived types can then define them with whatever attributes they want. The only thing you lose is the ability to compare Objects directly, which is not necessary in D and is arguably a bad idea anyway.It's worth noting that including a standard interface (as in the interface keyword) for stuff like Comparable, Hashable etc. is a possibility that can be explored to enable runtime polymorphism akin to the current Object for programs that need it. Either way, I'm glad it's on its way out of Object.
Mar 18 2013
On 19/03/2013 00:19, Jakob Ovrum wrote: <snip>It's worth noting that including a standard interface (as in the interface keyword) for stuff like Comparable, Hashable etc. is a possibility that can be explored to enable runtime polymorphism akin to the current Object for programs that need it. Either way, I'm glad it's on its way out of Object.Can you think a use case that can't be fulfilled by using an IsExpression to test whether a type supports such an operation? Stewart.
Mar 18 2013
On Monday, 18 March 2013 at 01:05:25 UTC, Jonathan M Davis wrote:On Monday, March 18, 2013 00:53:52 Stewart Gordon wrote:Am I right in thinking that removal of these methods from Object will mean that it will no longer be necessary for the the argument to be of type Object and that the need for casting in the implementation will go away? PeterWhy would some class want to implement these methods in a way that alters the object?Because const in D is physical const, not logical const. So, for instance, const prevents caching. And it's quite possible that a type which really cared about efficiency would cache the calculated value for toHash. Make toHash const would make that impossible. Another possible problem would be lazy initialization. If opEquals is const, then lazy initialization becomes impossible. We've discussed this on a number of occasions, and it's clear that forcing these functions to be const is a major problem, and yet they do need to be const for them to work with const objects. What was finally decided during the last big discussion on this a few months back was that we would remove opEqulas, opCmp, toHash, and toString from Object. They don't need to be there. As long as everything in the runtime which deals with them is templated, then there's no technical reason why Object would need them. D isn't Java where we have containers of Object or anything like that. Putting them on Object just restricts us. So, once all of those functions are removed from Object, derived types can then define them with whatever attributes they want. The only thing you lose is the ability to compare Objects directly, which is not necessary in D and is arguably a bad idea anyway. The work on this conversion hasn't been done yet, and a transition plan will have to be put in place to minimize code breakage, but that's what was decided on as the solution to the issues with const and Object's functions. - Jonathan M Davis
Mar 18 2013
On 19/03/2013 01:17, Peter Williams wrote: <snip>Am I right in thinking that removal of these methods from Object will mean that it will no longer be necessary for the the argument to be of type ObjectYes, and that's indeed a potentially good reason to remove opCmp and opEquals from Object, and one I'd thought of myself but hadn't got round to bringing into the conversation.and that the need for casting in the implementation will go away?The need to cast from Object to some class just to compare objects of that class will go away, but you might still have similar casts it if you have a class with a comparator and you create subclasses of it. Stewart.
Mar 18 2013
On Tuesday, March 19, 2013 02:17:28 Peter Williams wrote:Am I right in thinking that removal of these methods from Object will mean that it will no longer be necessary for the the argument to be of type Object and that the need for casting in the implementation will go away?Well, IIRC, if when you override a function, and you give it a different parameter type, it's a new overload rather than actually overriding anything (even if the type of the parameter in the derived type's function is a derived type of the type of the parameter in the base class' function). So, if you don't use a common type for the parameter, you'll run into overload conflicts regardless. So, while you _could_ use something other than Object, you could run into overload conflicts if you do. That can be gotten around by aliasing base class functions into the scope of the derived class (though that may cause derived types to be compared as base types if they're referred to via references to the base type, so that may no be a good idea) or by creating overloads for comparing every base class, but it's arguably easier to just accept Object and cast - or accept whatever the base type is which first introduces opEquals into the hierarchy. So, there's a good chance that what you'll end up doing is using the base type in the hierarchy which introduces opEquals as the parameter for opEquals. This is better than using Object in that it'll avoid having completely unrelated types even be comparable (as their opEquals wouldn't accept each other), but it would still require derived types to use that type as the parameter type for opEquals for the reasons mentioned above. - Jonathan M Davis
Mar 18 2013
On 19/03/2013 05:38, Jonathan M Davis wrote: <snip>Well, IIRC, if when you override a function, and you give it a different parameter type, it's a new overload rather than actually overriding anything (even if the type of the parameter in the derived type's function is a derived type of the type of the parameter in the base class' function).I've just been reminding myself of what http://dlang.org/hijack.html says about this. As it turns out, they are indeed separate overloads, albeit protected by hiding the base class's version from derived class references.So, if you don't use a common type for the parameter, you'll run into overload conflicts regardless. So, while you _could_ use something other than Object, you could run into overload conflicts if you do. That can be gotten around by aliasing base class functions into the scope of the derived class (though that may cause derived types to be compared as base types if they're referred to via references to the base type, so that may no be a good idea) or by creating overloads for comparing every base class, but it's arguably easier to just accept Object and cast - or accept whatever the base type is which first introduces opEquals into the hierarchy.<snip> I've always defined both opEquals(Object) and opEquals(MyClass), and likewise for opCmp where applicable, and made the Object version call the class-specific version. This way, the overhead of a cast is avoided in the case where you're comparing two objects through references of the specific class type. Of course, once these are removed from Object, it will just be a case of removing the Object versions of these methods from my class. But the question still remains: How do we implement this change without causing mass disruption? It might be the case that programmers just need to remove opEquals(Object) and opCmp(Object) from their classes, and remove the override attribute from toHash and toString (and declare them const as appropriate). But there will be a lot of libraries to convert, and it will take time for them all to be converted. Stewart.
Mar 19 2013
On Tuesday, March 19, 2013 20:51:28 Stewart Gordon wrote:But the question still remains: How do we implement this change without causing mass disruption? It might be the case that programmers just need to remove opEquals(Object) and opCmp(Object) from their classes, and remove the override attribute from toHash and toString (and declare them const as appropriate). But there will be a lot of libraries to convert, and it will take time for them all to be converted.I expect that we'll figure out how to do that when we're ready to actually start the transition process, but the obvious thing to do to start with would be to deprecate opEquals, opCmp, toHash, and toString on Object, which should then cause a number of deprecation warnings. That likely wouldn't be enough, but it would be a start. Additional compiler warnings would probably need to be added at some point, and eventually those functions would be outright removed. But regardless of how it's approached, it's obviously going to require changing existing code, and it's going to be disruptive. We can't avoid that, just mitigate it. But it _is_ a change that we're going to need to make in order to solve some of the problems surrounding opEquals et al and const. The main issue at this point is gettnig the work necessary to be able to make the transition done (and since that includes changes to the AA implementation, it's far from a small undertaking). - Jonathan M Davis
Mar 19 2013
On Wednesday, 20 March 2013 at 04:39:14 UTC, Jonathan M Davis wrote:On Tuesday, March 19, 2013 20:51:28 Stewart Gordon wrote:Wouldn't that trigger bunch of false warning on legitimate overloaded opSomething ?But the question still remains: How do we implement this change without causing mass disruption? It might be the case that programmers just need to remove opEquals(Object) and opCmp(Object) from their classes, and remove the override attribute from toHash and toString (and declare them const as appropriate). But there will be a lot of libraries to convert, and it will take time for them all to be converted.I expect that we'll figure out how to do that when we're ready to actually start the transition process, but the obvious thing to do to start with would be to deprecate opEquals, opCmp, toHash, and toString on Object, which should then cause a number of deprecation warnings. That likely wouldn't be enough, but it would be a start. Additional compiler warnings would probably need to be added at some point, and eventually those functions would be outright removed. But regardless of how it's approached, it's obviously going to require changing existing code, and it's going to be disruptive. We can't avoid that, just mitigate it. But it _is_ a change that we're going to need to make in order to solve some of the problems surrounding opEquals et al and const. The main issue at this point is gettnig the work necessary to be able to make the transition done (and since that includes changes to the AA implementation, it's far from a small undertaking). - Jonathan M Davis
Mar 21 2013
On Thursday, March 21, 2013 08:29:28 deadalnix wrote:Wouldn't that trigger bunch of false warning on legitimate overloaded opSomething ?I'm not sure that overriding a deprecated function triggers a deprecation warning, but regardless, for opEquals and opCmp, that wouldn't be a problem, because all classes would be being changed so that their opEquals and opCmp don't take Object, so the deprecation warnings would be a _good_ thing. It would probably be more of a problem for toString and toHash though, since they don't take arguments. Deprecating them won't work if overriding them triggers deprecation warnings, and the fact that you'll be forced to override them as long as they're in Object and then won't be allowed to once they're out of Object will probably mean simply removing them from one release to another and forcing everyone to immediately change their toHash and toString methods so that they don't break. So no, the situation isn't entirely clear-cut, and we'll have to look at it carefully to avoid as much immediate breakage as possible, but we may be forced to change some stuff in a way that causes immediate breakage. All that would depend on what the exact changes are that have to be made and the exact situation with override and deprecated and all that. And I don't think that anyone has thought that through completely yet. - Jonathan M Davis
Mar 21 2013
On 21/03/2013 07:51, Jonathan M Davis wrote:On Thursday, March 21, 2013 08:29:28 deadalnix wrote:The problem really is that the D runtime references these methods of Object. So the process would be something like this: (a) reimplement AAs using templates (b) rewrite any other code that depends on these methods of Object (sorting out TypeInfo might be tricky) (c) and then deprecate these methods Code that defines opEquals(Object) or opCmp(Object), either instead of or as a call to a class-specific version, will still work during this phase-out. But I'm inclined to agree that trying to override a deprecated method without declaring the override as deprecated should trigger a deprecation error.Wouldn't that trigger bunch of false warning on legitimate overloaded opSomething ?I'm not sure that overriding a deprecated function triggers a deprecation warning, but regardless, for opEquals and opCmp, that wouldn't be a problem, because all classes would be being changed so that their opEquals and opCmp don't take Object, so the deprecation warnings would be a _good_ thing.It would probably be more of a problem for toString and toHash though, since they don't take arguments. Deprecating them won't work if overriding them triggers deprecation warnings, and the fact that you'll be forced to override them as long as they're in Object and then won't be allowed to once they're out of Object will probably mean simply removing them from one release to another and forcing everyone to immediately change their toHash and toString methods so that they don't break.<snip> Yes, it does seem that we would need something to accommodate this. Maybe a way to mark a deprecated method so that it can be overridden without the override attribute, but overriding it with the override attribute will trigger a deprecation error. But would this feature get any real use after we've got rid of these methods from Object? Stewart.
Mar 22 2013
On 19/03/13 15:38, Jonathan M Davis wrote:On Tuesday, March 19, 2013 02:17:28 Peter Williams wrote:Yes. I didn't mean to imply that all casting would go away just the OBLIGATORY casting of Object. Peter PS I'd like to say that this is the first disappointment I've encountered in my mission to learn D. Otherwise, I'm finding it a pleasure to use and find the compiler error messages very useful compared to those I'm used to with similar languages. Well done!!Am I right in thinking that removal of these methods from Object will mean that it will no longer be necessary for the the argument to be of type Object and that the need for casting in the implementation will go away?Well, IIRC, if when you override a function, and you give it a different parameter type, it's a new overload rather than actually overriding anything (even if the type of the parameter in the derived type's function is a derived type of the type of the parameter in the base class' function). So, if you don't use a common type for the parameter, you'll run into overload conflicts regardless. So, while you _could_ use something other than Object, you could run into overload conflicts if you do. That can be gotten around by aliasing base class functions into the scope of the derived class (though that may cause derived types to be compared as base types if they're referred to via references to the base type, so that may no be a good idea) or by creating overloads for comparing every base class, but it's arguably easier to just accept Object and cast - or accept whatever the base type is which first introduces opEquals into the hierarchy. So, there's a good chance that what you'll end up doing is using the base type in the hierarchy which introduces opEquals as the parameter for opEquals. This is better than using Object in that it'll avoid having completely unrelated types even be comparable (as their opEquals wouldn't accept each other), but it would still require derived types to use that type as the parameter type for opEquals for the reasons mentioned above. - Jonathan M Davis
Mar 18 2013
On Monday, 18 March 2013 at 00:16:17 UTC, Timon Gehr wrote:On 03/18/2013 12:19 AM, Peter Williams wrote:Can't those (rare) implementations just do a non constant cast when they cast Object to their desired class?The current signatures for opCmp/opEqual mean that code like: bool strictly_ordered(T)(in T[] list) { for (auto j = 1; j < list.length; j++) { if (list[j - 1] >= list[j]) return false; } return true; } will fail to compile if T is a class because opCmp cannot be called with a const argument. This restricts the quality of code that can be written by limiting legitimate use of in/const arguments to functions/methods. Trying to work around this problem by defining opCmp/opEquals for the class being used with in or const argument does not work as they are not recognised as an override of the Object methods.Yes they are.So my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".Because not all valid implementations can be.They shouldn't be in Object anyway.Yes, if they could just be declared without overriding the problem would go away but whichever solution is chosen existing code would need minor changes. Peter
Mar 17 2013
On Monday, March 18, 2013 02:00:37 Peter Williams wrote:Can't those (rare) implementations just do a non constant cast when they cast Object to their desired class?No. In D, casting away const on an object and mutating it is undefined behavior. - Jonathan M Davis
Mar 17 2013
On 17/03/2013 23:19, Peter Williams wrote: <snip>So my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".Known bug. http://d.puremagic.com/issues/show_bug.cgi?id=1824 This was reported 5 years ago, and it's a serious issue, so I don't know why it's taking so long to get it sorted. Stewart. -- My email address is valid but not my primary mailbox and not checked regularly. Please keep replies on the 'group where everybody may benefit.
Mar 17 2013
On Monday, 18 March 2013 at 00:46:46 UTC, Stewart Gordon wrote:On 17/03/2013 23:19, Peter Williams wrote: <snip>I did look in bugs but failed to find this. Thanks for the reference. PeterSo my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".Known bug. http://d.puremagic.com/issues/show_bug.cgi?id=1824
Mar 17 2013
On Monday, March 18, 2013 00:19:03 Peter Williams wrote:So my question is "Why are the arguments to opEquals and opCmp (for Objects) not declared in or const?".By the way, this is post where it was officially decided that we'd be working on removing opCmp, opEquals, toHash, and toString from Object. It also has a link to the prior discussion. Apparently, all that was way back in July. Man, how time flies... - Jonathan M Davis
Mar 20 2013