Jump to page: 1 2
Thread overview
hiding a class property behind a method
Feb 22, 2014
luka8088
Feb 22, 2014
simendsjo
Feb 22, 2014
Ali Çehreli
Feb 22, 2014
Maxim Fomin
Feb 22, 2014
Ali Çehreli
Feb 22, 2014
simendsjo
Feb 22, 2014
Nynn007
Feb 22, 2014
Ali Çehreli
Feb 22, 2014
simendsjo
Feb 23, 2014
luka8088
Feb 23, 2014
luka8088
February 22, 2014
It seems to me that the following code should be illegal, but I am uncertain of it so I am posting here for a confirmation before I post it on bug tracker:


http://dpaste.dzfl.pl/dae728734cc6


import std.stdio;

class A {
  string x () { return "A"; };
}

class B : A {
  override string x () { return "B"; };
}

class C : A {
  string x = "C"; // should this be illegal?
}

void main () {
  A o1 = new B();
  writeln(o1.x); // B

  A o2 = new C();
  writeln(o2.x); // A
}
February 22, 2014
On 02/22/2014 06:21 PM, luka8088 wrote:
> import std.stdio;
>
> class A {
>    string x () { return "A"; };
> }
>
> class B : A {
>    override string x () { return "B"; };
> }
>
> class C : A {
>    string x = "C"; // should this be illegal?
> }
>
> void main () {
>    A o1 = new B();
>    writeln(o1.x); // B
>
>    A o2 = new C();
>    writeln(o2.x); // A
> }

Just an addition. The following line shows the problem:
  writeln((cast(C)o2).x); // C

February 22, 2014
On 02/22/2014 09:21 AM, luka8088 wrote:> It seems to me that the following code should be illegal, but I am
> uncertain of it so I am posting here for a confirmation before I post it
> on bug tracker:
>
>
> http://dpaste.dzfl.pl/dae728734cc6
>
>
> import std.stdio;
>
> class A {
>    string x () { return "A"; };
> }
>
> class B : A {
>    override string x () { return "B"; };
> }
>
> class C : A {
>    string x = "C"; // should this be illegal?
> }
>
> void main () {
>    A o1 = new B();
>    writeln(o1.x); // B
>
>    A o2 = new C();
>    writeln(o2.x); // A
> }
>

The code uses the two objects through the A interface and x() is a virtual function on that interface.

When the C interface is used then we get C.x, which happens to be hiding the x() function of the base class.

It looks normal to me.

Ali

February 22, 2014
On Saturday, 22 February 2014 at 17:41:58 UTC, Ali Çehreli wrote:
>
> The code uses the two objects through the A interface and x() is a virtual function on that interface.
>
> When the C interface is used then we get C.x, which happens to be hiding the x() function of the base class.
>
> It looks normal to me.
>
> Ali

Spec is silent on this, so this is indeed a question.

Actually A is not interface, so I don't understand why you mention it. And there is neither 'taking C interface' because static type is A, so A function is called, neither it hides function of the base class because it is base class function which is called. I don't understand you completely.

AFAIK this feature exists for many years, at least 3, possibly roots to D1. What happens is follows: since there is no function, base class virtual is not replaced, so virtual table of C looks like A, so A member function is called.

If example is modified, then

import std.stdio;

class A {
  //string x () { return "A"; };
	string x = "A";
}

class B : A {
  //override string x () { return "B"; };
	string x = "B";
}

class C : A {
  //string x = "C"; // should this be illegal?
	string x () { return "C"; }
}

void main () {
  A o1 = new B();
  A o2 = new C();
  writeln(o1.x); // A
  writeln(o2.x); //A
}

so it appears that data member have priority over function.

Probably this should be filed as a spec or compiler bug.
February 22, 2014
> The code uses the two objects through the A interface and x() is a virtual function on that interface.
[...]
> Ali

The book "Programming in D" (r651) says in chapter "57.7 Using the subclass in place of the superclass", in the example about Clock and AlarmClock :

    void use(Clock clock) { ... }
(sic) "In other words, although use() uses the object as a Clock, the actual object may be an inherited type that behaves in its own special way".

Should'nt we understand that the first object is a B, the second object is a C and then should both behave like a B and a C, not like two A ?
February 22, 2014
On 02/22/2014 12:06 PM, Nynn007 wrote:

>> The code uses the two objects through the A interface and x() is a
>> virtual function on that interface.
> [...]
>> Ali

