www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 3659] New: Too much exegesis on opEquals

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3659

           Summary: Too much exegesis on opEquals
           Product: D
           Version: unspecified
          Platform: Other
        OS/Version: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: nobody puremagic.com
        ReportedBy: andrei metalanguage.com


--- Comment #0 from Andrei Alexandrescu <andrei metalanguage.com> 2009-12-30
09:14:06 PST ---
struct A {
    bool opEquals(int x) {
        return false;
    }
}

Compiler says:

Error: function test.A.opEquals type signature should be const bool(ref
const(A)) not bool(int x)

The compiler should accept any signature for opEquals. All it needs to do is
rewrite a == b into a.opEquals(b) or b.opEquals(a).

For classes I'm doubtful that opEquals needs to be const.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 30 2009
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3659


Richard Webb <webby beardmouse.org.uk> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |webby beardmouse.org.uk


--- Comment #1 from Richard Webb <webby beardmouse.org.uk> 2009-12-30 09:42:14
PST ---
Same as #3607 ?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3659


Steven Schveighoffer <schveiguy yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy yahoo.com


--- Comment #2 from Steven Schveighoffer <schveiguy yahoo.com> 2009-12-31
05:51:24 PST ---
Here is the issue.  The compiler now is required to generate an opEquals which
calls x.m == y.m on all members m of the struct (this was to fix bug 3433). 
However, in order for this opEquals to handle const and immutable versions of
the struct, the opEquals generated needs to be const.  Given that, if member m
is another struct with a *user defined* opEquals, that opEquals must *also* be
const.

The current rule is too strict, I agree.  For instance, you should not require
that the argument be ref const if the argument type can be implicitly cast from
const to mutable (i.e. a pure value type), but if the argument is ref, it
*should* be const, and the opEquals function itself *should* be const. 
Otherwise, you could be changing stuff just by doing a comparison.

What is the use case for an opEquals not being const?

Another alternative is to allow any signature for opEquals on type T, but then
any type U which has a member of type T will be illegal to compare unless type
U also defines opEquals.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 31 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3659



