July 12, 2012
On 7/12/12 2:50 PM, H. S. Teoh wrote:
> On Thu, Jul 12, 2012 at 01:51:31PM -0400, Andrei Alexandrescu wrote:
>> On 7/12/12 1:40 PM, David Piepgrass wrote:
>>> 1. Most importantly, the C++ template approach is a big pain for
>>> large-scale systems, [...]
>>
>> The thing is, that can be done in an opt-in manner. People who want
>> methods in the root of the hierarchy can define a root that defines
>> them. But there's no way to opt out of inheriting Object. Basically
>> it's nice to not force people to buy into a constrained environment
>> without necessity.
>
> Having a class RawObject as a superclass of Object is an equally good
> solution.

As far as backward compatibility goes, I'm not sure. There's code out there that e.g. assumes Object has no supertype etc. (I wrote some.)

But one thing the recent discussion brought back to attention is that opEquals and opCmp are somewhat crappy. In D, they are in fact unnecessary, so it's better to undo that entire design if we can.


Andrei


July 12, 2012
On 2012-07-12 20:50, H. S. Teoh wrote:

> Having a class RawObject as a superclass of Object is an equally good
> solution. Declare a class without a base class, and the base class
> defaults to Object.  Explicitly write "class MyClass : RawObject" and
> you get a class without the stuff in Object. If you want an entire
> hierarchy free of the stuff in Object, just write "class MyBaseClass :
> RawObject" and inherit everything from it.
>
> This has the advantage of _not_ breaking any existing code, and the
> people who want to opt out of Object, can.

Exactly, this is also what Ruby 1.9 does.

-- 
/Jacob Carlborg


July 12, 2012
On 2012-07-12 16:28, kenji hara wrote:
> Is this really need?
>
> The four const operators in Object should not block the user-defined
> mutable operators.
>
> // My purpose for 2.060 release
> class C {
>    override opEquals(const Object o) const { ... } // or just alias
> super.opEquals opEquals;
>    bool opEquals(Object o) { ... } // add overload for mutable object comparison
> }
>
> auto c1 = new C(), c2 = new C2();
> c1 == c2;   // the both hand side is mutable, so mutable opEquals will run
>
> In git head, it is not disallowed, but it is a *compiler bug*.
> To fix the problem, I have a pull request for dmd.
> https://github.com/D-Programming-Language/dmd/pull/1042
> (The pull will kill attribute inference for const, but I think it is
> unnecessary for D.)
>
> ...But, I would never opposed to advancing toward the better language design.
>
> Kenji Hara

I would have thought that that already worked.

-- 
/Jacob Carlborg


July 12, 2012
On Thursday, July 12, 2012 21:11:54 Jacob Carlborg wrote:
> On 2012-07-12 16:28, kenji hara wrote:
> > Is this really need?
> > 
> > The four const operators in Object should not block the user-defined mutable operators.
> > 
> > // My purpose for 2.060 release
> > class C {
> > 
> > override opEquals(const Object o) const { ... } // or just alias
> > 
> > super.opEquals opEquals;
> > 
> > bool opEquals(Object o) { ... } // add overload for mutable object
> > comparison>
> > }
> > 
> > auto c1 = new C(), c2 = new C2();
> > c1 == c2; // the both hand side is mutable, so mutable opEquals will run
> > 
> > In git head, it is not disallowed, but it is a *compiler bug*.
> > To fix the problem, I have a pull request for dmd.
> > https://github.com/D-Programming-Language/dmd/pull/1042
> > (The pull will kill attribute inference for const, but I think it is
> > unnecessary for D.)
> > 
> > ...But, I would never opposed to advancing toward the better language design.
> > 
> > Kenji Hara
> 
> I would have thought that that already worked.

The recently added attribute inferrence for overridden functions in derived types makes it impossible to have a non-const overload of a const function.

- Jonathan M Davis
July 12, 2012
On Thursday, July 12, 2012 20:42:49 David Piepgrass wrote:
> - 'const' is not overly harsh if the user has machanisms to make that mean 'logical const'.

That will _never_ happen. That completely destroys a number of the benefits of const, and it adds quite a bit of cognitive load in dealing with const, because all of a sudden, you have to worry about whether _this_ const means actual const or logical const. It would also complicate the compiler's handling of const by quite a bit. Not to mention, Walter hates logical const in general, so he wouldn't support it. So, no, it's not going to happen.

If we get any kind of logical const, it's going to be separate from const. const always has been and always will be physical const in D.

- Jonathan M Davis
July 12, 2012
On Thursday, July 12, 2012 17:47:10 Mehrdad wrote:
> So if you're saying you can't use const with OOP, then I'm saying one of those needs to be fixed, and I was suggesting the former as a candidate.

