www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why are opEquals/opCmp arguments not in or const for Objects?

reply "Peter Williams" <pwil3058 bigpond.net.au> writes:
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
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/18/2013 01:16 AM, Timon Gehr wrote:
 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.

Actually you are right.
 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
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
On 18/03/2013 00:34, Jonathan M Davis wrote:
 On Monday, March 18, 2013 01:20:40 Timon Gehr wrote:
 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.


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,

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?
 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
next sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
On 18/03/2013 01:05, Jonathan M Davis wrote:
 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.

Look up std.functional.memoize.
 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.

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
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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-correctness

That's a C++ term and it is not applicable to D.
Mar 18 2013
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
On 18/03/2013 23:06, Jonathan M Davis wrote:
 On Monday, March 18, 2013 22:11:49 Stewart Gordon wrote:

 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.

Why can't it be used as a means of 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.

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
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
On 19/03/2013 00:46, Jonathan M Davis wrote:
 On Tuesday, March 19, 2013 00:26:40 Stewart Gordon wrote:

 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.

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>
 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.

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
prev sibling next sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
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
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
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 Object

Yes, 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
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, March 18, 2013 01:20:40 Timon Gehr wrote:
 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.


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 Davis
Mar 17 2013
prev sibling next sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
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
prev sibling next sibling parent "Peter Williams" <pwil3058 bigpond.net.au> writes:
On Monday, 18 March 2013 at 00:46:46 UTC, Stewart Gordon wrote:
 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

I did look in bugs but failed to find this. Thanks for the reference. Peter
Mar 17 2013
prev sibling next sibling parent "Peter Williams" <pwil3058 bigpond.net.au> writes:
On Monday, 18 March 2013 at 00:16:17 UTC, Timon Gehr wrote:
 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.

Can't those (rare) implementations just do a non constant cast when they cast Object to their desired class?
 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
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
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.

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
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent "Paulo Pinto" <pjmlp progtools.org> writes:
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:
 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.

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. -- Paulo
Mar 18 2013
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, March 18, 2013 23:53:05 Timon Gehr wrote:
 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-correctness

That's a C++ term and it is not applicable to D.

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
Mar 18 2013
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, March 18, 2013 22:11:49 Stewart Gordon wrote:
 On 18/03/2013 01:05, Jonathan M Davis wrote:
 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.

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.
 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.

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.

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 Davis
Mar 18 2013
prev sibling next sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
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
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, March 19, 2013 00:26:40 Stewart Gordon wrote:
 On 18/03/2013 23:06, Jonathan M Davis wrote:
 On Monday, March 18, 2013 22:11:49 Stewart Gordon wrote:

 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.

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.
 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
prev sibling next sibling parent "Peter Williams" <pwil3058 bigpond.net.au> writes:
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:
 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

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? Peter
Mar 18 2013
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent Peter Williams <pwil3058 bigpond.net.au> writes:
On 19/03/13 15:38, Jonathan M Davis wrote:
 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

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!!
Mar 18 2013
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
On 21/03/2013 07:51, Jonathan M Davis wrote:
 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.

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.
 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.

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
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
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:
 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

Wouldn't that trigger bunch of false warning on legitimate overloaded opSomething ?
Mar 21 2013
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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