digitalmars.D - How will we fix opEquals?
- Don (11/11) Feb 10 2011 Andrei once stated a worthy goal: as far as possible, const should be
- Jonathan M Davis (34/50) Feb 10 2011 Honestly, I think that opEquals should just be const. It doesn't general...
- so (4/20) Feb 10 2011 Problem is not having to provide const for these functions, it is the
- Jonathan M Davis (17/40) Feb 10 2011 The thing is that opEquals, opCmp, toHash, and toString/writeTo _must_ b...
- Graham St Jack (3/3) Feb 10 2011 Vote++
- so (3/12) Feb 10 2011 Structs avoid because unlike classes structs don't share the same base
- Jonathan M Davis (14/20) Feb 10 2011 Hmm. You're right (I just tried it). I was certain that you could. It's ...
- spir (22/29) Feb 11 2011 In the worst case, people who really, really, want it it non-const are l...
- spir (8/24) Feb 10 2011 Has it been considered for constness or such constraints not to be inher...
- so (3/6) Feb 10 2011 I asked something similar not more than a week ago,
- Steven Schveighoffer (8/43) Feb 10 2011 This isn't really true. If you make opEquals const, then anything
- Graham St Jack (5/11) Feb 10 2011 I totally agree - we need to take the plunge and roll const out through
- Peter Alexander (3/15) Feb 10 2011 It's not possible. You need logical const to make const opt-in. This has...
- so (6/25) Feb 10 2011 There might be a way involving something like inout attribute for
- Andrei Alexandrescu (5/23) Feb 10 2011 It is, in fact, possible. We can define two opEquals overloads, for
- Steven Schveighoffer (7/31) Feb 10 2011 This doesn't work. If you only define one, then you will have problems ...
- Don (11/47) Feb 10 2011 I fear that you're probably right. The const opEquals must exist,
- Steven Schveighoffer (13/20) Feb 10 2011 I think then we get back to
- Don (4/26) Feb 10 2011 No, we don't, because that issue applies to structs, not classes. For
- Steven Schveighoffer (15/41) Feb 10 2011 What I meant was, if the compiler complains that some specific function ...
- Jonathan M Davis (16/66) Feb 10 2011 I'd argue that silently changing function signatures is a _bad_ idea. An...
- Michel Fortin (12/31) Feb 10 2011 I don't like this idea at all. If the problem is that it's too easy to
- Andrei Alexandrescu (5/37) Feb 10 2011 In fact I agree. Doubling the number of basic Object members has other
- so (7/10) Feb 10 2011 It doesn't look like we can solve this by switching the constness of an ...
- Don (8/17) Feb 10 2011 I can think of many examples where opEquals would be logically pure, but...
- so (2/4) Feb 10 2011 I can't think of any.
- Jonathan M Davis (34/52) Feb 10 2011 The hash problem can be semi-reasonably solved by declaring a non-const ...
- Michel Fortin (16/34) Feb 10 2011 If you use casts on some member variables, make sure the member is
- Bruno Medeiros (14/29) Feb 23 2011 Are you sure that such casts should be allowed? The compiler will be
- bearophile (4/7) Feb 23 2011 If I have a string type, I'd like to compute its hash value lazily, the ...
- Bruno Medeiros (8/15) Feb 25 2011 (Hum, I was thinking more of the inputs to the functions, the data that
- spir (14/18) Feb 10 2011 Is this point very annoying in practice? In my experience, such "languag...
- Jason House (9/24) Feb 10 2011 class LazyObject{
- Steven Schveighoffer (6/33) Feb 10 2011 This doesn't work. What if the user overrides one and not the other? O...
- Jason House (16/56) Feb 10 2011 You can never stop programmers from writing incorrect code...
- Steven Schveighoffer (13/68) Feb 10 2011 No but you can make things easier to avoid writing incorrect code. This...
- Steven Schveighoffer (4/8) Feb 10 2011 This should have said "forgetting to modify one when you modify the othe...
- Jonathan M Davis (11/41) Feb 11 2011 structs are a different beast. The issue under discussion here is classe...
Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?
Feb 10 2011
On Thursday 10 February 2011 00:19:38 Don wrote:Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?Honestly, I think that opEquals should just be const. It doesn't generally make any sense for it not to be const anyway. And if Object's opEquals _isn't_ const, then const in general is screwed. opEquals, toString, opCmp, and toHash _all_ need to be const for const and immutable classes to work in general. I just don't think that it's realistic to say that a programmer can totally ignore const if they don't want to worry about it. But for the most part, what does it matter? So, there's a few functions which they have to mark as const, because that's the way that they are in Object. That doesn't mean that it's viral throughout their program. They can just skip const everywhere else. The _one_ thing that I can think of that's problematic about that approach is caching - which would matter for toHash and possibly toString (though changing to writeTo would likely make caching on that a non-option anyway). But if they _really_ want caching, then can't they just add a second toHash to their class and make that one non-const? That one could cache the result, and then the const version could use the cached result if it existed. Realistically, Object _must_ be const-correct for const to work. And if Object is const-correct, then anyone overriding those const methods _must_ worry about const for those functions. I don't see any way around that. Now, they don't have to make any other functions const or declare any variables which are const or do anything else with const whatsoever, but toString/writeTo, opEquals, opCmp, and toHash _must_ be const or const and immutable objects are screwed, because they couldn't call them. Saying that no one should have to worry about const if they don't want to is a noble though, I suppose, but I don't think that it's entirely realistic. const is part of the language, and some things just plain have to be const to work. And given the prevalence of immutable with regards to threads and the like, you're going to be forced to use const in many cases anyway. I don't think that it's all that big a deal with these 4 functions are const. The impact on the programmer is minimal. They can _almost_ ignore const completely, since they can still ignore it pretty much everywhere else other than with the overriden const Object functions. - Jonathan M Davis
Feb 10 2011
Saying that no one should have to worry about const if they don't want to is a noble though, I suppose, but I don't think that it's entirely realistic. const is part of the language, and some things just plain have to be const to work. And given the prevalence of immutable with regards to threads and the like, you're going to be forced to use const in many cases anyway. I don't think that it's all that big a deal with these 4 functions are const. The impact on the programmer is minimal. They can _almost_ ignore const completely, since they can still ignore it pretty much everywhere else other than with the overriden const Object functions. - Jonathan M DavisProblem is not having to provide const for these functions, it is the lock-in we introduce by marking them const. These functions would be const most of the times, if not all. Still, it is hard to rule the other case out completely i suppose.
Feb 10 2011
On Thursday 10 February 2011 01:26:14 so wrote:The thing is that opEquals, opCmp, toHash, and toString/writeTo _must_ be const for const and immutable objects to be able to use them. It's non-negotiable unless Object is breaking the type system (which would _not_ be good). That being the case, it doesn't matter how much we might want to avoid forcing const on people. We _must_ have it there, so anyone overriding those functions _must_ use it for those functions. They could create non-const versions in addition to the const ones, but they _must_ create the const versions. They're stuck. There's no way around it without breaking the type system. structs avoid the problem, because they don't have to worry about polymorphism, but classes obviously do. Now, that forces people to use const on _4_ functions per class - that's it. And that's assuming that they want to override them all. They can ignore const beyond that. So, they _cannot_ ignore const completely. It's just not practical. However, they _can_ _mostly_ ignore it. So, it _does_ have an impact on those who don't wish to bother with const, but the impact should be minimal. - Jonathan M DavisSaying that no one should have to worry about const if they don't want to is a noble though, I suppose, but I don't think that it's entirely realistic. const is part of the language, and some things just plain have to be const to work. And given the prevalence of immutable with regards to threads and the like, you're going to be forced to use const in many cases anyway. I don't think that it's all that big a deal with these 4 functions are const. The impact on the programmer is minimal. They can _almost_ ignore const completely, since they can still ignore it pretty much everywhere else other than with the overriden const Object functions. - Jonathan M DavisProblem is not having to provide const for these functions, it is the lock-in we introduce by marking them const. These functions would be const most of the times, if not all. Still, it is hard to rule the other case out completely i suppose.
Feb 10 2011
We _must_ have it there, so anyone overriding those functions _must_ use it for those functions. They could create non-const versions in addition to the const ones,It is the whole point, they can't.but they _must_ create the const versions. They're stuck. There's no way around it without breaking the type system. structs avoid the problem, because they don't have to worry about polymorphism, but classes obviously do.Structs avoid because unlike classes structs don't share the same base class.
Feb 10 2011
On Thursday 10 February 2011 21:50:45 so wrote:Hmm. You're right (I just tried it). I was certain that you could. It's the kind of thing that you could do in C++, but I guess that D is pickier about overriding. You can certainly _add_ const, but I guess you can't take it away even if there's also a const version defined. I guess that part of the problem is that I so rarely use classes in D. I usually use structs. I'm sure that will change if I ever get the chance to write large programs in D, but until I can use it at work I'm not sure how likely that is. Well, then there's no caching of toHash unless you call a separate function to calculate and cache the result first, or you use a global variable to hold the cached value. That kind of sucks, but I think that const is far more valuable that the possibility of caching, and there _are_ ways to get the caching behavior in spite of const - they just aren't all that nice. - Jonathan M DavisWe _must_ have it there, so anyone overriding those functions _must_ use it for those functions. They could create non-const versions in addition to the const ones,It is the whole point, they can't.
Feb 10 2011
On 02/11/2011 07:13 AM, Jonathan M Davis wrote:In the worst case, people who really, really, want it it non-const are left with: struct S { ... bool equals (ref S s) {...} } ... if (s1.equals(s2)) {...} I do not find it /that/ terrible (esp.compared to many other wrokarounds we commonly are forced to use for various reasons). The one bad case is if S belongs to the interface to client code. In other words, this is not an acceptable solution for library public types. But since const opEquals (and same reasoning for other methods) is only annoying for structs that (1) need custom equality predicate (2) call other funcs, else as member opEquals, (3) which themselves may be called by yet other funcs (else virality does not spread), then I guess very few cases remain. What do you think? Denis -- _________________ vita es estrany spir.wikidot.comHmm. You're right (I just tried it).We _must_ have it there, so anyone overriding those functions _must_> use it for those functions. They could create non-const versions in > addition to > the const ones,It is the whole point, they can't.
Feb 11 2011
On 02/10/2011 10:26 AM, so wrote:Has it been considered for constness or such constraints not to be inherited? (Rather plain functype signature.) Denis -- _________________ vita es estrany spir.wikidot.comSaying that no one should have to worry about const if they don't want to is a noble though, I suppose, but I don't think that it's entirely realistic. const is part of the language, and some things just plain have to be const to work. And given the prevalence of immutable with regards to threads and the like, you're going to be forced to use const in many cases anyway. I don't think that it's all that big a deal with these 4 functions are const. The impact on the programmer is minimal. They can _almost_ ignore const completely, since they can still ignore it pretty much everywhere else other than with the overriden const Object functions. - Jonathan M DavisProblem is not having to provide const for these functions, it is the lock-in we introduce by marking them const. These functions would be const most of the times, if not all. Still, it is hard to rule the other case out completely i suppose.
Feb 10 2011
Has it been considered for constness or such constraints not to be inherited? (Rather plain functype signature.) DenisI asked something similar not more than a week ago, it is possible in theory i think but implementation is very problematic and requires a big shift in the meaning of constness.
Feb 10 2011
On Thu, 10 Feb 2011 03:37:58 -0500, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Thursday 10 February 2011 00:19:38 Don wrote:This isn't really true. If you make opEquals const, then anything opEquals calls must be const. Inevitably, you need to make a lot of your object const, which then goes viral to things that object uses, etc. On the other hand, I don't think opt-in const is that worthy a goal. Things that are const, should be const. -SteveAndrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?Honestly, I think that opEquals should just be const. It doesn't generally make any sense for it not to be const anyway. And if Object's opEquals _isn't_ const, then const in general is screwed. opEquals, toString, opCmp, and toHash _all_ need to be const for const and immutable classes to work in general. I just don't think that it's realistic to say that a programmer can totally ignore const if they don't want to worry about it. But for the most part, what does it matter? So, there's a few functions which they have to mark as const, because that's the way that they are in Object. That doesn't mean that it's viral throughout their program. They can just skip const everywhere else.
Feb 10 2011
This isn't really true. If you make opEquals const, then anything opEquals calls must be const. Inevitably, you need to make a lot of your object const, which then goes viral to things that object uses, etc. On the other hand, I don't think opt-in const is that worthy a goal. Things that are const, should be const. -SteveI totally agree - we need to take the plunge and roll const out through phobos. If const (and immutable too for that matter) is broken in subtle ways, then it needs to be fixed and then embraced. -- Graham St Jack
Feb 10 2011
On 10/02/11 8:19 AM, Don wrote:Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?It's not possible. You need logical const to make const opt-in. This has been argued many times by several people.
Feb 10 2011
On Thu, 10 Feb 2011 10:58:25 +0200, Peter Alexander <peter.alexander.au gmail.com> wrote:On 10/02/11 8:19 AM, Don wrote:There might be a way involving something like inout attribute for functions. It would solve these problems and wouldn't intrude those that don't care constness.Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?It's not possible. You need logical const to make const opt-in. This has been argued many times by several people.
Feb 10 2011
On 2/10/11 2:58 AM, Peter Alexander wrote:On 10/02/11 8:19 AM, Don wrote:It is, in fact, possible. We can define two opEquals overloads, for const and non-const. By default, the non-const version forwards to the const one. AndreiAndrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?It's not possible.
Feb 10 2011
On Thu, 10 Feb 2011 10:19:50 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 2/10/11 2:58 AM, Peter Alexander wrote:This doesn't work. If you only define one, then you will have problems with hidden function errors, and/or inconsistencies. For example, how does .opEquals(Object obj1, Object obj2) know which version to call? I think the only true solution is to make it const and call it a day. -SteveOn 10/02/11 8:19 AM, Don wrote:It is, in fact, possible. We can define two opEquals overloads, for const and non-const. By default, the non-const version forwards to the const one.Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?It's not possible.
Feb 10 2011
Steven Schveighoffer wrote:On Thu, 10 Feb 2011 10:19:50 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I fear that you're probably right. The const opEquals must exist, otherwise the object will be unable to use most functions in Phobos. And I don't see how it can safely be synthesised from non-const opEquals. A tiny compromise which could be made, is to silently add 'const' to any class opEquals declaration, even if not present in the source. That way, simple use cases would still compile without complaint. (As long as only a single, precisely defined opEquals signature is permitted, I think any other signature should be flagged as an error -- but it could be converted to the correct signature, instead). Dunno if this would be worth it, though.On 2/10/11 2:58 AM, Peter Alexander wrote:This doesn't work. If you only define one, then you will have problems with hidden function errors, and/or inconsistencies. For example, how does .opEquals(Object obj1, Object obj2) know which version to call? I think the only true solution is to make it const and call it a day. -SteveOn 10/02/11 8:19 AM, Don wrote:It is, in fact, possible. We can define two opEquals overloads, for const and non-const. By default, the non-const version forwards to the const one.Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?It's not possible.
Feb 10 2011
On Thu, 10 Feb 2011 15:22:48 -0500, Don <nospam nospam.com> wrote:A tiny compromise which could be made, is to silently add 'const' to any class opEquals declaration, even if not present in the source. That way, simple use cases would still compile without complaint. (As long as only a single, precisely defined opEquals signature is permitted, I think any other signature should be flagged as an error -- but it could be converted to the correct signature, instead). Dunno if this would be worth it, though.I think then we get back to http://d.puremagic.com/issues/show_bug.cgi?id=3659 Would it be possible to define a construct that says "you can override this function, but you cannot add any new overloads"? like a precise or something like that? Then we can have a real feature to use for things like this. Sort of like final, but you're not cutting off the virtual function path, just restricting the API to be exactly what you specify. That way someone cannot accidentally create a non-const opEquals and generate a hidden function exception. Although, anyone worth his salt will test the opEquals, which dmd forces through .opEquals(const(Object), const(Object)). -Steve
Feb 10 2011
Steven Schveighoffer wrote:On Thu, 10 Feb 2011 15:22:48 -0500, Don <nospam nospam.com> wrote:No, we don't, because that issue applies to structs, not classes. For classes, it has to go in the vtable, so the precise signature must be known.A tiny compromise which could be made, is to silently add 'const' to any class opEquals declaration, even if not present in the source. That way, simple use cases would still compile without complaint. (As long as only a single, precisely defined opEquals signature is permitted, I think any other signature should be flagged as an error -- but it could be converted to the correct signature, instead). Dunno if this would be worth it, though.I think then we get back to http://d.puremagic.com/issues/show_bug.cgi?id=3659Would it be possible to define a construct that says "you can override this function, but you cannot add any new overloads"? like a precise or something like that? Then we can have a real feature to use for things like this. Sort of like final, but you're not cutting off the virtual function path, just restricting the API to be exactly what you specify. That way someone cannot accidentally create a non-const opEquals and generate a hidden function exception. Although, anyone worth his salt will test the opEquals, which dmd forces through .opEquals(const(Object), const(Object)).For sure that could be done. But again -- does it add much value?
Feb 10 2011
On Thu, 10 Feb 2011 16:34:57 -0500, Don <nospam nospam.com> wrote:Steven Schveighoffer wrote:What I meant was, if the compiler complains that some specific function x has the wrong signature, you have assumptions the compiler is making that may not be correct. To me, opEquals is special, but it's still a regular function, and putting extra limitations is sure to ferret out some use case where the limitation is invalid. I consider bool opEquals(int x) const to be a perfectly valid signature in a class.On Thu, 10 Feb 2011 15:22:48 -0500, Don <nospam nospam.com> wrote:No, we don't, because that issue applies to structs, not classes. For classes, it has to go in the vtable, so the precise signature must be known.A tiny compromise which could be made, is to silently add 'const' to any class opEquals declaration, even if not present in the source. That way, simple use cases would still compile without complaint. (As long as only a single, precisely defined opEquals signature is permitted, I think any other signature should be flagged as an error -- but it could be converted to the correct signature, instead). Dunno if this would be worth it, though.I think then we get back to http://d.puremagic.com/issues/show_bug.cgi?id=3659Not really, I don't think it's that big a problem to begin with. If you try comparing two objects and you didn't define your signature right, then you get a hidden func error, since they all go through .opEquals(...) which casts everything to Object (should be const(Object) ). So the only way this gets out is if you never test the code you wrote. -SteveWould it be possible to define a construct that says "you can override this function, but you cannot add any new overloads"? like a precise or something like that? Then we can have a real feature to use for things like this. Sort of like final, but you're not cutting off the virtual function path, just restricting the API to be exactly what you specify. That way someone cannot accidentally create a non-const opEquals and generate a hidden function exception. Although, anyone worth his salt will test the opEquals, which dmd forces through .opEquals(const(Object), const(Object)).For sure that could be done. But again -- does it add much value?
Feb 10 2011
On Thursday 10 February 2011 12:22:48 Don wrote:Steven Schveighoffer wrote:I'd argue that silently changing function signatures is a _bad_ idea. And when they actually try and do something which would violate the constness of the function, they're going to be left scratching their head as to why a non-const function is getting complaints about const. In general, D has opted to _not_ do things silently. C++ did a fair bit of it in certain places (albeit not with const), and it's caused problems. I don't think that this would be as big a problem, but I still don't think that it's a good idea. Really, while it's reasonable to not force D programmers to use const all over the place, I do _not_ think that it's reasonable for a D programmer to not be aware of const. And since opEquals, opCmp, toHash, and toString/writeTo all need to be const, the programmer is just going to have to deal with that fact. And having to put const on a few functions is likely to cause less pain than figuring out why they're getting errors with regards to const when they never use const anywhere. - Jonathan M DavisOn Thu, 10 Feb 2011 10:19:50 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I fear that you're probably right. The const opEquals must exist, otherwise the object will be unable to use most functions in Phobos. And I don't see how it can safely be synthesised from non-const opEquals. A tiny compromise which could be made, is to silently add 'const' to any class opEquals declaration, even if not present in the source. That way, simple use cases would still compile without complaint. (As long as only a single, precisely defined opEquals signature is permitted, I think any other signature should be flagged as an error -- but it could be converted to the correct signature, instead). Dunno if this would be worth it, though.On 2/10/11 2:58 AM, Peter Alexander wrote:This doesn't work. If you only define one, then you will have problems with hidden function errors, and/or inconsistencies. For example, how does .opEquals(Object obj1, Object obj2) know which version to call? I think the only true solution is to make it const and call it a day. -SteveOn 10/02/11 8:19 AM, Don wrote:It is, in fact, possible. We can define two opEquals overloads, for const and non-const. By default, the non-const version forwards to the const one.Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?It's not possible.
Feb 10 2011
On 2011-02-10 15:22:48 -0500, Don <nospam nospam.com> said:Steven Schveighoffer wrote:I don't like this idea at all. If the problem is that it's too easy to hide the underlying function without noticing (no compile-time error), then fix how the language treats hidden functions (make it a compile-time error). If opEquals has this problem, other non-language-defined functions will have it too, and fixing it with a special case for opEquals will not fix it elsewhere. Not to speak of the inconsistency it adds. -- Michel Fortin michel.fortin michelf.com http://michelf.com/This doesn't work. If you only define one, then you will have problems with hidden function errors, and/or inconsistencies. For example, how does .opEquals(Object obj1, Object obj2) know which version to call? I think the only true solution is to make it const and call it a day. -SteveI fear that you're probably right. The const opEquals must exist, otherwise the object will be unable to use most functions in Phobos. And I don't see how it can safely be synthesised from non-const opEquals. A tiny compromise which could be made, is to silently add 'const' to any class opEquals declaration, even if not present in the source. That way, simple use cases would still compile without complaint. (As long as only a single, precisely defined opEquals signature is permitted, I think any other signature should be flagged as an error -- but it could be converted to the correct signature, instead).
Feb 10 2011
Michel Fortin wrote:On 2011-02-10 15:22:48 -0500, Don <nospam nospam.com> said:Oh, I agree. I don't think it's a good idea. It's just the _only_ compromise I could see that would actually work.Steven Schveighoffer wrote:I don't like this idea at all. If the problem is that it's too easy to hide the underlying function without noticing (no compile-time error), then fix how the language treats hidden functions (make it a compile-time error). If opEquals has this problem, other non-language-defined functions will have it too, and fixing it with a special case for opEquals will not fix it elsewhere. Not to speak of the inconsistency it adds.This doesn't work. If you only define one, then you will have problems with hidden function errors, and/or inconsistencies. For example, how does .opEquals(Object obj1, Object obj2) know which version to call? I think the only true solution is to make it const and call it a day. -SteveI fear that you're probably right. The const opEquals must exist, otherwise the object will be unable to use most functions in Phobos. And I don't see how it can safely be synthesised from non-const opEquals. A tiny compromise which could be made, is to silently add 'const' to any class opEquals declaration, even if not present in the source. That way, simple use cases would still compile without complaint. (As long as only a single, precisely defined opEquals signature is permitted, I think any other signature should be flagged as an error -- but it could be converted to the correct signature, instead).
Feb 10 2011
On 02/11/2011 12:20 AM, Don wrote:It seems const is expected to be a rather relevant feature of D2. And to be correct (compared to the notion of const in ...), providing true semantic gain both for programmers and the compiler, IIUC. Then, programmers must learn about it, even if they do not intend to use it (often, or more than they are forced to). FWIW, I have stepped on this issue (incorrect signature of opEquals rejected by the compiler) 3 times in a few weeks. I was rather annoyed the first time, the second time it me let think for a whilet (why is const part of the inherited signature, in fact?), and the third time just copied the signature from the error message, e basta! In fact, I end up finding it good the compiler to loudly protest that way, thus confronting me with the existence of the feature, letting me explore a it a bit even if by myself I wouldn't have done it; this is positive thank to the fact the error also provides the means to correct the bug, using a not to obcure formulation (for once ;-). It is, in any case, far better to give programmers a superficial impression of the compiler getting on their way from time to time (as long as it's rare), than making hidden exceptions to the core semantics of the language just to avoid giving such impressions. Woof, that were more words than I intended to write on the topic, sorry :-) Denis -- _________________ vita es estrany spir.wikidot.comI don't like this idea at all. If the problem is that it's too easy to hide the underlying function without noticing (no compile-time error), then fix how the language treats hidden functions (make it a compile-time error). If opEquals has this problem, other non-language-defined functions will have it too, and fixing it with a special case for opEquals will not fix it elsewhere. Not to speak of the inconsistency it adds.Oh, I agree. I don't think it's a good idea. It's just the _only_ compromise I could see that would actually work.
Feb 10 2011
On 2/10/11 9:33 AM, Steven Schveighoffer wrote:On Thu, 10 Feb 2011 10:19:50 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:You define two: const taking a const, and nonconst taking a nonconst.On 2/10/11 2:58 AM, Peter Alexander wrote:This doesn't work. If you only define one, then you will have problems with hidden function errors, and/or inconsistencies. For example, how does .opEquals(Object obj1, Object obj2) know which version to call?On 10/02/11 8:19 AM, Don wrote:It is, in fact, possible. We can define two opEquals overloads, for const and non-const. By default, the non-const version forwards to the const one.Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?It's not possible.I think the only true solution is to make it const and call it a day. -SteveIn fact I agree. Doubling the number of basic Object members has other nefarious circumstances. Andrei
Feb 10 2011
(1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const.It doesn't look like we can solve this by switching the constness of an Object.function, unless we also state that every function in Object must be const and accept the fact that no one would use these functions for other purposes like caching, late binding etc... Or maybe changing the design of default class inheritance somehow, like C++, no more single base class, i guess this is out of question :)
Feb 10 2011
so wrote:I can think of many examples where opEquals would be logically pure, but not formally pure, but it's harder to come up with ones where it is not const. I guess you can have situations where a lazily-computed hash value is cached in the object, to speed up comparisons, but it doesn't seem inappropriate for casts to be required when you're doing that. Can you think of a use case where the calling function should know that opEquals() isn't truly const?(1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const.It doesn't look like we can solve this by switching the constness of an Object.function, unless we also state that every function in Object must be const and accept the fact that no one would use these functions for other purposes like caching, late binding etc...
Feb 10 2011
Can you think of a use case where the calling function should know that opEquals() isn't truly const?I can't think of any. If we are going to this way, we need all four of them to be const i think.
Feb 10 2011
On Thursday 10 February 2011 01:36:56 Don wrote:so wrote:The hash problem can be semi-reasonably solved by declaring a non-const version of toHash along with the const one. That way, if you're not dealing with const objects, the hash can be cached (whicth the const version could also take advantage of if the non-const version had already been called or if some other function call had calculated the hash previously). However, without logical const, you can't do any sort of caching inside of a const function unless you either break the type system by casting away const or keep the cache outside of the class itself (at which point toHash couldn't be pure, which would suck). So, you're screwed on that point as long as toHash is const, but it _has_ to be const or const and immutable objects can't use it. Actually, that brings up the question as to whether opEquals, opCmp, toHash, and toString/writeTo should be pure as well. It sucks if they're not, but I'm not sure that it's as critical. I think that I'd have to mess around with it a bit to decide how much of a problem that would really be. You can still declare the overridden versions as pure and take advantage of pure as long as you kept the references as being of the derived type, but you'd be screwed if you were doing something that required Object's functions to be pure. Personally, I wish that there was a way to act like a logically pure function was actually pure. You can do that with nothrow by catching any exceptions (and preferably then asserting false if one actually gets thrown), but you can't do that with pure. I'd love it if you could just do the equivalent of that with pure and then call logically pure functions from actually pure ones, and if it isn't actually logically pure and the compiler ends up doing an incorrect optimization, then it's on your head. But it would make it much easier to use pure in situations where you _know_ that a function is logically pure, but you can't mark it as pure (calling C functions would be a prime example). As for opEquals not truly being const... I can't think of any situation where that would be necessary other than trying to save programmers from having to use it, and calling functions _definitely_ care, since they can't use const objects with it if it's not const. So, I'm not quite sure that I understand your question about the calling function needing to know that opEquals isn't truly const. - Jonathan M DavisI can think of many examples where opEquals would be logically pure, but not formally pure, but it's harder to come up with ones where it is not const. I guess you can have situations where a lazily-computed hash value is cached in the object, to speed up comparisons, but it doesn't seem inappropriate for casts to be required when you're doing that. Can you think of a use case where the calling function should know that opEquals() isn't truly const?(1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const.It doesn't look like we can solve this by switching the constness of an Object.function, unless we also state that every function in Object must be const and accept the fact that no one would use these functions for other purposes like caching, late binding etc...
Feb 10 2011
On 2011-02-10 04:36:56 -0500, Don <nospam nospam.com> said:so wrote:If you use casts on some member variables, make sure the member is marked shared don't forget to cast to shared mutable, not just mutable, because you never know when your immutable object is shared between threads. Suggesting casts looks error prone to me, but it can probably work. To answer the original question, yes opCmp, opEquals, toHash, toString should all be const. Otherwise it makes it impossible to use const/immutable objects in any generic code requiring those functions to be available. And generic code will otherwise work perfectly fine with non-mutable objects once Walter merges my const(Object)ref patch in the mainline. -- Michel Fortin michel.fortin michelf.com http://michelf.com/I can think of many examples where opEquals would be logically pure, but not formally pure, but it's harder to come up with ones where it is not const. I guess you can have situations where a lazily-computed hash value is cached in the object, to speed up comparisons, but it doesn't seem inappropriate for casts to be required when you're doing that. Can you think of a use case where the calling function should know that opEquals() isn't truly const?(1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const.It doesn't look like we can solve this by switching the constness of an Object.function, unless we also state that every function in Object must be const and accept the fact that no one would use these functions for other purposes like caching, late binding etc...
Feb 10 2011
On 10/02/2011 09:36, Don wrote:so wrote:Are you sure that such casts should be allowed? The compiler will be expecting opEquals to be proper const, not just logical const, and if it's not the code may break, even if only on some more obscure (but nonetheless valid) cases. The only situation where I see where such cast would be valid, is if the object can ensure all lazily-computed values (well, at least those required for opEquals) have already been computed, and thus executing const opEquals() will indeed not change the object in any way. The good news is that I suspect the fields used for opEquals/opCmp/opHash in any class are unlikely to be fields that are computed lazily. It's just a guess though, anyone have examples otherwise? -- Bruno Medeiros - Software EngineerI can think of many examples where opEquals would be logically pure, but not formally pure, but it's harder to come up with ones where it is not const. I guess you can have situations where a lazily-computed hash value is cached in the object, to speed up comparisons, but it doesn't seem inappropriate for casts to be required when you're doing that.(1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const.It doesn't look like we can solve this by switching the constness of an Object.function, unless we also state that every function in Object must be const and accept the fact that no one would use these functions for other purposes like caching, late binding etc...
Feb 23 2011
Bruno Medeiros:The good news is that I suspect the fields used for opEquals/opCmp/opHash in any class are unlikely to be fields that are computed lazily. It's just a guess though, anyone have examples otherwise?If I have a string type, I'd like to compute its hash value lazily, the first time I need it. Computing it requires time, it's useful for sets and associative arrays, but being the string immutable the result doesn't change, so you want to compute it only once. So only the array of chars is immutable, while the hash is computed lazily and is not immutable (it 's "lazily immutable"). Bye, bearophile
Feb 23 2011
On 23/02/2011 17:50, bearophile wrote:Bruno Medeiros:(Hum, I was thinking more of the inputs to the functions, the data that is primarily used to determine the equality/comparison.) But that's a completely good and valid example nonetheless, in that it would not be uncommon for opHash to have some values computed lazily. But what about the other functions, opEquals/opCmp, any examples? -- Bruno Medeiros - Software EngineerThe good news is that I suspect the fields used for opEquals/opCmp/opHash in any class are unlikely to be fields that are computed lazily. It's just a guess though, anyone have examples otherwise?If I have a string type, I'd like to compute its hash value lazily, the first time I need it. Computing it requires time, it's useful for sets and associative arrays, but being the string immutable the result doesn't change, so you want to compute it only once. So only the array of chars is immutable, while the hash is computed lazily and is not immutable (it 's "lazily immutable"). Bye, bearophile
Feb 25 2011
On 02/10/2011 10:09 AM, so wrote:Is this point very annoying in practice? In my experience, such "language methods" like opEquals are typically small and simple. They do not call other funcs very often, except sometimes super(), or the opEquals of their members. When they call one 'normal' func, it can be one that barely has any other use than beeing called from there. The remaining cases of virality should be very rare, shouldn't they? Globally, I wouldn't care very much about the viral effect of const for such language methods, precisely. What do you think? denis -- _________________ vita es estrany spir.wikidot.com(1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const.It doesn't look like we can solve this by switching the constness of an Object.function,
Feb 10 2011
Don Wrote:Andrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?class LazyObject{ bool OpEquals(LazyObject); } class Object : LazyObject{ override bool OpEquals(LazyObject) const; bool opEquals(const Object) const; } By default, all classes derive from Object, but those that want to ignore viral const or implement lazy calculations can derive from LazyObject.
Feb 10 2011
On Thu, 10 Feb 2011 09:15:36 -0500, Jason House <jason.james.house gmail.com> wrote:Don Wrote:This doesn't work. What if the user overrides one and not the other? Or the semantics are different? Then const objects compare differently than non-const ones. -SteveAndrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?class LazyObject{ bool OpEquals(LazyObject); } class Object : LazyObject{ override bool OpEquals(LazyObject) const; bool opEquals(const Object) const; } By default, all classes derive from Object, but those that want to ignore viral const or implement lazy calculations can derive from LazyObject.
Feb 10 2011
Steven Schveighoffer Wrote:On Thu, 10 Feb 2011 09:15:36 -0500, Jason House <jason.james.house gmail.com> wrote:You can never stop programmers from writing incorrect code... The way I envision this, "bool opEquals(LazyObject) const" would rarely be overridden. I would recommend writing it in terms of the other function. Something akin to the following: bool OpEquals(LazyObject o) const{ auto x = cast(const Object) o; if (x is null) return false; return (this==x); } There may be a better way to implement that, but the general idea is that 99% of people overriding a class's OpEquals will return false for anything that is not an Object. I don't know D's overloading rules well enough... Which overload would be called for the following code? Object o1; Object o2; o1 == o2; Ideally, it'd call the const(Object) overload and not the LazyObject overload. I have a sneaking suspicion that D would consider it ambiguous and give a compile error. I'm sure that could be fixed if people like this solution.Don Wrote:This doesn't work. What if the user overrides one and not the other? Or the semantics are different? Then const objects compare differently than non-const ones. -SteveAndrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viral effect on all objects -- any function called by opEquals will have to be marked const. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?class LazyObject{ bool OpEquals(LazyObject); } class Object : LazyObject{ override bool OpEquals(LazyObject) const; bool opEquals(const Object) const; } By default, all classes derive from Object, but those that want to ignore viral const or implement lazy calculations can derive from LazyObject.
Feb 10 2011
On Thu, 10 Feb 2011 12:24:02 -0500, Jason House <jason.james.house gmail.com> wrote:Steven Schveighoffer Wrote:No but you can make things easier to avoid writing incorrect code. This kind of change invites mistakes, because it looks like you have to completely duplicate your logic, and forgetting to modify one and not the other happens to even seasoned veterans.On Thu, 10 Feb 2011 09:15:36 -0500, Jason House <jason.james.house gmail.com> wrote:You can never stop programmers from writing incorrect code...Don Wrote:effectAndrei once stated a worthy goal: as far as possible, const should be opt-in: it should be possible to code without requiring const on everything. opEquals() is in conflict with this, since it is a member function of Object. (1) If it is a const member function, then it will have a viralmarkedon all objects -- any function called by opEquals will have to beThis doesn't work. What if the user overrides one and not the other? Or the semantics are different? Then const objects compare differently than non-const ones. -Steveconst. (2) If it is not const, then const objects cannot be compared! Currently, it's not const, but the problem isn't visible because of compiler bug 5080. (Same problem applies to opCmp). How will we break this dilemma?class LazyObject{ bool OpEquals(LazyObject); } class Object : LazyObject{ override bool OpEquals(LazyObject) const; bool opEquals(const Object) const; } By default, all classes derive from Object, but those that want to ignore viral const or implement lazy calculations can derive from LazyObject.The way I envision this, "bool opEquals(LazyObject) const" would rarely be overridden. I would recommend writing it in terms of the other function. Something akin to the following: bool OpEquals(LazyObject o) const{ auto x = cast(const Object) o; if (x is null) return false; return (this==x); }This also doesn't work without extra effort -- overriding less than the full overload set results in runtime hidden function errors. You need to alias the base class' method, in *every* derived class. I don't really think adding extra complexity makes things better. I think a single overload for opEquals (which is const) is fine. Anything beyond that can use a separate method name, and you just call the method directly. -Steve
Feb 10 2011
On Thu, 10 Feb 2011 13:59:24 -0500, Steven Schveighoffer <schveiguy yahoo.com> wrote:No but you can make things easier to avoid writing incorrect code. This kind of change invites mistakes, because it looks like you have to completely duplicate your logic, and forgetting to modify one and not the other happens to even seasoned veterans.This should have said "forgetting to modify one when you modify the other" -Steve
Feb 10 2011
On Friday 11 February 2011 02:43:11 spir wrote:On 02/11/2011 07:13 AM, Jonathan M Davis wrote:structs are a different beast. The issue under discussion here is classes where polymorphism gets involved. There, the function signatures have to match. Now, structs are still problematic, because the compiler currently insists that their signature for opEquals look something like bool opEquals(const ref S s) const; Structs shouldn't be so picky. Pretty much anything named opEquals which takes one argument and returns bool should work. But that hasn't been fixed yet. Regardless, it's a separate issue from classes where polymorphism puts much stricter requirements on opEquals' signature. - Jonathan M DavisIn the worst case, people who really, really, want it it non-const are left with: struct S { ... bool equals (ref S s) {...} } ... if (s1.equals(s2)) {...} I do not find it /that/ terrible (esp.compared to many other wrokarounds we commonly are forced to use for various reasons). The one bad case is if S belongs to the interface to client code. In other words, this is not an acceptable solution for library public types. But since const opEquals (and same reasoning for other methods) is only annoying for structs that (1) need custom equality predicate (2) call other funcs, else as member opEquals, (3) which themselves may be called by yet other funcs (else virality does not spread), then I guess very few cases remain. What do you think?Hmm. You're right (I just tried it).We _must_ have it there, so anyone overriding those functions _must_> use it for those functions. They could create non-const versions > in addition to > the const ones,It is the whole point, they can't.
Feb 11 2011