You can use const and OOP together just fine, but that means that if you have a function which is marked as const in the base class, and you're operating on the objects through the base class pointer, then all of the derived classes must be able to have that function as const. In general, I really don' think that that's a big deal. The problem is that opEquals, opCmp, toHash, and toString affect _all_ classes, because they're in Object, and that unnecessarily restricts all classes. const is forced on you, and it's forced on you with incredibly common functions in a way that completely disallows some idioms (e.g. caching and lazy loading).

On the other hand, if you're dealing with your own class hierarchy, you can choose what you're going to mark as const or not, and so you can either forgoe const entirely or only use it on functions where you can reasonably require that they be const in all classes in that hierarchy. It's not being forced on you, and you can pick what works best for your set of classes.

The fact that const is restrictive isn't the problem. It's the fact that const is forced on you which is. As long as you have the choice whether to use it or not, then it's fine.

- Jonathan M Davis
July 12, 2012
On Thu, 12 Jul 2012 14:43:57 -0400, Jacob Carlborg <doob@me.com> wrote:

> On 2012-07-12 15:39, Steven Schveighoffer wrote:
>
>> I think if we want a solution that allows old code to work, why not what
>> Timon suggested? Have a base class for Object (RawObject was suggested)
>> that does not implement the opFunctions.  It would still break code, but
>> would be easy to fix (just specify your class derives from Object, not
>> RawObject).
>
> Wouldn't the default be to inherit from Object?
>
> Like this:
>
> class RawObject {}
> class Object : RawObject {}
> class Foo {} // inherits from Object by default.
>
> Most people would not need to change anything, they can continue to use Object. If they want to avoid the methods declared in Object they need to explicitly inherit from RawObject.

Many (most?) classes never care about opHash, opCmp, opEquals and toString.  But Object defines them, incorrectly for most cases.

These apathetic classes would not break at all.  And to make the default be to inherit those methods would promote their usage or reliance on them.

Not only that, but you are almost *forced* to define them, because you don't want accidental incorrect usage of them.  We have lovely situations where the only solution is to define a version of the method that *always throws* in a statically typed language.

It's really a terrible solution (to force the definition of them) which Andrei quite correctly pointed out only existed because of the lack of templates back then.

I think this discussion is somewhat academic at this point, as Andrei seems not too keen on the idea of having dual base classes.

-Steve
July 12, 2012
On 7/12/12 4:20 PM, Steven Schveighoffer wrote:
> I think this discussion is somewhat academic at this point, as Andrei
> seems not too keen on the idea of having dual base classes.

Well I wasn't keen on eliminating the four methods and look what happened!

Andrei
July 12, 2012
On Thu, 12 Jul 2012 16:27:39 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 7/12/12 4:20 PM, Steven Schveighoffer wrote:
>> I think this discussion is somewhat academic at this point, as Andrei
>> seems not too keen on the idea of having dual base classes.
>
> Well I wasn't keen on eliminating the four methods and look what happened!

My personal opinion is we should simply eliminate the four methods (or at least the three required for AAs), fix AAs, and deal with the fallout.  I can't really remember the last time I simply used obj1.opEquals(obj2) to do comparisons instead of obj1 == obj2 (which should do the right thing if obj1.opEquals(obj2) is valid).  The code that relies on this is probably very rare.  I certainly would *love* to rewrite all my opCmp and opEquals functions to accept the minimal base class instead of doing the dual dispatch dance with Object parameters.

I'm also actually not liking using Object as the scorned child of RawObject, I'd rather keep Object as the base, and use something like OldObject as a different base class, or maybe use an interface.

I still am not keen on having a runtime vtable comparison to see if we want to mimic old behavior, how does one declare "this comparison isn't valid" to the compiler?  That is one of the main benefits I see with dumping the methods.

-Steve
July 12, 2012
On Thursday, July 12, 2012 16:50:21 Steven Schveighoffer wrote:
> I can't really remember the last time I simply used obj1.opEquals(obj2) to
> do comparisons instead of obj1 == obj2 (which should do the right thing if
> obj1.opEquals(obj2) is valid). The code that relies on this is probably
> very rare.

It's almost certainly bad code anyway. The free function version of opEquals specifically does extra work to make equality checks correct and avoids some of the pitfalls that opEquals causes in Java (e.g. doing comparison in both directions if the types aren't identical). So, if we break that, it's probably a _good_ thing. And if they _really_ want to do that, that can still do it with their derived classes which define opEquals. They just can't do it with Object.

- Jonathan M Davis