July 24, 2004
Thanks Walter;

Instead of falling back on tradition, why don't you look at it afresh? The
salient point in this respect was as follows:
"A subclass is expected to inherit all methods from its super-class, unless
declared private therein. The compiler is expected to provide override
support where signatures match exactly. As such, the required use of alias
for this fundamental behavior is non-intuitive, inappropriate, and defies
commonly accepted OO practices"

You clearly don't agree with this, yet you don't question for a moment that the use of alias in this respect is, in fact, completely non-intuitive. Nor do you appear to think that perhaps there might be a better way to handle it. D can do a lot better than your example below. Just as Java can! This is clearly a special case scenario; I'm saying it shouldn't be.

What on earth is the benefit?

I implore you to please explain why it's so beneficial to hide these signatures, from the developers perspective? At least that would provide a solid counter-point.

Regards;

- Kris




"Walter" <newshound@digitalmars.com> wrote in message news:cdt9m6$puh$1@digitaldaemon.com...
> You describe it as "The latter is utter nonsense, and smacks of either an implementation-specific hack or a half-baked and headless C-style implementation of method hiding." However, consider the following C++ program:
>
> --------------------------------------
> C:\cbx>type foo.cpp
>
> #include <stdio.h>
>
> struct X { };
>
> struct A
> {
>         int foo(void *p) { return 1; }
>         int foo(struct X x) { return 2; }
> };
>
> struct B : A
> {
> //      using A::foo;
>         int foo(int i) { return 3; }
> };
>
> void main()
> {
>     B b;
>     X x;
>     int i;
>
>     i = b.foo((void*)0);
>     printf("i = %d\n", i);
>     i = b.foo(x);
>     printf("i = %d\n", i);
>     i = b.foo(1);
>     printf("i = %d\n", i);
> }
>
> C:\cbx>sc foo
>     i = b.foo((void*)0);
>                       ^
> foo.cpp(24) : Error: need explicit cast for function parameter 1 to get
> from: void *
> to  : int
>     i = b.foo(x);
>                ^
> foo.cpp(26) : Error: need explicit cast for function parameter 1 to get
> from: X
> to  : int
> --- errorlevel 1
>
> C:\cbx>
> -------------------------------------------
> Now uncomment the 'using' declaration, and the program will compile successfully and produce the 1, 2, 3 output. Therefore, D uses overloading/hiding rules that are completely analogous to C++. I've read a lot of critiques of C++, and this has not been mentioned. Or perhaps my
C++
> compiler has an egregious fault in it, but I find it hard to believe it would have lasted this long with such a fundamental flaw <g>.
>
>


July 24, 2004
Also, please note that following doesn't even compile:

class Writer
{
        void put (bit x){}
        void put (int x){}
}

class MyWriter : Writer
{
        alias Writer.put put;

        void put (bit x){}
}

"function put conflicts with MyWriter.put"



July 24, 2004
Okay, so I screwed up the example slightly. Please permit me to rectify that:

interface IFoo
{
    void foo ();
}


interface IBar
{
    void bar ();
}

class Foo
{
   void foo (){}
}

class Bar : Foo, IFoo, IBar
{
    void bar() {}
}

There have been reasons indicated as to why one would want to include super-class methods as part of a contract exposed by a sub-class. There hasn't yet been a single one to the contrary ...

How about it Walter? I'd really, really, like to understand why super-class methods are explicitly denied from satisfying a sub-class contract. At least then there'd be something to discuss vis-a-vis pro & con ...

- Kris




> > Here is the code in your example:
> >
> > <code>
> >  interface IFoo
> >  {
> >   void foo ();
> >  }
> >
> >  interface IBar
> >  {
> >   void bar ();
> >  }
> >
> >  class Foo : IFoo
> >  {
> >   void foo ()
> >   {
> >   }
> >  }
> >
> >  class Bar : Foo, IFoo, IBar
> >  {
> >   void bar()
> >   {
> >   }
> >  }
> > </code>
> >
> > The problem is with class Bar. Here you explictly say that it is derived from class Foo, and must implement interface IFoo and IBar. However, you have only got it implementing bar(). The foo() is implemented in Foo but not in Bar. You are obviously expecting implemented interfaces to be inherited. Now although the foo() function IS inherited by Bar, you have also said it must be *implemented* in Bar too, but you didn't implement
> it.
>
> You're correct, it is deliberately designed to work this way. To get it to work the other way, just rewrite Bar as:
>
>     class Bar : Foo, IBar