I agree. :)

>
> The book "Programming in D" (r651) says in chapter "57.7 Using the
> subclass in place of the superclass", in the example about Clock and
> AlarmClock :
>
>      void use(Clock clock) { ... }
> (sic) "In other words, although use() uses the object as a Clock, the
> actual object may be an inherited type that behaves in its own special
> way".

I obviously agree with that as well. :)

>
> Should'nt we understand that the first object is a B, the second object
> is a C and then should both behave like a B and a C, not like two A ?

You are correct. What I meant above is that the code uses the two object as two As, which involves the "interface" of A. The behaviors may be different.

Ali

February 22, 2014
On 02/22/2014 10:00 AM, Maxim Fomin wrote:

> On Saturday, 22 February 2014 at 17:41:58 UTC, Ali Çehreli wrote:
>>
>> The code uses the two objects through the A interface and x() is a
>> virtual function on that interface.
>>
>> When the C interface is used then we get C.x, which happens to be
>> hiding the x() function of the base class.

Sorry. I meant "If the C interface is used", not "When the".

>>
>> It looks normal to me.
>>
>> Ali
>
> Spec is silent on this, so this is indeed a question.
>
> Actually A is not interface, so I don't understand why you mention it.

I did not mean D's feature 'interface'. The code explicitly specifies the objects as As, comitting to A's class interface. (As in, every used defined type defines an interface.)

> And there is neither 'taking C interface' because static type is A, so A
> function is called, neither it hides function of the base class because
> it is base class function which is called. I don't understand you
> completely.

I agree with all of that.

> since there is no function, base class
> virtual is not replaced, so virtual table of C looks like A, so A member
> function is called.

Exactly. Otherwise, when faced with such a situation the compiler would have to synthesize a virtual function for C's virtual table.

string x()
{
    return member_x;
}

> so it appears that data member have priority over function.

It looks like name hiding, which I am familiar from C++. Name hiding does not differentiate between functions and variables.

Ali

February 22, 2014
On 02/22/2014 09:43 PM, Ali Çehreli wrote:
> It looks like name hiding, which I am familiar from C++. Name hiding
> does not differentiate between functions and variables.
>
> Ali
>

The problem is that hiding a name *is* a problem. When you are hiding a name,
then a class would no longer behave as you would expect. It breaks LSP in
a pretty awful way, and suddenly the *type* of a symbol changes based on what
superclass you happened to use for a reference.

    class A {
        void f() {}
    }

    class B : A {
        int f;
    }

    A b = new B();
    writeln(typeof(b.f).stringof); // void()

    B veryB = cast(B)b;
    writeln(typeof(veryB.f).stringof); // int

Now suddenly, it's very important to use B as the type for a reference. This is
very dangerous behavior in my opinion, and I think I've only used it *once* in
C# - which requires you to be explicit about it - and I still feel dirty.

Now what if a superclass implements a symbol that you are using in a subclass?
I say we force it to be explicit as we finally did with `override`, which is
shows some of the same issues, although not nearly as dangerous and hidden.
I think member hiding is nearly always a bug, and we should be very explicit
about it.
February 22, 2014
On Saturday, 22 February 2014 at 17:21:50 UTC, luka8088 wrote:
> It seems to me that the following code should be illegal, but I am
> uncertain of it so I am posting here for a confirmation before I post it
> on bug tracker:
[snip]

Nice find. I guess we could add this to the list of "ugly code caused by calling functions without parenthesis. If parenthesis were not optional, I don't think that the code would behave in such a horrible way, right?
February 22, 2014
On 02/22/2014 11:33 PM, Francesco Cattoglio wrote:
> On Saturday, 22 February 2014 at 17:21:50 UTC, luka8088 wrote:
>> It seems to me that the following code should be illegal, but I am
>> uncertain of it so I am posting here for a confirmation before I post it
>> on bug tracker:
> [snip]
>
> Nice find. I guess we could add this to the list of "ugly code caused by
> calling functions without parenthesis. If parenthesis were not optional,
> I don't think that the code would behave in such a horrible way, right?

The problem isn't about optional parenthesis or properties. It's the fact that
you can redefine a symbol to be something entierly different, and that this
difference will only be seen if you are looking at the symbol through the
"correct" type.

C# also allows has this feature, but you have to state explicitly that you are
hiding an existing symbol. D as a safe-by-default language should also require
this.
« First   ‹ Prev
1 2