www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Transition for removing functions from Object [Compiler devs in

reply Jonathan M Davis <jmdavisProg gmx.com> writes:
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 

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 26 2013
parent reply Lionello Lunesu <lionello lunesu.remove.com> writes:
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 27 2013
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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.
- Jonathan M Davis
May 27 2013
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
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
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
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
parent reply "Idan Arye" <GenericNPC gmail.com> writes:
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 28 2013
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
May 30 2013