July 24, 2004
"Walter" <newshound@digitalmars.com> wrote in message news:cdtapd$q5m$1@digitaldaemon.com...
> Let me explain what is happening here.
>
> In FileConduit, you have an import declaration, which brings a bunch of names into FileConduit's scope. How name lookup happens in a scope is a multi-stage process, each proceeding to the next if the name is not found
in
> the current stage:
>
>     1) look for a member with the name
>     2) look in any imports *in this scope*
>     3) if current scope is a class scope, recursively look in base classes
>     4) repeat the process with the most enclosing scope
>
> I think it would be very strange to have base class symbols override
imports
> in the current scope, i.e. if imports were looked up in some other scope than the one in which they were imported.
>
> The solution is straightforward. Move the import declaration outside of
the
> class. Then it won't override base class names. There's no need whatsoever to refactor class heirarchies. By where you place the import declaration, you specify when the lookups for the names in the import happen.


That's wonderful! However, you avoided a significant attribute of the issue in that signature matching is abandoned during the matching process. That is, there is no notion of looking to see if there's a better 'signature' match. As I stated; the import of those two linux function names blows away the other signatures. This seems like a half-way house, but I can certainly /imagine/ why your compiler might choose not to do that.

Perhaps the most serious problem is that said issue does not manifest within
FileConduit itself. It's only when a call is placed (from some other module)
to either read() or close() that the compile breaks. I thought John Reimer
stated it rather well in his post (7/24/2004,12:41) on the subject.

Lastly: You, and many others, are acutely aware of the problems related to forward references. Placing the import inside the class scope is currently the /only/ way to resolve some of those. Now we have to move imports outside the class-scope just so we can call certain methods therein. Do you see the conflict here?

What I personally find strange, Walter, is that such conflicts just don't seem to register as valid problems. At least now we know what the compiler does.


July 24, 2004
"Sean Kelly" <sean@f4.ca> wrote in message news:cdu0jn$12bs$1@digitaldaemon.com...
> In article <cdt9m6$puh$1@digitaldaemon.com>, Walter says...
> >
> > Therefore, D uses
> >overloading/hiding rules that are completely analogous to C++. I've read
a
> >lot of critiques of C++, and this has not been mentioned. Or perhaps my
C++
> >compiler has an egregious fault in it, but I find it hard to believe it would have lasted this long with such a fundamental flaw <g>.
>
> Agreed.  The resolution rules might be complex but I think they make
perfect
> sense once understood.  And I personally find the consistency with C++ to
be a
> good thing.
>
> Sean

Sean; would you mind explaining the benefits of those particular resolution rules to me please? While I may be both blind and stupid, I completely fail to see where the value lies, and no-one has specified what they are.

Thanks;


July 24, 2004
In article <cdu5f0$14dm$1@digitaldaemon.com>, Kris says...
>
>Also, please note that following doesn't even compile:
>
>class Writer
>{
>        void put (bit x){}
>        void put (int x){}
>}
>
>class MyWriter : Writer
>{
>        alias Writer.put put;
>
>        void put (bit x){}
>}
>
>"function put conflicts with MyWriter.put"

I'd forgotten about this detail.  And adding override doesn't help.  Okay so I do have a gripe or two with C++ name resolution rules ;)


Sean


July 24, 2004
Kris wrote:
> Also, please note that following doesn't even compile:
> 
> class Writer
> {
>         void put (bit x){}
>         void put (int x){}
> }
> 
> class MyWriter : Writer
> {
>         alias Writer.put put;
> 
>         void put (bit x){}
> }
> 
> "function put conflicts with MyWriter.put"

I'm not saying you're wrong on the broader issues (I'm still trying to understand the various viewpoints), but this problem seems to be solvable by moving the alias to the *end*.

Don't ask me to explain it or justify it (I can't), but maybe this specific case is more of a compiler bug. I think Walter's a lot more flexible about fixing compiler bugs that rewriting his carefully designed lookup rules.

Here's my short (yet complete) code:

class MyWriter : Writer
{
    void put (bit x)
    {
        printf("MyWriter.put(bit x)\n");
    }
    alias Writer.put put;
}

void main()
{
    bit b;
    int i;

    Writer w = new Writer();
    MyWriter m = new MyWriter();

    w.put(b);
    w.put(i);
    m.put(b);
    m.put(i);
}

-- 
Justin (a/k/a jcc7)
http://jcc_7.tripod.com/d/
July 24, 2004
Kris wrote:
> "Sean Kelly" <sean@f4.ca> wrote in message
> news:cdu0jn$12bs$1@digitaldaemon.com...
> 
>>In article <cdt9m6$puh$1@digitaldaemon.com>, Walter says...
>>
>>>Therefore, D uses
>>>overloading/hiding rules that are completely analogous to C++. I've read
> 
> a
> 
>>>lot of critiques of C++, and this has not been mentioned. Or perhaps my
> 
> C++
> 
>>>compiler has an egregious fault in it, but I find it hard to believe it
>>>would have lasted this long with such a fundamental flaw <g>.
>>
>>Agreed.  The resolution rules might be complex but I think they make
> 
> perfect
> 
>>sense once understood.  And I personally find the consistency with C++ to
> 
> be a
> 
>>good thing.
>>
>>Sean
> 
> 
> Sean; would you mind explaining the benefits of those particular resolution
> rules to me please? While I may be both blind and stupid, I completely fail
> to see where the value lies, and no-one has specified what they are.
> 
> Thanks;

