Thread overview
Transition for removing functions from Object [Compiler devs in particular, please read]
May 27, 2013
Jonathan M Davis
May 28, 2013
Lionello Lunesu
May 28, 2013
Jonathan M Davis
May 28, 2013
Jacob Carlborg
May 28, 2013
Jacob Carlborg
May 28, 2013
Idan Arye
May 30, 2013
Jonathan M Davis
May 27, 2013
A few months back, we agreed that we'd be better of if we removed toString, toHash, opEquals, and opCmp from Object. They don't need to be there, and they're causing us havoc with attributes like const, pure, nothrow, and @safe, because every class object in all of D has to follow the attributes that Object has for them, which prevents certain idioms (and which idioms it prevents depends on what the attributes on Object's functions are). For instance, the only reason that comparing const  class objects even works right now is because druntime is casting away const to do the comparison (which is _not_ good). So, we're removing those functions. But that raises the question of what we have to do in order to do that with minimal code breakage.

There are four enhancement requests for this (one for each function):

http://d.puremagic.com/issues/show_bug.cgi?id=9769 http://d.puremagic.com/issues/show_bug.cgi?id=9770 http://d.puremagic.com/issues/show_bug.cgi?id=9771 http://d.puremagic.com/issues/show_bug.cgi?id=9772

Also, I have a pull request open

https://github.com/D-Programming-Language/druntime/pull/459

which will make it so that you can declare an opEquals on classes which compares your own type rather than Object (you'd presumably pick the least derived type in your hierarchy that you want to be able to compare). But this only partially solves the problem for opEquals and does nothing for the others.

We need a way to deprecate these functions on Object without breaking code. This is _almost_ possible. One solution would be to make all 4 of those functions free functions in object_.d so that they can be used with Object via UFCS. We then deprecate those functions. However, this doesn't quite work for three reasons:

1. You can't do UFCS with overloaded operators, and opEquals and opCmp are overloaded operators. In general, I think that it would be bad to be able to overload operators via UFCS (especially with functions that are as core as opEquals and opCmp), but if we could at least make it so that the compiler allowed it in this case until the deprecation process has been completed, then these functions could be UFCS-able.

2. super doesn't work with UFCS. Take this code for example

---------
import std.stdio;

class C { }

class D : C
{
    void foo() { writeln("D"); }
    void bar() { writeln(super.foo()); }
}

void foo(C c) { writeln("C"); }

void main()
{
    (new C).foo();
    (new D).foo();
    (new D).bar();
}
---------

Assuming that super worked with UFCS, it should print

C
D
C

but it just won't compile. giving a rather bizarre error

q.d(16): Error: template std.stdio.writeln does not match any function
template declaration. Candidates are:
/usr/include/D/phobos/std/stdio.d(1694):        std.stdio.writeln(T...)(T
args)
q.d(16): Error: template std.stdio.writeln(T...)(T args) cannot deduce
template function from argument types !()(void)

I'm not sure how much it makes sense for super to work with UFCS in general, but if we make the 4 functions in question free functions, any code which  was calling them via super will break. I'm not sure that that would be a frequent occurence (in fact, I have no idea why you'd ever want to do that given what those functions do on Object), but it _is_ a potential point of breakage.

3. The override keyword makes it so that making those functions free functions will break all code everywhere that uses them. override is fantastic for catching changes in class hierarchies so that you can fix your code when base classes change, but it does not give us a good deprecation path. So, if we were to make these functions free functions, the compiler would have to be adjusted so that in this particular case, it gave a message about it (and not a warning, as that would cause code to break with -w) rather than giving an error. I do think that this _should_ give an error normally, as you'd no longer be overriding anything, but we need to be able to not make it an error in this case if we want a smooth transition. override is intended for catching bugs quite loudly and not for smooth deprecation paths.


With all 3 of these issues, compiler changes are required, and save perhaps for #2 (super working with UFCS), I'd argue that they aren't changes that we'd want to have normally accept as part of this deprecation path, so I don't know how acceptable they are. Also, I have no idea how easy it would be to make these changes in the compiler. But this approach is the only one that I've been able to come up with for smoothly removing these functions from Object, which we really need to do (and sooner rather than later in order to minimize how much code has to be changed).

So, assuming that you managed to read all of this, what are your thoughts on this? Is there anything obvious I'm missing? How feasible do you think this is? And any ideas on how to improve upon what I'm suggesting?

- Jonathan M Davis
May 28, 2013
On 5/27/13 9:20, Jonathan M Davis wrote:
> class D : C
> {
>      void foo() { writeln("D"); }
>      void bar() { writeln(super.foo()); }
> }

I think this works just fine, you just have to drop the writeln(). foo() doesn't return anything, but prints itself.

But yet, super should just work: it should be a variable of type C and treated as such.

L.
May 28, 2013
On Tuesday, May 28, 2013 10:28:34 Lionello Lunesu wrote:
> On 5/27/13 9:20, Jonathan M Davis wrote:
> > class D : C
> > {
> > 
> >      void foo() { writeln("D"); }
> >      void bar() { writeln(super.foo()); }
> > 
> > }
> 
> I think this works just fine, you just have to drop the writeln(). foo()
> doesn't return anything, but prints itself.
> 
> But yet, super should just work: it should be a variable of type C and treated as such.

Oh, you're right. Apparently, I wrote that up too quickly. So, my issue #2 isn't a problem. super will work with UFCS. But that still leaves #1 and #3.

- Jonathan M Davis
May 28, 2013
On 2013-05-28 04:28, Lionello Lunesu wrote:

> But yet, super should just work: it should be a variable of type C and
> treated as such.

So super should work with UFCS?

-- 
/Jacob Carlborg
May 28, 2013
On 2013-05-28 04:28, Lionello Lunesu wrote:

> I think this works just fine, you just have to drop the writeln(). foo()
> doesn't return anything, but prints itself.
>
> But yet, super should just work: it should be a variable of type C and
> treated as such.

What about "this".

-- 
/Jacob Carlborg
May 28, 2013
On Tuesday, 28 May 2013 at 09:02:32 UTC, Jacob Carlborg wrote:
> On 2013-05-28 04:28, Lionello Lunesu wrote:
>
>> I think this works just fine, you just have to drop the writeln(). foo()
>> doesn't return anything, but prints itself.
>>
>> But yet, super should just work: it should be a variable of type C and
>> treated as such.
>
> What about "this".

`this` works with UFCS, but poses another problem:

    class C{
        void foo(){
            writeln(this.toString());//UFCS
            writeln(toString());//Won't work with UFCS
        }
    }
May 30, 2013
On Tuesday, May 28, 2013 15:36:49 Idan Arye wrote:
> On Tuesday, 28 May 2013 at 09:02:32 UTC, Jacob Carlborg wrote:
> > On 2013-05-28 04:28, Lionello Lunesu wrote:
> >> I think this works just fine, you just have to drop the
> >> writeln(). foo()
> >> doesn't return anything, but prints itself.
> >> 
> >> But yet, super should just work: it should be a variable of
> >> type C and
> >> treated as such.
> > 
> > What about "this".
> 
> `this` works with UFCS, but poses another problem:
> 
>      class C{
>          void foo(){
>              writeln(this.toString());//UFCS
>              writeln(toString());//Won't work with UFCS
>          }
>      }


Hmm. Well, that would only be a problem if toString (or one of the other 4 methods) was not overridden, and Object's versions of those functions are pretty useless, so it's unlikely that much code would do this, though worst case, we may be able to special case this in the compiler.

- Jonathan M Davis