--- Comment #3 from Andrei Alexandrescu <andrei metalanguage.com> 2009-12-31
07:37:33 PST ---
(In reply to comment #2)
 Here is the issue.  The compiler now is required to generate an opEquals which
 calls x.m == y.m on all members m of the struct (this was to fix bug 3433). 
 However, in order for this opEquals to handle const and immutable versions of
 the struct, the opEquals generated needs to be const.  Given that, if member m
 is another struct with a *user defined* opEquals, that opEquals must *also* be
 const.

I see, thanks for explaining.
 The current rule is too strict, I agree.  For instance, you should not require
 that the argument be ref const if the argument type can be implicitly cast from
 const to mutable (i.e. a pure value type), but if the argument is ref, it
 *should* be const, and the opEquals function itself *should* be const. 
 Otherwise, you could be changing stuff just by doing a comparison.
 
 What is the use case for an opEquals not being const?

I'm thinking of our current stance on const: if you don't care about const, don't use it and for the most part it won't intrude on you. For example, string's use of immutability is fairly confined. opEquals is a stark deviation from the stance above. It *will* intrude. The classic example is this: class Widget { bool opEquals(Widget); } Compiling this issues: Warning: object.Object.opEquals(Object o) is hidden by Widget It only gets downhill from there: struct Widget { bool opEquals(Widget) { return true; } } This time it's an error: Error: function test.Widget.opEquals type signature should be const bool(ref const(Widget)) not bool(ref Widget) So you simply can't "not care" about const. But then it's all getting viral because cons is viral. Consider the user grudgingly agrees to add const, and then... class Widget { private Gadget g; bool opEquals(const Widget rhs) { return compatibleGadgets(g, rhs.g); } } But now it's still not fine because compatibleGadgets is also written without caring about const. It's all a mess. Now consider that the user capitulates and decides to use const wherever applicable. Things will still not work in certain cases. For example if opEquals must compare members that are lazily computed, you can't make it compile. Cheating by casting away const will mess up a variety of assumptions. As an aside, I know what it takes to define lazily computed state to work with const, but Walter is at the bottom of a 5000 TeV potential hole that spells like "this is like C++ mutable and C++ mutable is incorrect, therefore I will not process any information henceforth". So I am unable to even start explaining that to him. Besides, assuming Walter is convinced of the correctness of the feature, it's unclear whether it will pull its weight. It will complicate the language, and the benefits, while there, are rather subtle. So I don't know what to do. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 31 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3659



--- Comment #4 from Steven Schveighoffer <schveiguy yahoo.com> 2009-12-31
09:45:21 PST ---
(In reply to comment #3)
 I'm thinking of our current stance on const: if you don't care about const,
 don't use it and for the most part it won't intrude on you. For example,
 string's use of immutability is fairly confined.

Well, that wasn't the case not too long ago. For example, all std.string functions used strings as args, making doing simple things like searching for substrings in mutable strings impossible. I think if we work harder, we may find some acceptable middle ground.
 opEquals is a stark deviation from the stance above. It *will* intrude. The
 classic example is this:
 
 class Widget {
     bool opEquals(Widget);
 }    

Structs and classes are different stories. A class is always passed by reference, so I think it must always be const as an argument to an opEquals. This goes with my assertion above. The counter-argument to having opEquals not be const on Object is then you cannot compare 2 const objects without defining separate functions.
 
 So you simply can't "not care" about const. But then it's all getting viral
 because cons is viral. Consider the user grudgingly agrees to add const, and
 then...
 
 class Widget {
     private Gadget g;
     bool opEquals(const Widget rhs) {
         return compatibleGadgets(g, rhs.g);
     }
 }
 
 But now it's still not fine because compatibleGadgets is also written without
 caring about const. It's all a mess. 

There is no way around it. Const is viral, and in order to guarantee what it does, it has to be. The proposed inout helps in regards to not making things const when they don't have to be, but at some point, the compiler has to either give in (i.e. C++ mutable) or put its foot down. Have you considered the other alternative that you didn't copy in reply: "Another alternative is to allow any signature for opEquals on type T, but then any type U which has a member of type T will be illegal to compare unless type U also defines opEquals." I think this is a reasonable compromise, and doesn't require const signatures. The thing I don't like about the current solution is it requires a certain signature even if you *never* include it as a member of another aggregate, just in case the compiler has to write an opEquals around it.
 Now consider that the user capitulates and decides to use const wherever
 applicable. Things will still not work in certain cases. For example if
 opEquals must compare members that are lazily computed, you can't make it
 compile. Cheating by casting away const will mess up a variety of assumptions.
 
 As an aside, I know what it takes to define lazily computed state to work with
 const, but Walter is at the bottom of a 5000 TeV potential hole that spells
 like "this is like C++ mutable and C++ mutable is incorrect, therefore I will
 not process any information henceforth". So I am unable to even start
 explaining that to him. Besides, assuming Walter is convinced of the
 correctness of the feature, it's unclear whether it will pull its weight. It
 will complicate the language, and the benefits, while there, are rather subtle.

This is logical const all over again :) Note that I think this is possibly the only use case for opEquals not being const. To compare 2 items and have them visibly change is against any expectation I've ever had. But this doesn't mean we need to remove const from the signature, it just means we need to find a way to make logical const work. I remember arguing this with Janice a while back, Walter never participated, and you were MIA. Is there a possible library solution? i.e. something like: private mutable!(Gadget) g; Where g is always mutable, no matter what it's container's mutability (i.e. contain the dangerous const casts to a library type). I'm sure there are some template wizards out there who can make this happen, especially with opDispatch now :) -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Dec 31 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3659


Tomasz Sowiński <tomeksowi gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |tomeksowi gmail.com


--- Comment #5 from Tomasz Sowiński <tomeksowi gmail.com> 2010-01-20 11:27:48
PST ---
Another quirk caused by forcing const in opEquals is bug 3729.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 20 2010
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3659


Don <clugdbug yahoo.com.au> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |rejects-valid, spec
                 CC|                            |clugdbug yahoo.com.au
            Version|unspecified                 |D2
           Severity|normal                      |blocker


--- Comment #6 from Don <clugdbug yahoo.com.au> 2010-12-06 00:38:51 PST ---
Raising severity, as I think this is one of the crucial unresolved issues in
the const system.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Dec 06 2010