As I wrote in http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/7011, I'm still not sure which side I'm on in this debate.

I've often read people on this newsgroup lobbying for the compiler to *require* the programmer to do things the hard way to keep the programmer from accidentily doing things wrong. Before the alias is added, a person might remember that they meant to add ISomeInterface to the base class anyways. It's a hypothetical, but it's also a possible benefit.

I am somewhat concerned that leaving out that alias can still silently fail to do what it's expected to do. For example, if I leave the alias out of the example below (based upon your example of course), in the MyWriter m.put(b) call, the bit b would be converted into a int. Oops.

As I also mentioned in the other thread (http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/7011), the aliasing works if it's AFTER the new types. I don't know if it should be that way or has to be that way, but that's how it works in DMD 0.96.


interface ISomeInterface{}

class Writer
{
        Writer put (bit x)
        {
            printf("Writer.put(bit x)\n");
            return null;
        }
        Writer put (int x)
        {
            printf("Writer.put(int x)\n");
            return null;
        }
}


class MyWriter : Writer
{
    Writer put (int x)
    {
        printf("MyWriter.put(int x)\n");
        return null;
    }

    Writer put (ISomeInterface x)
    {
        printf("Writer.put(ISomeInterface x)\n");
        return null;
    }
    alias Writer.put put;
}


void main()
{
    ISomeInterface I;
    int i;
    bit b;
    Writer w = new Writer();
    MyWriter m = new MyWriter();

    w.put(i);
    w.put(b);

    m.put(I);
    m.put(i);
    m.put(b);
}

Output:
Writer.put(int x)
Writer.put(bit x)
Writer.put(ISomeInterface x)
MyWriter.put(int x)
Writer.put(bit x)


-- 
Justin (a/k/a jcc7)
http://jcc_7.tripod.com/d/
July 24, 2004
Hi Justin: inline

"J C Calvarese"wrote ...
> I've often read people on this newsgroup lobbying for the compiler to *require* the programmer to do things the hard way to keep the programmer from accidentily doing things wrong. Before the alias is

If your referring to me, then yes; I do advocate such things. But in those where I do, it's when there's no other valid context (or, at least no-one has come up with anything to the contrary).

> added, a person might remember that they meant to add ISomeInterface to the base class anyways. It's a hypothetical, but it's also a possible benefit.

The compiler would simply spit out an error saying it couldn't find a put(ISomeInterface), just as it does today within the scope of a self-contained class ~ the benefit of strongly-typed language. (BCPL would silently eat it).

> I am somewhat concerned that leaving out that alias can still silently fail to do what it's expected to do. For example, if I leave the alias out of the example below (based upon your example of course), in the MyWriter m.put(b) call, the bit b would be converted into a int. Oops.

That's not what I'm suggesting at all Justin. From where I stand, all inherited signatures are still valid and the compiler picks the most appropriate one: Writer.put(bit) in your example. In other words, it should do /exactly/ what it would do today within the scope of a self-contained class. This latter part is key.

> As I also mentioned in the other thread (http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/7011), the aliasing works if it's AFTER the new types. I don't know if it should be that way or has to be that way, but that's how it works in DMD 0.96.

This is surely indicative of another bug in method name resolution.

The point I'm trying to make is this: there's a special case involved here, yet no-one can say /why/ it's a special case. My perspective is that it would be far simpler for both the programmer, and for the compiler, to treat inherited method signatures as though they were actually within the scope of the sub-class. By doing so, the compiler would treat inherited methods in exactly the same manner it treats them within a self-contained class, and there's no crufty edge conditions.

Without a solid counter-point forthcoming, it's very hard for anyone to construct pros and cons ...

There's another thing to consider: At the very least, I've pointed out no less than three non-obvious things, restricted entirely to the area of method name resolution. I think that spells trouble for anyone coming to the language. Don't you? As such I feel it's a very big mistake for Walter to simply "pooh pooh" the entire thing ...

- Kris




> interface ISomeInterface{}
>
> class Writer
> {
>          Writer put (bit x)
>          {
>              printf("Writer.put(bit x)\n");
>              return null;
>          }
>          Writer put (int x)
>          {
>              printf("Writer.put(int x)\n");
>              return null;
>          }
> }
>
>
> class MyWriter : Writer
> {
>      Writer put (int x)
>      {
>          printf("MyWriter.put(int x)\n");
>          return null;
>      }
>
>      Writer put (ISomeInterface x)
>      {
>          printf("Writer.put(ISomeInterface x)\n");
>          return null;
>      }
>      alias Writer.put put;
> }
>
>
> void main()
> {
>      ISomeInterface I;
>      int i;
>      bit b;
>      Writer w = new Writer();
>      MyWriter m = new MyWriter();
>
>      w.put(i);
>      w.put(b);
>
>      m.put(I);
>      m.put(i);
>      m.put(b);
> }
>
> Output:
> Writer.put(int x)
> Writer.put(bit x)
> Writer.put(ISomeInterface x)
> MyWriter.put(int x)
> Writer.put(bit x)
>
>
> --
> Justin (a/k/a jcc7)
> http://jcc_7.tripod.com/d/


July 24, 2004
Kris wrote:
> Hi Justin: inline
> 
> "J C Calvarese"wrote ...
> 
>>I've often read people on this newsgroup lobbying for the compiler to
>>*require* the programmer to do things the hard way to keep the
>>programmer from accidentily doing things wrong. Before the alias is
> 
> 
> If your referring to me, then yes; I do advocate such things. But in those
> where I do, it's when there's no other valid context (or, at least no-one
> has come up with anything to the contrary).

You asked for an explanation of "the benefits of those particular resolution rules", so I came up a possible benefit. The benefit may be achieved better by another method but it could be a benefit.

I suspect another benefit is that Walter thinks these rules are somewhat easy to implement, but I don't remember him mentioning anything specific to that effect.

>>added, a person might remember that they meant to add ISomeInterface to
>>the base class anyways. It's a hypothetical, but it's also a possible
>>benefit.
> 
> 
> The compiler would simply spit out an error saying it couldn't find a
> put(ISomeInterface), just as it does today within the scope of a
> self-contained class ~ the benefit of strongly-typed language. (BCPL would
> silently eat it).
> 
> 
>>I am somewhat concerned that leaving out that alias can still silently
>>fail to do what it's expected to do. For example, if I leave the alias
>>out of the example below (based upon your example of course), in the
>>MyWriter m.put(b) call, the bit b would be converted into a int. Oops.
> 
> 
> That's not what I'm suggesting at all Justin. From where I stand, all
> inherited signatures are still valid and the compiler picks the most
> appropriate one: Writer.put(bit) in your example. In other words, it should
> do /exactly/ what it would do today within the scope of a self-contained
> class. This latter part is key.
> 
> 
>>As I also mentioned in the other thread
>>(http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/7011), the
>>aliasing works if it's AFTER the new types. I don't know if it should be
>>that way or has to be that way, but that's how it works in DMD 0.96.
> 
> 
> This is surely indicative of another bug in method name resolution.
>
> The point I'm trying to make is this: there's a special case involved here,
> yet no-one can say /why/ it's a special case. My perspective is that it
> would be far simpler for both the programmer, and for the compiler, to treat
> inherited method signatures as though they were actually within the scope of
> the sub-class. By doing so, the compiler would treat inherited methods in
> exactly the same manner it treats them within a self-contained class, and
> there's no crufty edge conditions.
> 
> Without a solid counter-point forthcoming, it's very hard for anyone to
> construct pros and cons ...
> 
> There's another thing to consider: At the very least, I've pointed out no
> less than three non-obvious things, restricted entirely to the area of
> method name resolution. I think that spells trouble for anyone coming to the
> language. Don't you? 

Lack of documentation is still a serious problem for D users. The spec only demonstrates how to do the simplest cases.

> As such I feel it's a very big mistake for Walter to
> simply "pooh pooh" the entire thing ...

Often, Walter doesn't agree to change things until we gang up on him. I think there are really several issues at play here. Perhaps more could be achieved by breaking them into the separate parts:

1. External Names
   Possible solutions:
   a.  Change the compiler to not do the offending behavior.
   b.  Don't import within the class unless you want this behavior.
       If this still creates additional problems (as I know it did in
       the past with forward references), maybe those problems would be
       easier to fix that convincing Walter to redesign the name lookup
       rules.

2. Satisfying Interface Contracts
   I agree with you on this one. I don't understand at all why this
   doesn't automatically inherit the needed method. Am I being dense?
   Maybe we can convince Walter that this is an actual bug that needed
   fixing.

3. The Alias peek-a-boo Game
   Possible solutions:
   a.  Change the compiler to not do the offending behavior.
   b.  Clearly document that the alias should be added if the super's
       methods are wanted.

Your document is well-organized, but it might be easier to build consensus one page at a time. Point 2 seems the least controversial, yet it also seems to be the least discussed in this thread.

> 
> - Kris

-- 
Justin (a/k/a jcc7)
http://jcc_7.tripod.com/d/