www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Three notable problems with method name resolution

reply "some idiot" <fu bar.org> writes:
Attached is a small pdf noting three outstanding issues ~ each of which has
various bug reports stretching back into the old NG. Given that the v1.0
release is getting closer, it seemed appropriate to try getting some
priority and/or traction.

Such things might simply fall by the wayside otherwise.
Jul 23 2004
next sibling parent reply Andrew Edwards <ridimz_at yahoo.dot.com> writes:
some idiot wrote:

 Attached is a small pdf noting three outstanding issues ~ each of which has
 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.
 
 Such things might simply fall by the wayside otherwise.
 
 

All viable points that undoubtedly needs attention. What's fascinating about this post is how you can almost identify the author simply by the message "he" sends. Interesting!
Jul 23 2004
next sibling parent "some idiot" <fu bar.org> writes:
Don't follow Andrew; you imply there's some kind of hidden identity ...


"Andrew Edwards" <ridimz_at yahoo.dot.com> wrote in message
news:cdsei9$1kh$1 digitaldaemon.com...
 some idiot wrote:

 Attached is a small pdf noting three outstanding issues ~ each of which


 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.

 Such things might simply fall by the wayside otherwise.

All viable points that undoubtedly needs attention. What's fascinating about this post is how you can almost identify the author simply by the message "he" sends. Interesting!

Jul 23 2004
prev sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Whoops! I see what you mean. That's what I get for posting from the laptop
(under CA sunshine).

Glad to know I'm consistent though! Or maybe you thought it was someone
else?

<g>


"Andrew Edwards" <ridimz_at yahoo.dot.com> wrote in message
news:cdsei9$1kh$1 digitaldaemon.com...
 some idiot wrote:

 Attached is a small pdf noting three outstanding issues ~ each of which


 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.

 Such things might simply fall by the wayside otherwise.

All viable points that undoubtedly needs attention. What's fascinating about this post is how you can almost identify the author simply by the message "he" sends. Interesting!

Jul 23 2004
parent J C Calvarese <jcc7 cox.net> writes:
Kris wrote:
 Whoops! I see what you mean. That's what I get for posting from the laptop
 (under CA sunshine).
 
 Glad to know I'm consistent though! Or maybe you thought it was someone
 else?
 
 <g>

I knew who it was immediately. Even if it was PDF instead of a Word document (good call!). I think your points were good. Hopefully, they're quirks of the implementation that can be unquirked. -- Justin (a/k/a jcc7) http://jcc_7.tripod.com/d/
Jul 23 2004
prev sibling next sibling parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
There's always one more thing: forgot to mention that the required use of
alias regarding the peek-a-boo game doesn't compile for certain signatures:

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"



"some idiot" <fu bar.org> wrote in message
news:cdsd5t$u5$1 digitaldaemon.com...
 Attached is a small pdf noting three outstanding issues ~ each of which

 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.

 Such things might simply fall by the wayside otherwise.

Jul 23 2004
prev sibling next sibling parent reply Derek <derek psyc.ward> writes:
On Fri, 23 Jul 2004 18:16:30 -0700, some idiot wrote:

 Attached is a small pdf noting three outstanding issues ~ each of which has
 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.
 
 Such things might simply fall by the wayside otherwise.

I must be using a different version of D, because the three issues you mention are not a problem in v0.95. The first one, referring to extern C functions do not produce any error messages. In fact, it compiles and runs just fine. The second, using interfaces, is also correctly working here. By correct, I mean that it fails in exactly the way you say it does, because that's what I was expecting it to do - fail. My understanding of an interface is that it is JUST a list of cuntions that must be implemented in the class that you specified with the interface name. 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. In the third example, your Writer class does NOT implement any of the functions. And the MyWriter class only implements one of the functions, the one with (int). So when you try to call the function using (ulong) as an argument, the linker can't find any! This is exactly what I'd expect so I don't think taht D is in error here. Maybe you need to provide real implementation in the Writer class or else declare them as abstract functions. At least that way, you get a compiler error rather than a linker error. So maybe I don't really understand what the issues are. -- Derek Melbourne, Australia
Jul 23 2004
next sibling parent Ant <duitoolkit yahoo.ca> writes:
On Sat, 24 Jul 2004 15:32:51 +1000, Derek wrote:

 On Fri, 23 Jul 2004 18:16:30 -0700, some idiot wrote:
 
 I must be using a different version of D, because the three issues you
 mention are not a problem in v0.95.

you're wrong the problems manifest on 0.95.
 
 The first one, referring to extern C functions do not produce any error
 messages. In fact, it compiles and runs just fine.

you're wrong.
 
 The second, using interfaces, is also correctly working here.

you're wrong. I have 1400 lines like: ddi/GC.d(61): class GC 1interface function OGTK.gtk is not implemented ddi/GC.d(61): class GC 1interface function OGTK.gtk is not implemented ddi/GC.d(61): class GC 1interface function OGTK.gtk is not implemented ddi/Screen.d(66): class Screen 1interface function OGTK.gtk is not implemented ddi/Screen.d(66): class Screen 1interface function OGTK.gtk is not implemented ddi/Screen.d(66): class Screen 1interface function OGTK.gtk is not implemented ddi/Visual.d(59): class Visual 1interface function OGTK.gtk is not implemented ddi/Visual.d(59): class Visual 1interface function OGTK.gtk is not implemented ddi/Visual.d(59): class Visual 1interface function OGTK.gtk is not implemented dui/AccelGroup.d(59): class AccelGroup 1interface function OGTK.gtk is not implemented none of those classes declares to implement the interface or is missing the method (as far as I can tell, you see 1400 lines...) just adding or removing "private import ddi.Pixbuf;" to class ddi.Value body makes the compiler produce or not those 1400 errors. (it's not a typo it one thousand and four hundred lines)
 My understanding of an interface is that it is JUST a list of cuntions that
 must be implemented in the class that you specified with the interface
 name.

you're wrong.
 The problem is with class Bar. Here you explictly say that it is derived
 from class Foo, and must implement interface IFoo and IBar.

the example is wrong.
 In the third example,

alias is dumb for OO but D supports other paradigmas. examples? I posted enough already. Ant
Jul 23 2004
prev sibling next sibling parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Firstly, I apologize for not providing working examples. In the interests of
brevity the code was sliced and diced to occupy minimal space ~ inline for
the rest:

"Derek" <derek psyc.ward> wrote ...
 The first one, referring to extern C functions do not produce any error
 messages. In fact, it compiles and runs just fine.

That's because (in the interests of brevity) I didn't attempt to make any references to the methods. Believe me, it's a problem we ran into with Mango (as were all these issues, and many more besides). I think you'll find several others will vouch for this also. There's a good writeup in the buglist if you care to look (6/10/2004).
 My understanding of an interface is that it is JUST a list of cuntions

 must be implemented in the class that you specified with the interface
 name.

That's essentially correct, except for one thing: the class has only to satisfy the contract to be in compliance (otherwise it's a 'partial' implementation: like an abstract class). It does not matter how that contract is implemented! I'm tempted to repeat that. When you build something of significant complexity, the exposed interface (the contract) rarely matches the implementation in terms of inheritance, and nor should it! That's one of the true benefits of Interfaces. In D, you have to stub and dispatch all kinds of stuff to satisfy the compiler, or dispense with your nice clean inheritance-based design. It's very, very, messy. And rapidly becomes brittle as complexity increases. I'm not asking you to believe me Derek. Just build yourself something fairly complex using interfaces in D, and you'll run into this very quickly. Here's (a temporary link to) an older document written during the "Interface Wars" so you can get some additional history: http://svn.dsource.org/svn/projects/mango/trunk/doc/D%20Interfaces%20-%20v0. 82.pdf . Points #4 & #5 are the salient ones here.
 In the third example, your Writer class does NOT implement any of the
 functions. And the MyWriter class only implements one of the functions,

 one with (int). So when you try to call the function using (ulong) as an
 argument, the linker can't find any! This is exactly what I'd expect so I
 don't think taht D is in error here. Maybe you need to provide real
 implementation in the Writer class or else declare them as abstract
 functions. At least that way, you get a compiler error rather than a

 error.

There's no need to shout mate ... I can 'hear' ya loud and clear <g> Again, that was just in the interests of brevity ... you have to understand that aspect of alias was added in a vague attempt to appease yet another problem exposed via Mango (in what I considered to be, and clearly expressed to Walter as a thorough hack). I'm afraid the examples were perhaps not as useful as I thought they were, although this was documented as a bug on several occasions. Why don't you try the one I posted a little earlier that doesn't even compile, and get back to me please? (fyi ~ I don't spend hours writing documents like those for fun ... it's a PITA)
Jul 23 2004
prev sibling next sibling parent Lars Ivar Igesund <larsivar igesund.net> writes:
Derek wrote:

 In the third example, your Writer class does NOT implement any of the
 functions. And the MyWriter class only implements one of the functions, the
 one with (int). So when you try to call the function using (ulong) as an
 argument, the linker can't find any! This is exactly what I'd expect so I
 don't think taht D is in error here. Maybe you need to provide real
 implementation in the Writer class or else declare them as abstract
 functions. At least that way, you get a compiler error rather than a linker
 error.
 
 So maybe I don't really understand what the issues are.

I won't comment on the first two as I haven't tried to do such things. The third problem is very real. Kris' example might not have implementations, but that's just for brevity (I suspect) as it really don't work for implemented functions. The workaround hack is incredibly unintuitive (and quite stupid) making D somewhat less of an OO language. The fact that it is only documented on the declarations page, just adds to the silliness of it all. How is _ANYONE_ coming to the language going to guess this? I flagged this as bug (together with many others) because I didn't know of the alias hack and got a system shock when I found out that it was intended behaviour. It is really a 'break' feature in a potential 'make or break' review of D. Lars Ivar Igesund
Jul 24 2004
prev sibling next sibling parent reply John Reimer <brk_6502 yahoo.com> writes:
On Sat, 24 Jul 2004 15:32:51 +1000, Derek wrote:

 I must be using a different version of D, because the three issues you
 mention are not a problem in v0.95.
 
 The first one, referring to extern C functions do not produce any error
 messages. In fact, it compiles and runs just fine.

I'll back Kris up on this one. It is most definitely an issue. The example may compile fine as is, and that's part of the problem! Nobody sees the error until much later. Just go try to call a FileConduit instance's close() or read( char[] dst ) from another module, class, main, or whatever. You should quickly find that these methods no longer exist due to the miraculous effects of privately importing (internal to the class) std.c.linux.linux "close" and "read." These imported functions completely hide the classes close and read methods with no hope of recovery. DMD doesn't even attempt to overload these methods. Instead it rudely ends with a compile error message grumbling confusingly about incorrect arguments. The hapless programmer, not realizing what the import is doing, looks back in the code and is thoroughly confused at trying to figure out why his method calls are not valid even though they match the signatures in the class perfectly. This problem is definitely real.
Jul 24 2004
parent reply Derek <derek psyc.ward> writes:
On Sat, 24 Jul 2004 00:41:15 -0700, John Reimer wrote:

 On Sat, 24 Jul 2004 15:32:51 +1000, Derek wrote:
 
 I must be using a different version of D, because the three issues you
 mention are not a problem in v0.95.
 
 The first one, referring to extern C functions do not produce any error
 messages. In fact, it compiles and runs just fine.

I'll back Kris up on this one. It is most definitely an issue. The example may compile fine as is, and that's part of the problem! Nobody sees the error until much later. Just go try to call a FileConduit instance's close() or read( char[] dst ) from another module, class, main, or whatever. You should quickly find that these methods no longer exist due to the miraculous effects of privately importing (internal to the class) std.c.linux.linux "close" and "read." These imported functions completely hide the classes close and read methods with no hope of recovery. DMD doesn't even attempt to overload these methods. Instead it rudely ends with a compile error message grumbling confusingly about incorrect arguments. The hapless programmer, not realizing what the import is doing, looks back in the code and is thoroughly confused at trying to figure out why his method calls are not valid even though they match the signatures in the class perfectly. This problem is definitely real.

Before I wrote my original response to this thread, I wrote some test programs to try and understand what the issues were. I based my response on those test programs. As I've now had a number of people come back and tell me I'm dead wrong (and possibly a bit simple too), I guess I really do not understand the issues. Anyhow, here are the three files I made to test the "extern C/private import" issue. <file: test3.d> extern (C) { int read (int, void*, int); int close (int); } <end of file> <file: test4.d> class Conduit { abstract void close (); abstract void read (char[] dst); } class FileConduit : Conduit { private import test3; private int handle; void close () { printf("FileConduit Close\n"); test3.close (handle); } void read (char[] dst) { test3.read (handle, dst, dst.length); } } <end of file> <file: test2.d> import test4; void main() { FileConduit f = new FileConduit; f.close(); } <end of file> <compile and run results> G:\temp>dmd test2 test4 test3 F:\DMD\BIN\..\..\dm\bin\link.exe test2+test4+test3,,,user32+kernel32/noi; G:\temp>test2 FileConduit Close G:\temp> <end of run> What haven't I understood? -- Derek Melbourne, Australia
Jul 24 2004
parent John Reimer <brk_6502 yahoo.com> writes:
 Before I wrote my original response to this thread, I wrote some test
 programs to try and understand what the issues were. I based my response
 on those test programs. As I've now had a number of people come back and
 tell me I'm dead wrong (and possibly a bit simple too), I guess I really
 do not understand the issues.

Ha! nobody would dare hint that you are simple. That's a good one :-). Your point of view is appreciated. You obviously got some tough grit to wrangle on these issues against multiple opposing opinions. In fact you've generated a good example that I'll have to think about. One problem is that the situation in mango still generates the compiler error that I'm referring too despite your example's apparent revelation of the problem's non-existence. So somehow, the slightly more complicated example does what yours is not doing. Walter seems to have already described why this issue even comes up. So I assume it's existence is formally accepted already (but the argument is that there is a good reason for it's existence, such that it's not an important issue). As for your example, I'll have to figure what's going on differently between mango and it. Many pardons if I made out that this is an obvious case. Obviously it's not as obvious as I obviously thought! ;-)
 Anyhow, here are the three files I made to test the "extern

 import" issue.
 
 <file: test3.d>
 extern (C)
 {
 int read (int, void*, int);
 int close (int);
 }
 <end of file>
 
 <file: test4.d>
 class Conduit
 {
     abstract void close ();
     abstract void read (char[] dst);
 }
 class FileConduit : Conduit
 {
     private import test3;
     private int handle;
     void close ()
     {
        printf("FileConduit Close\n");
        test3.close (handle);
     }
     void read (char[] dst)
     {
         test3.read (handle, dst, dst.length);
     }
     }
 <end of file>
 
 <file: test2.d>
 import test4;
 void main()
 {
     FileConduit f = new FileConduit;
     
     f.close();
 }
 <end of file>
 
 <compile and run results>
 G:\temp>dmd test2 test4 test3
 F:\DMD\BIN\..\..\dm\bin\link.exe
 test2+test4+test3,,,user32+kernel32/noi;
 
 G:\temp>test2
 FileConduit Close
 
 G:\temp>
 
 <end of run>
 
 What haven't I understood?

Jul 24 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Derek" <derek psyc.ward> wrote in message
news:h0kf8z2x9zvu.tyujz7z737ln$.dlg 40tude.net...
 The second, using interfaces, is also correctly working here. By correct,

 mean that it fails in exactly the way you say it does, because that's what
 I was expecting it to do - fail.

 My understanding of an interface is that it is JUST a list of cuntions

 must be implemented in the class that you specified with the interface
 name.

 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

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
Jul 24 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
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

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

Jul 24 2004
next sibling parent reply Derek <derek psyc.ward> writes:
On Sat, 24 Jul 2004 10:47:15 -0700, Kris wrote:

 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 ...

I don't think that anybody is disagreeing with this position. Even Walter ;-)
 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 ...

<pedantic_mode> D does not *explicitly* deny super-clas methods from being exposed in a subclass. It *implicitly* denys them. To explicitly deny, one would have to add code to do that. However, it happens by *not* adding code, thus it is implicit. </pedantic_mode> But more to the point...D is not Java, is not C++, is not C#, is not SmallTalk, is not <insert your favorite OO lang here>. D allows the coder to satisfy a sub-class contract, but you have to do it explicitly. It is not done automatically for you. Why? Dunno. But that's the way of D ( and Walter). Okay so Java does it for you --- good on ya, Java! But this is D. class Bar : Foo, IFoo, IBar { void bar() {} } The way that I translate this definition is like this ... Bar is a class that is derived from Foo, and must implement the methods listed in IFoo, and must implement the methods listed in IBar. Because it is derived from Foo, any methods implemented in Bar that have the same signature as those in Foo, will override the Foo methods. If I wish users of Bar to access Foo methods, I must explicitly name those methods in Bar. In the code above, Bar implements all the IBar methods but does not implement any of the IFoo methods. Therefore it is in error. In D, one way of doing this is ... class Bar : Foo, IFoo, IBar { void bar() {} void foo() {Foo.foo();} } Yes, it is verbose having to write all this stuff. So maybe the approach to Walter should be along the lines of ... "Hey Walter, how can you get D to help us out here? We would like to avoid all this tedious and error-prone tasks, and thus make it easier for coders?" -- Derek Melbourne, Australia
Jul 24 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Derek"  wrote ...
 <pedantic_mode>
 D does not *explicitly* deny super-clas methods from being exposed in a
 subclass. It *implicitly* denys them. To explicitly deny, one would have

 add code to do that. However, it happens by *not* adding code, thus it is
 implicit.
 </pedantic_mode>

When I can't satisfy the Interface contract even with the mystical power of 'alias', I consider it an explicit denial. You are certainly welcome to your particular interpretation too :-)
 Yes, it is verbose having to write all this stuff. So maybe the approach

 Walter should be along the lines of ...

 "Hey Walter, how can you get D to help us out here? We would like to avoid
 all this tedious and error-prone tasks, and thus make it easier for
 coders?"

I tried that approach on April 8th. Perhaps he'll listen to you instead <g>
Jul 24 2004
prev sibling next sibling parent Andy Friesen <andy ikagames.com> writes:
Kris wrote:
 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 ...

The idea here, as I understand it, is that Bar is *re*implementing the IFoo interface, and so much explicitly define those methods declared in the interface. What I don't understand is why it would be useful to do so. Secondly, it leads me to think that, perhaps, Bar implements IFoo *twice*. But then I have to wonder why we would want to do this and how we are to state which IFoo we want to access. This second reason is probably, I think, the most important reason the current behaviour should be changed: it doesn't mean what a stupid person (hello!) thinks it means at first glance. Implementing an interface should just be a contractual obligation that the class define all the methods specified therein, wherever the definition. (abstract or what have you) -- andy
Jul 24 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdu77l$158f$1 digitaldaemon.com...
 How about it Walter? I'd really, really, like to understand why

 methods are explicitly denied from satisfying a sub-class contract. At

 then there'd be something to discuss vis-a-vis pro & con ...

Sure. D originally worked the way you suggest. However, this message to me by Joe Battelle convinced me otherwise. It's a bit complicated, but essentially it's to close up a type hole: -------------------------------------------- The fact that interfaces are snapshots and not inherited from class to subclass can really mess you up sometimes. Just tacking on the interface at each level in the hierarchy doesn't work either--you get a unique interface each time. Casting to a superclass before casting to the interface that the derived type supports can alter which version of the interface is called. The spec doesn't say much about this. Are we stuck with this feature of interfaces or will they be inheritable at some point? Example code: ------------- import c.stdio; interface D { abstract int foo(); } class A : D { int foo() { return 1; } } class B : A, D { //carry the D's, 'cause they're not inherited //this should tell you something's up :) //A's foo is taken to satisfy D. //a cast(D) on a B will always use this foo //even if the B is actually a further derived type with overloaded foo() } class C : B, D { int foo() { return 2; } } int main (char[][] args) { C c = new C; A a = (A) c; int j = a.foo(); // 2 B b = (B) c; int k = b.foo(); // 2 D d1 = (D) c; int l = d1.foo(); // 2 D d2 = (D) b; int m = d2.foo(); // 1 Gotcha!!! b.foo() not equal to (D)b.foo() //prints 2 2 2 1 printf("%d %d %d %d", j, k, l, m); return 0; } ---------------------------------------------------------------
Jul 24 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Walter"  wrote .
 Sure. D originally worked the way you suggest. However, this message to me
 by Joe Battelle convinced me otherwise. It's a bit complicated, but
 essentially it's to close up a type hole:

1) You'll forgive me Walter, for saying that I think you may just be mistaken in terms of the time period. Recall that you sent me the prototype Interface-enabled D compiler (other than COM), and it most certainly did not have this "Contract Resolution" ability enabled. 2) the example below makes a comment about Interfaces not being inherited. In D they *are* inherited! I've used that time and time again to work around the ever-persistent "diamond interface" related bugs. 3) the example below is built explicitly to circumvent /some/ type system. Also, it /could/ be argued that the example below produces a valid and predictable result. Regardless, Because D interfaces *are* inherited this is quite possibly a moot point entirely. 4) Do you not think this supposed type hole can be worked out in some other way? The trade-off is, well, I've already documented all the fallout from this decision. 5) While not wishing to harp on about Java ~ if this is such a problem, how does Java avoid it? It manages perfectly well, thank-you-very-much. D is so supposed to be so much better, right? This "decoupled contract" aspect is a major player in the Interface arsenel: please do not underestimate it. Here's an extract from my revised document: "An Interface is nothing more than a contract. One of the great things about this notion is there's no implicit binding between the Interface and how it is actually fulfilled by the implementation. It is perfectly legitimate for a class to satisfy its contractual obligation in any way it sees fit; including via local methods, via public methods exposed through inheritance, via some base class, via an implemented abstract class, or any combination thereof. That is, the contract can be declared and fulfilled entirely by any particular class, aggregated piecemeal through inheritance, or be inherited itself through some super-class derivation. To stipulate otherwise would enforce an implementation restriction that has no bearing whatsoever upon the contract itself. Under those terms, satisfying the contract would mean ensuring the design of your exposed class hierarchy matches exactly that of the Interface hierarchy - thus negating the intentionally decoupled nature of the contract." Sorry Walter; there's just too many flaws in your statement to be convincing. - Kris
 --------------------------------------------
 The fact that interfaces are snapshots and not inherited from class to
 subclass
 can really mess you up sometimes.  Just tacking on the interface at each
 level
 in the hierarchy doesn't work either--you get a unique interface each

 Casting to a superclass before casting to the interface that the derived
 type
 supports can alter which version of the interface is called.  The spec
 doesn't
 say much about this.

 Are we stuck with this feature of interfaces or will they be inheritable

 some
 point?

 Example code:
 -------------
 import c.stdio;

 interface D {
 abstract int foo();
 }

 class A : D {
 int foo() { return 1; }
 }

 class B : A, D { //carry the D's, 'cause they're not inherited
 //this should tell you something's up :)
 //A's foo is taken to satisfy D.
 //a cast(D) on a B will always use this foo
 //even if the B is actually a further derived type with overloaded foo()
 }

 class C : B, D {
 int foo() { return 2; }
 }

 int main (char[][] args)
 {
 C c = new C;
 A a = (A) c;
 int j = a.foo(); // 2
 B b = (B) c;
 int k = b.foo(); // 2
 D d1 = (D) c;
 int l = d1.foo(); // 2
 D d2 = (D) b;
 int m = d2.foo(); // 1 Gotcha!!!  b.foo() not equal to (D)b.foo()

 //prints 2 2 2 1
 printf("%d %d %d %d", j, k, l, m);

 return 0;
 }

 ---------------------------------------------------------------

Jul 25 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdvo13$1srb$1 digitaldaemon.com...
 1)  You'll forgive me Walter, for saying that I think you may just be
 mistaken in terms of the time period. Recall that you sent me the

 Interface-enabled D compiler (other than COM), and it most certainly did

 have this "Contract Resolution" ability enabled.

At some point it must have worked that way, otherwise Joe wouldn't have sent me that message <g>.
 2) the example below makes a comment about Interfaces not being inherited.
 In D they *are* inherited! I've used that time and time again to work

 the ever-persistent "diamond interface" related bugs.
 3) the example below is built explicitly to circumvent /some/ type system.
 Also, it /could/ be argued that the example below produces a valid and
 predictable result. Regardless, Because D interfaces *are* inherited this

 quite possibly a moot point entirely.

The way interfaces have worked has changed around a bit, some of it was in response to Joe's emails to me. Interface semantics have several subtleties that escaped me early on <g>. In any case, what it used to do way back is not so important, what it does now is, and the type hole would exist if the inheritance rules would change. While it certainly would be predictable, so are the other scoping/resolution rules that you argue are incorrect <g>. I think this type hole is in the category of ones like: class A{} class B:A{} B[] b; foo(A[]); foo(b); which is disallowed. It's better to issue an error when a potential type hole exists, requiring a cast to resolve it, than to silently open such a hole and one's program inexplicably crashes.
 4) Do you not think this supposed type hole can be worked out in some

 way? The trade-off is, well, I've already documented all the fallout from
 this decision.

Why is simply omitting the redundant interface name in the inheritance list a problem? I don't get it.
 5) While not wishing to harp on about Java ~ if this is such a problem,

 does Java avoid it? It manages perfectly well, thank-you-very-much. D is

 supposed to be so much better, right? This "decoupled contract" aspect is

 major player in the Interface arsenel: please do not underestimate it.
 Here's an extract from my revised document:

 "An Interface is nothing more than a contract. One of the great things

 this notion is there's no implicit binding between the Interface and how

 is actually fulfilled by the implementation. It is perfectly legitimate

 a class to satisfy its contractual obligation in any way it sees fit;
 including via local methods, via public methods exposed through

 via some base class, via an implemented abstract class, or any combination
 thereof. That is, the contract can be declared and fulfilled entirely by

 particular class, aggregated piecemeal through inheritance, or be

 itself through some super-class derivation.

 To stipulate otherwise would enforce an implementation restriction that

 no bearing whatsoever upon the contract itself. Under those terms,
 satisfying the contract would mean ensuring the design of your exposed

 hierarchy matches exactly that of the Interface hierarchy - thus negating
 the intentionally decoupled nature of the contract."

 Sorry Walter; there's just too many flaws in your statement to be
 convincing.

Jul 25 2004
next sibling parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Walter" <newshound digitalmars.com> wrote in message

 Why is simply omitting the redundant interface name in the inheritance

 a problem? I don't get it.

Ah ... right. There's a total disconnect here, since the original document had an error in it. I guess you didn't get a chance to see the various posts regarding clarification. Please take a look at this revised version, and let's pick up again after that? Thanks, and sorry about the misdirection. The example and grayed-text contained within should hopefully clarify what the issue is ... if not, I'll try to devise an alternate way to state it. http://svn.dsource.org/svn/projects/mango/trunk/doc/Names.pdf - Kris
Jul 25 2004
prev sibling next sibling parent reply parabolis <parabolis softhome.net> writes:
Walter wrote:

 
 Why is simply omitting the redundant interface name in the inheritance list
 a problem? I don't get it.

Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider: ================================ interface Reads { void readSomething(); } interface Writes { void writeSomething(); } interface ReadsWrites { void readSomething(); void writeSomething(); } class Reader : Reads { void readSomething() { printf( "read1()" ); } } class Writer : Writer{ void writeSomething() { printf( "write1()" ); } } class WritingReader : Reader, ReadingWriter { void writeSomething() { printf( "write2()" ); } } class ReadingWriter : Writer, ReadingWriter { void readSomething() { printf( "read2()" ); } } ================ There are no redundant interfaces in this example and yet ReadingWriter and WritingReader are not considered valid implementations. BUG NOTE!!! The above code crashes the compiler when I try to compile it so you may want to note this as a bug. (win32 version)
Jul 26 2004
next sibling parent reply Berin Loritsch <bloritsch d-haven.org> writes:
parabolis wrote:

 Walter wrote:
 
 Why is simply omitting the redundant interface name in the inheritance 
 list
 a problem? I don't get it.

Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider: ================================ interface Reads { void readSomething(); } interface Writes { void writeSomething(); } interface ReadsWrites { void readSomething(); void writeSomething(); } class Reader : Reads { void readSomething() { printf( "read1()" ); } } class Writer : Writer{ void writeSomething() { printf( "write1()" ); } } class WritingReader : Reader, ReadingWriter { void writeSomething() { printf( "write2()" ); } } class ReadingWriter : Writer, ReadingWriter { void readSomething() { printf( "read2()" ); } }

I would expect this to not compile. For example, if you had two classes that referred to each other in the extends/implements area, then you would have a circular reference. This is really bad. All inheritance structures should follow a DAG (Directed Acyclic Graph), and there are all kinds of ways of detecting them, etc. For now, I have to assume that you meant to write ReadsWrites in place of ReadingWriter in the extends/implements area of the last two classes. If it does not compile in this case, I would be inclined to agree with you. I do realize that D is not Java, but it does seem very cumbersome to me to see a class have all methods necessary for an interface through inheritance, but it doesn't compile or run correctly. Question: wouldn't it be equally valid to have this: interface ReadsWrites : Reads, Writes {} wich would in turn have the readSomething() and writeSomething() methods inherited from the super-interfaces? I would expect that (i.e. can have it in Java and C#, and, if you include pure virtual abstract classes as interfaces, in C++).
Jul 26 2004
parent parabolis <parabolis softhome.net> writes:
Berin Loritsch wrote:
 
 For now, I have to assume that you meant to write ReadsWrites in place 
 of ReadingWriter in the extends/implements area of the last two classes. 
  If it does not compile in this case, I would be inclined to agree with 
 you.

class WritingReader : Reader, Writes { void writeSomething() { printf( "write2()" ); } } class ReadingWriter : Writer, Reads { void readSomething() { printf( "read2()" ); } } Even with the change the the compiler still crashes.
 
 I do realize that D is not Java, but it does seem very cumbersome to me 
 to see a class have all methods necessary for an interface through 
 inheritance, but it doesn't compile or run correctly.
 
 Question:  wouldn't it be equally valid to have this:
 
 interface ReadsWrites : Reads, Writes {}
 
 wich would in turn have the readSomething() and writeSomething() methods 
 inherited from the super-interfaces?

This compiles: interface Reads { void readSomething(); } interface Writes { void writeSomething(); } interface ReadsWrites : Reads, Writes {} As for whether I would use that instead... I think it depends on the sort of code you are writing. When writing library or large app code the Reads and Writes interfaces could be in different files and/or directories. The way I did it saves having to hunt for the source files and or docs to get the return type and function parameters. However code written for a smaller task would probably view interfaces as clutter and want to make them as terse as possible. Of course for that reason the code is unlikely to even use interfaces.
Jul 26 2004
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <ce3rao$1g1s$1 digitaldaemon.com>, parabolis says...
Walter wrote:

 
 Why is simply omitting the redundant interface name in the inheritance list
 a problem? I don't get it.

Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider: ================================ interface Reads { void readSomething(); } interface Writes { void writeSomething(); } interface ReadsWrites { void readSomething(); void writeSomething(); } class Reader : Reads { void readSomething() { printf( "read1()" ); } } class Writer : Writer{ void writeSomething() { printf( "write1()" ); } } class WritingReader : Reader, ReadingWriter { void writeSomething() { printf( "write2()" ); } } class ReadingWriter : Writer, ReadingWriter { void readSomething() { printf( "read2()" ); } } ================ There are no redundant interfaces in this example and yet ReadingWriter and WritingReader are not considered valid implementations. BUG NOTE!!! The above code crashes the compiler when I try to compile it so you may want to note this as a bug. (win32 version)

You have a recursive inheritance declaration with ReadingWriter. Not that dmd should crash on it, but I suspect this is why :) Assuming you meant WritingReader for the second definition then you've still got a circular inheritance heirarchy which is not allowed. All that aside, should this really be legal? interface I { void foo( char x ); } class A { void foo( char x ) {} } class B : A {} class C : B {} class D : C {} class E : D {} class F : E, I { void foo( int x ) {} } Assume that all the above classes have very long definitions and that A, B, and C are in a third-party library with no documentation and convoluted, badly formatted import files. As the creator of F I may very well have intended to define F.foo as the implementation for I.foo and just screwed up the prototype. With the existing lookup scheme this is flagged as an error but with the proposed scheme it would compile just fine. Further assume that A.foo is for some completely different purpose as A may have no knowledge of interface I. IMO the proposed scheme will be the cause of some very hard to find bugs. Sean
Jul 26 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
You're talking about spelling mistakes here Sean. You can make exactly the
same case for everyday methods also (no Interfaces anywhere to be seen):

 class A { void foo ( char x ) {} }
 class B : A {}
 class C : B {}
 class D : C {}
 class E : D {}
 class F : E { void foo1 ( char x ) {} }

class G : F { void ICantSpell() { super.foo ('!'); } } Oops! I mistyped super.foo() when I really meant super.foo1() ... what a silly billy <g> "Sean Kelly" <sean f4.ca> wrote in message news:ce3vq3$1ht7$1 digitaldaemon.com...
 In article <ce3rao$1g1s$1 digitaldaemon.com>, parabolis says...
Walter wrote:

 Why is simply omitting the redundant interface name in the inheritance



 a problem? I don't get it.

Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider: ================================ interface Reads { void readSomething(); } interface Writes { void writeSomething(); } interface ReadsWrites { void readSomething(); void writeSomething(); } class Reader : Reads { void readSomething() { printf( "read1()" ); } } class Writer : Writer{ void writeSomething() { printf( "write1()" ); } } class WritingReader : Reader, ReadingWriter { void writeSomething() { printf( "write2()" ); } } class ReadingWriter : Writer, ReadingWriter { void readSomething() { printf( "read2()" ); } } ================ There are no redundant interfaces in this example and yet ReadingWriter and WritingReader are not considered valid implementations. BUG NOTE!!! The above code crashes the compiler when I try to compile it so you may want to note this as a bug. (win32 version)

You have a recursive inheritance declaration with ReadingWriter. Not that

 should crash on it, but I suspect this is why :)  Assuming you meant
 WritingReader for the second definition then you've still got a circular
 inheritance heirarchy which is not allowed.  All that aside, should this

 be legal?

 interface I { void foo( char x ); }
 class A { void foo( char x ) {} }
 class B : A {}
 class C : B {}
 class D : C {}
 class E : D {}
 class F : E, I { void foo( int x ) {} }

 Assume that all the above classes have very long definitions and that A,

 C are in a third-party library with no documentation and convoluted, badly
 formatted import files.  As the creator of F I may very well have intended

 define F.foo as the implementation for I.foo and just screwed up the

 With the existing lookup scheme this is flagged as an error but with the
 proposed scheme it would compile just fine.  Further assume that A.foo is

 some completely different purpose as A may have no knowledge of interface

 IMO the proposed scheme will be the cause of some very hard to find bugs.


 Sean

Jul 26 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <ce414s$1icc$1 digitaldaemon.com>, Kris says...
You're talking about spelling mistakes here Sean. You can make exactly the
same case for everyday methods also (no Interfaces anywhere to be seen):

 class A { void foo ( char x ) {} }
 class B : A {}
 class C : B {}
 class D : C {}
 class E : D {}
 class F : E { void foo1 ( char x ) {} }

class G : F { void ICantSpell() { super.foo ('!'); } } Oops! I mistyped super.foo() when I really meant super.foo1() ... what a silly billy

hehe... It was an example, after all. I imagine a real case would be much more complex. What I was trying to point out was the risk of unintended consequences of the proposed lookup rules. The existing rules are explicit: the interface for each class is well-defined and if the user wants to leverage existing code then he can do so explicitly using alias. The result may be a bit more verbose but it's also far clearer IMO. I grant that the risk of mistakes isn't as great as if D supported multiple inheritance, but I think it's definately still there. And looking at my example, does it really seem reasonble that the code *should* compile? We're talking about a method that could be 10 levels up in the inheritance tree, and one which the implementer of F may not even know about. Perhaps I'm a control freak but I prefer my classes to have only the baggage I pack them with ;) Sean
Jul 26 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
I would tend to agree with you regarding compilation Sean <g>. My view is
that name overrides should not be treated as a special case. If you're going
to be specific, then why not require the programmer to explicitly bring in
each and every single method they wish to expose from the super-class? At
least it would be symmetrical, and therefore consistent and easy to grok.

However, although having to be explicit for *everything* would work
beautifully regarding all these concerns, it might not meet with much
agreement <g>

My issue is that currently there's this half-way house approach: all
non-private methods are inherited, except those that happen to have a name
that happens to be used by the subclass. It's a special-case, and those are
always prone to failure (I believe Vathix has one such failure scenario
regarding alias and override).

- Kris




"Sean Kelly" <sean f4.ca> wrote in message
news:ce41qm$1ij4$1 digitaldaemon.com...
 In article <ce414s$1icc$1 digitaldaemon.com>, Kris says...
You're talking about spelling mistakes here Sean. You can make exactly


same case for everyday methods also (no Interfaces anywhere to be seen):

 class A { void foo ( char x ) {} }
 class B : A {}
 class C : B {}
 class D : C {}
 class E : D {}
 class F : E { void foo1 ( char x ) {} }

class G : F { void ICantSpell() { super.foo ('!'); } } Oops! I mistyped super.foo() when I really meant super.foo1() ... what a silly billy

hehe... It was an example, after all. I imagine a real case would be much

 complex.  What I was trying to point out was the risk of unintended

 of the proposed lookup rules.  The existing rules are explicit: the

 for each class is well-defined and if the user wants to leverage existing

 then he can do so explicitly using alias.  The result may be a bit more

 but it's also far clearer IMO.  I grant that the risk of mistakes isn't as

 as if D supported multiple inheritance, but I think it's definately still

 And looking at my example, does it really seem reasonble that the code

 compile?  We're talking about a method that could be 10 levels up in the
 inheritance tree, and one which the implementer of F may not even know

 Perhaps I'm a control freak but I prefer my classes to have only the

 pack them with ;)


 Sean

Jul 26 2004
prev sibling next sibling parent reply parabolis <parabolis softhome.net> writes:
Sean Kelly wrote:

 interface I { void foo( char x ); }
 class A { void foo( char x ) {} }
 class B : A {}
 class C : B {}
 class D : C {}
 class E : D {}
 class F : E, I { void foo( int x ) {} }
 
 Assume that all the above classes have very long definitions and that A, B, and
 C are in a third-party library with no documentation and convoluted, badly
 formatted import files.  As the creator of F I may very well have intended to
 define F.foo as the implementation for I.foo and just screwed up the prototype.
 With the existing lookup scheme this is flagged as an error but with the
 proposed scheme it would compile just fine.  Further assume that A.foo is for
 some completely different purpose as A may have no knowledge of interface I.
 IMO the proposed scheme will be the cause of some very hard to find bugs.

I see your point and that would be a nasty bug to track down. In this instance the inheritance bug actually seems to be a feature. But I believe that your argument misses a subtle point. You argue that in the current D language, call it Dc, your error is caught. In the proposed language, call it Dp, you error is not caught and a bug is born. So you would like to believe that the total number of bugs in Dp is 1 more than Dc. However you failed to account for the bugs in the Dc library that are not in the Dp library. I believe there would be more bugs for two reasons: 1) If you can make a mistake like the one above, then consider the library writers who were forced to sprinkle extra function stubs throughout their code. 2) Since Dc has an exception to a rule that Dp does not you must ask yourself how many library writers only partly understand the exception and end up writing buggy code.
Jul 26 2004
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <ce48ge$1klr$1 digitaldaemon.com>, parabolis says...
You argue that in the current D language, call it Dc, your error is 
caught. In the proposed language, call it Dp, you error is not caught 
and a bug is born. So you would like to believe that the total number of 
bugs in Dp is 1 more than Dc.

Yes I would :)
However you failed to account for the bugs in the Dc library that are 
not in the Dp library. I believe there would be more bugs for two reasons:

   1) If you can make a mistake like the one above, then consider
the library writers who were forced to sprinkle extra function stubs 
throughout their code.

Well, all that's needed is an alias, not a full stub, but that's pretty much the same thing. I like the stubs because they're explicit. Requiring their presence gives the programmer more control over how call resolution is handled.
   2) Since Dc has an exception to a rule that Dp does not you must ask 
yourself how many library writers only partly understand the exception 
and end up writing buggy code.

Every language has a feature set that a would-be programmer has to learn. I think either way there will be the same number of ignorant programmers making approximately the same number of stupid mistakes. Frankly, I'm on the fence regarding this issue. In the presence of multiple inheritance I would be firmly in the C++ camp, but without it I could probably be convinced that the proposed solution is better. What I've been hoping for is that someone will come up with a good concrete example of why one or the other is problematic. I'm not concerned with convenience so much as with the prevention of subtle errors and with maintaining a predictable and intuitive set of rules. I think the C++ scheme provides that and I think the proposed scheme *may* provide that, but I worry that I just haven't come up with a good enough reason why it does not. Sean
Jul 26 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Sean Kelly" <sean f4.ca> wrote in message
news:ce4gr1$1njs$1 digitaldaemon.com...
 In article <ce48ge$1klr$1 digitaldaemon.com>, parabolis says...


 same thing.  I like the stubs because they're explicit.  Requiring their
 presence gives the programmer more control over how call resolution is

Minor detail: You'll forgive me for not wishing to confuse the Interface Resolution issue with that of the peek-a-boo game ~ one cannot alias a super-class method to fulfill an interface contract. You are simple forced to stub & dispatch. At any level of complexity, these stubs become a real issue.
 Every language has a feature set that a would-be programmer has to learn.

 think either way there will be the same number of ignorant programmers

 approximately the same number of stupid mistakes.  Frankly, I'm on the

 regarding this issue.  In the presence of multiple inheritance I would be

 in the C++ camp, but without it I could probably be convinced that the

 solution is better.  What I've been hoping for is that someone will come

 a good concrete example of why one or the other is problematic.  I'm not
 concerned with convenience so much as with the prevention of subtle errors

 with maintaining a predictable and intuitive set of rules.  I think the

 scheme provides that and I think the proposed scheme *may* provide that,

 worry that I just haven't come up with a good enough reason why it does

Did you see what Regan proposed? <g>
Jul 26 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <ce4h8p$1nq7$1 digitaldaemon.com>, Kris says...
Minor detail: You'll forgive me for not wishing to confuse the Interface
Resolution issue with that of the peek-a-boo game ~ one cannot alias a
super-class method to fulfill an interface contract. You are simple forced
to stub & dispatch. At any level of complexity, these stubs become a real
issue.

Well that sucks. I hadn't realized that. Is the same true with "using" in C++?
Did you see what Regan proposed? <g>

I think I scanned it. Better scroll back :) Sean
Jul 26 2004
parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Sean Kelly wrote:

 In article <ce4h8p$1nq7$1 digitaldaemon.com>, Kris says...
 
Minor detail: You'll forgive me for not wishing to confuse the Interface
Resolution issue with that of the peek-a-boo game ~ one cannot alias a
super-class method to fulfill an interface contract. You are simple forced
to stub & dispatch. At any level of complexity, these stubs become a real
issue.

Well that sucks. I hadn't realized that. Is the same true with "using" in C++?

Yes, I would think so, but that is because C++ don't have interfaces, only the possibility of classes that are abstract through having only method prototypes. These classes can be inherited to function as interfaces, but then other superclasses would be independent of the interface in the inheritance hierarchy. Lars Ivar Igesund
Jul 27 2004
parent Sean Kelly <sean f4.ca> writes:
In article <ce4vka$1tvt$1 digitaldaemon.com>, Lars Ivar Igesund says...
Sean Kelly wrote:

 In article <ce4h8p$1nq7$1 digitaldaemon.com>, Kris says...
 
Minor detail: You'll forgive me for not wishing to confuse the Interface
Resolution issue with that of the peek-a-boo game ~ one cannot alias a
super-class method to fulfill an interface contract. You are simple forced
to stub & dispatch. At any level of complexity, these stubs become a real
issue.

Well that sucks. I hadn't realized that. Is the same true with "using" in C++?

Yes, I would think so, but that is because C++ don't have interfaces, only the possibility of classes that are abstract through having only method prototypes.

Oops, of course. No wonder I had never run into it before. Sean
Jul 27 2004
prev sibling parent Sean Kelly <sean f4.ca> writes:
In article <ce48ge$1klr$1 digitaldaemon.com>, parabolis says...
I see your point and that would be a nasty bug to track down. In this
instance the inheritance bug actually seems to be a feature. But I 
believe that your argument misses a subtle point.

Okay, I've modified my example a bit, but doing so has me reconsidering a bit. Here it is: interface I { void foo(); } interface J { void foo(); } class A : I { void foo() { printf( "I\n" ); } } class B : A {} class C : B {} class D : C {} class E : D {} class F : E, J { void foo() { printf( "J\n" ); } } int main() { F f = new F(); I i = f; J j = f; i.foo(); j.foo(); return 0; } As you might expect, this prints: J J Because member functions are virtual by default in D, I'm now inclined to support the inheritance proposal. It just seems more consistent all around. But if anyone can offer a good reason why the proposed scheme doesn't work, please do. Sean
Jul 26 2004
prev sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 22:15:32 +0000 (UTC), Sean Kelly <sean f4.ca> wrote:
 All that aside, should this really
 be legal?

 interface I { void foo( char x ); }
 class A { void foo( char x ) {} }
 class B : A {}
 class C : B {}
 class D : C {}
 class E : D {}
 class F : E, I { void foo( int x ) {} }

 Assume that all the above classes have very long definitions and that A, 
 B, and
 C are in a third-party library with no documentation and convoluted, 
 badly
 formatted import files.  As the creator of F I may very well have 
 intended to
 define F.foo as the implementation for I.foo and just screwed up the 
 prototype.
 With the existing lookup scheme this is flagged as an error but with the
 proposed scheme it would compile just fine.  Further assume that A.foo 
 is for
 some completely different purpose as A may have no knowledge of 
 interface I.
 IMO the proposed scheme will be the cause of some very hard to find bugs.

I think a new keyword is required to show the programmers intent. i.e. "implement". If you are writing a method to satisfy an interface you preceed it with "implement" Using your example: class A { void foo( char x ) {} } class B : A {} class C : B {} class D : C {} class E : D {} class F : E, I { implement void foo( int x ) {} } Would error saying "F.foo(int) does not implement any interface (I)" If the user then changed it to the correct definition: class F : E, I { implement void foo( char x ) {} } assuming override is mandatory, it would again error "F.foo(char) overrides A.foo(char)" so the user now needs: class F : E, I { implement override void foo( char x ) {} } This is _another_ argument _for_ a mandatory override keyword, _even if_ you don't agree a new implement keyword is a good idea. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
parent "Blandger" <zeroman aport.ru> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message
news:opsbrvovk35a2sq9 digitalmars.com...
 On Mon, 26 Jul 2004 22:15:32 +0000 (UTC), Sean Kelly <sean f4.ca> wrote:

 Using your example:
 class A { void foo( char x ) {} }
 class B : A {}
 class C : B {}
 class D : C {}
 class E : D {}
 class F : E, I { implement void foo( int x ) {} }

 Would error saying "F.foo(int) does not implement any interface (I)"
 If the user then changed it to the correct definition:
    class F : E, I { implement void foo( char x ) {} }

 assuming override is mandatory, it would again error "F.foo(char)
 overrides A.foo(char)"
 so the user now needs:
    class F : E, I { implement override void foo( char x ) {} }

 This is _another_ argument _for_ a mandatory override keyword, _even if_
 you don't agree a new implement keyword is a good idea.

I think it's good example! Thanks Regan. I'm almost lost in this debates about override rules but I personally feel java rules are more intuitive for me (java experience). I've just now (reading debates) realized the actual mean of 'alias' and what does it for! And I think using alias in the inheritance is very NON-intuitive IMO but may be it's a habbit? What I do like in java are explicit keywords - 'extends' + 'implements'. What's all at this time. -------- Sorry for Off-topic: I'm not lawyer and don't know the rules. Actually they are can be different in different countries. It seems to me like this: For example, I'm single heir and I inherited some property from my parents by 'testament', 'will'. Testament says - I can use them all (inherited). 1. I have inherited all property 'by default' and can use it as I wish. 2. I can use them all because the are inherited by me. 3. But if I would like to give, bequeath it to my child I must write about usage of inheritance (EVERY property) 'explicitly' but NOT by default? Sound a little strang for me but it depends... Sorry again, just thoughts. ---- Yuriy.
Jul 26 2004
prev sibling parent reply parabolis <parabolis softhome.net> writes:
(Note this is a repost which corrects an example with a bug that crashes
the compiler. Note the corrected version also crashes the compiler and
thus I hope keeping the bug from be written off warrants reposting.)

Walter wrote:

 
 Why is simply omitting the redundant interface name in the inheritance list
 a problem? I don't get it.
 

Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider: ================================ interface Reads { void readSomething(); } interface Writes { void writeSomething(); } interface ReadsWrites { void readSomething(); void writeSomething(); } class Reader : Reads { void readSomething() { printf( "read1()" ); } } class Writer : Writer{ void writeSomething() { printf( "write1()" ); } } class WritingReader : Reader, Writes { void writeSomething() { printf( "write2()" ); } } class ReadingWriter : Writer, Reads { void readSomething() { printf( "read2()" ); } } ================================ There are no redundant interfaces in this example and yet ReadingWriter and WritingReader are not considered valid implementations. BUG NOTE!!! The above code crashes the compiler when I try to compile it so you may want to note this as a bug. (win32 version)
Jul 26 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
You may have a typo here:

 class Writer : Writer{

Should be "Writer : Writes" instead? That's probably what's choking the compiler. Other than that, then yes. You have to stub & dispatch for each of the inherited methods. It's no big deal with small interfaces, but quickly becomes messy (and brittle) once complexity ramps up to any significant degree. BTW, the redundant Interface thing was a complete red-herring :-) I made a mistake in my initial tardy example, and subsequently Walter thought that's what the issue was. My apologies. - Kris "parabolis" <parabolis softhome.net> wrote in message news:ce459o$1jnu$1 digitaldaemon.com...
 (Note this is a repost which corrects an example with a bug that crashes
 the compiler. Note the corrected version also crashes the compiler and
 thus I hope keeping the bug from be written off warrants reposting.)

 Walter wrote:

 Why is simply omitting the redundant interface name in the inheritance


 a problem? I don't get it.

Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider: ================================ interface Reads { void readSomething(); } interface Writes { void writeSomething(); } interface ReadsWrites { void readSomething(); void writeSomething(); } class Reader : Reads { void readSomething() { printf( "read1()" ); } } class Writer : Writer{ void writeSomething() { printf( "write1()" ); } } class WritingReader : Reader, Writes { void writeSomething() { printf( "write2()" ); } } class ReadingWriter : Writer, Reads { void readSomething() { printf( "read2()" ); } } ================================ There are no redundant interfaces in this example and yet ReadingWriter and WritingReader are not considered valid implementations. BUG NOTE!!! The above code crashes the compiler when I try to compile it so you may want to note this as a bug. (win32 version)

Jul 26 2004
parent parabolis <parabolis softhome.net> writes:
Kris wrote:

 You may have a typo here:
 
 
class Writer : Writer{

Should be "Writer : Writes" instead? That's probably what's choking the

Thanks. I was wrong, there is no bug.
Jul 26 2004
prev sibling parent reply parabolis <parabolis softhome.net> writes:
Walter wrote:
 
 Sure. D originally worked the way you suggest. However, this message to me

My understanding of what Kris wants is both (1) inheritable interfaces _and_ (2) no function hiding for interface satisfaction Joe's message indicates that D originally satisfied (2) but not (1)
 --------------------------------------------
 The fact that interfaces are snapshots and not inherited from class to
 subclass
 can really mess you up sometimes.  Just tacking on the interface at each

Currently (1) is satisfied but not (2). The reason Kris (rather indelicately) attacks the current implementation is both because the current implementation violates the inheritance aspect of the OO paradigm and because he does not see any valid justification for the violation. That the current implementation violates the inheritance aspect of the OO paradigm should be evident. Classes inherit except when satisfying interface requirements. Violating some aspect of the OO paradigm is not condemning in and of itself. As you pointed out in another post C++ also places restrictions on inheritance for the purpose of resolving against multiple possibilities. I doubt any non-trivial language can adhere perfectly to the OO paradigm. What is condemning is a violation without a reason. Your reason seems to be that 1) and 2) are not possible without a typehole. If you are right then there can be no similar language without the typehole. However Kris believes that Java is similar enough language to be a counter example. Since you have graciously made the relevant source public Kris also has the option to make the required changes to demonstrate the possibilty. I have an implementation in mind that might work. However I am still finding my way around the source and learning the basics behind OO implementation. Am I correct in assuming that dmd\src\phobos\internal\mars.h accurately portrays the memory layout? (ie a pointer to a Vtbl in memory points to a 32 bit value len which is followed by a 32 bit pointer to len 32 bit pointers) If so I am curious why an interface would need a Vtbl. Actually the fact that there is an Interface struct at all make me suspicious. ================================ (disclaimer - I have no idea how the following is actually done. Any pointers to more info would be appreciated.) ================================ I am assuming instantiated objects contain a pointer to their class's static data structure, which I am assuming is what ClassInfo represents. I am also assuming a Vtbl retains the super class' ordering of functions and extends the table by adding its new functions at the end and ovverides functions by replacing the pointers in the super class' Vtbl with appropriate pointers but otherwise copies the super class' Vtbl verbtatim. Finally I am assuming that ClassInfo's member baseClass is a pointer to the super class. Given these assumptions I would imagine interface data could be kept by assining a unique ID to each interface and keeping a table of all interfaces a class implements so that if class B is a subclass of class A then B's Interface table contains the union of the B's Interface IDs and A's IDs. For exampe if: A implements Interfaces with IDs 0, IDa, IDb and IDj, B implements Interfaces with IDs IDi, IDj, ... and IDk, then: A's table [0,IDa,IDb,IDj] B's table [0,IDa,IDb,IDi,IDj,...,IDk] A) Assigning an instantiated object, o, to an interface variable, i, would entail three steps at runtime: 1) verify that o has i's ID 2) create space for i 3) set i to point to the same thing o does B) The casting operation for an interface would require only one step at runtime: 1) verify that o has i's ID C) Assigning an instantiated object, o, to a class variable, c, would entail three steps at runtime: 1) verify that o can reach class c via baseclass 2) create space for i 3) set i to point to the same thing o does D) The casting operation for a class would also require only one step at runtime: 1) verify that o can reach class c via baseclass A), B), C) and D) above are the implict implementation. Explicit casting/assigning forgoes the implicit cast check. If function calls are made using a constant offset into the Vtbl in the ClassInfo that o has a pointer to then Joe Battelle's type-hole example is not a problem for this implementation. o's data is not changed by any of the operations used in Joe's code, so all the function calls are resolved with o's ClassInfo. Inherited functions can be allowed to satisfy an interface and interfaces are inherited. Since the ClassInfo interface table is a Set, redundant interfaces are trivial. Another benefit would seem to be that the ClassInfo Interfaces hold in the current implemntation would be eliminated.
Jul 26 2004
parent "Walter" <newshound digitalmars.com> writes:
"parabolis" <parabolis softhome.net> wrote in message
news:ce43mo$1j7c$1 digitaldaemon.com...
 What is condemning is a violation without a reason. Your
 reason seems to be that 1) and 2) are not possible without
 a typehole.

Yes, that's exactly the reason I gave. It's not a violation without a reason.
 If you are right then there can be no similar
 language without the typehole. However Kris believes that
 Java is similar enough language to be a counter example.

Maybe Java does indeed have a type hole in it.
 Am I correct in assuming that dmd\src\phobos\internal\mars.h
 accurately portrays the memory layout? (ie a pointer to a
 Vtbl in memory points to a 32 bit value len which is followed
 by a 32 bit pointer to len 32 bit pointers)

The vtbls work just like in C++ single inheritance, except that the first entry is reserved for a reference to the .classinfo for the type.
 If so I am curious why an interface would need a Vtbl.

For two reasons: 1) efficiency - polymorphic function dispatch via vtbl[] has proven to be very efficient. 2) compatibility with COM vtbl[]s.
 Actually the fact that there is an Interface struct at
 all make me suspicious.

That's needed so that downcasting of interfaces can work.
 ================================
 (disclaimer - I have no idea how the following is actually
 done. Any pointers to more info would be appreciated.)
 ================================

The best way to figure it out is to write a couple bits of sample code, compile them, and obj2asm the result. Also, check out phobos/internal/cast.d.
Jul 26 2004
prev sibling next sibling parent reply "Walter" <newshound digitalmars.com> writes:
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>.
Jul 24 2004
next sibling parent reply Sean Kelly <sean f4.ca> writes:
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
Jul 24 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"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


lot of critiques of C++, and this has not been mentioned. Or perhaps my


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

 sense once understood.  And I personally find the consistency with C++ to

 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;
Jul 24 2004
next sibling parent reply J C Calvarese <jcc7 cox.net> writes:
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 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 (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/
Jul 24 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
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
 (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/

Jul 24 2004
parent reply J C Calvarese <jcc7 cox.net> writes:
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
(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/
Jul 24 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"J C Calvarese" <jcc7 cox.net> wrote in message
news:cdurdu$1cm6$1 digitaldaemon.com...
 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


 where I do, it's when there's no other valid context (or, at least


 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'm sorry, there was a misunderstanding there. I was referring to a different topic where I've been advocating a stricter application of the language. I thought that's perhaps what you were referring to :-) You did indeed come up with possible benefits for this one, and those were much appreciated.
 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)

Breaking it into three topics is a good idea (I think it might have already happened). The reason they were all in one document to begin with is that there's perhaps an inherent problem with name resolution in general. All very good suggestions though. Please take a look at the revised document (posted soon) which speculates a reason for all of these "issues".
Jul 24 2004
prev sibling parent "Walter" <newshound digitalmars.com> writes:
"J C Calvarese" <jcc7 cox.net> wrote in message
news:cduh40$18fr$1 digitaldaemon.com...
 As I also mentioned in the other thread
 (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.

That's a compiler bug.
Jul 24 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdu9ov$162i$1 digitaldaemon.com...
 Sean; would you mind explaining the benefits of those particular

 rules to me please? While I may be both blind and stupid, I completely

 to see where the value lies, and no-one has specified what they are.

Stroustrup has laid them out in chapter 13.1 of the "The Annotated C++ Reference Manual." The general rule for overloading is that a name is looked up first, and then overload resolution is applied. Stroustrup rejects the idea of ignoring scope issues when doing overload resolution. He argues that it would be surprising in a deeply nested heirarchy of class derivation, that a user of a derived class would have to know too much detail about a base class that may be hidden. Also, he shows how surprising errors can creep in, such as if a function in base class B copies only the B part of an object, but was inadvertantly called with an instance of D that didn't override each of those B functions properly.
Jul 24 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cdvhgv$1nf1$1 digitaldaemon.com...
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:cdu9ov$162i$1 digitaldaemon.com...
 Sean; would you mind explaining the benefits of those particular

 rules to me please? While I may be both blind and stupid, I completely

 to see where the value lies, and no-one has specified what they are.

Stroustrup has laid them out in chapter 13.1 of the "The Annotated C++ Reference Manual." The general rule for overloading is that a name is

 up first, and then overload resolution is applied. Stroustrup rejects the
 idea of ignoring scope issues when doing overload resolution. He argues

 it would be surprising in a deeply nested heirarchy of class derivation,
 that a user of a derived class would have to know too much detail about a
 base class that may be hidden. Also, he shows how surprising errors can
 creep in, such as if a function in base class B copies only the B part of

 object, but was inadvertantly called with an instance of D that didn't
 override each of those B functions properly.

That is indeed the C++ view. However you forget a number of things: 1) D is better in so many ways than C++. It says so right there in the comparison charts ... <g> 2) C++ had to deal with multiple inheritance ~ I imagine that's a big factor in the above statement. D (and Java) do not. Notice how Java dispensed with the 'alias' notion completely, and did not pay this 'Stroustrup Tax' in any shape or form. 3) Aliasing method names is truly non-intuitive. It stands out like a sore thumb within D as a kludge. It does not matter from whence it came, or what supposed pedigree it has ~ so many other things in D are completely natural. 4) You may find that a required use of "override" would alleviate some of those Stroustrup concerns. 5) You are at least as smart as B.S., and can do much better than this. I can't help but be entirely suspicious about the B.S. Gospel applying in the same manner. I challenge you, Walter, to exceed C++ in this respect also. I further challenge you to provide a reasonable D example showing where this kind of alias thing is actually beneficial to the D programmer. If you cannot do that, then this Stroustrup argument holds no water whatsoever. Since we're all required to spend a bunch of time whittling down code in bug reports for you, I think it's only fair you do the same in return with respect to issues like this. I mean, if the value of alias is so obvious, how much effort would a few good examples really take? Regards; - Kris
Jul 24 2004
next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdvjvc$1osa$1 digitaldaemon.com...
 "Walter" <newshound digitalmars.com> wrote in message
 news:cdvhgv$1nf1$1 digitaldaemon.com...
 Stroustrup has laid them out in chapter 13.1 of the "The Annotated C++
 Reference Manual." The general rule for overloading is that a name is

 up first, and then overload resolution is applied. Stroustrup rejects


 idea of ignoring scope issues when doing overload resolution. He argues

 it would be surprising in a deeply nested heirarchy of class derivation,
 that a user of a derived class would have to know too much detail about


 base class that may be hidden. Also, he shows how surprising errors can
 creep in, such as if a function in base class B copies only the B part


 an
 object, but was inadvertantly called with an instance of D that didn't
 override each of those B functions properly.

That is indeed the C++ view. However you forget a number of things: 1) D is better in so many ways than C++. It says so right there in the comparison charts ... <g>

Stroustrup's arguments apply equally to D.
 2) C++ had to deal with multiple inheritance ~ I imagine that's a big

 in the above statement. D (and Java) do not.

This behavior of C++ predated MI by several years, and his argument is not about MI.
 Notice how Java dispensed with the 'alias' notion completely, and did not

 shape or form.

I am not an experienced Java programmer, so I cannot give you an in-depth view of this from real world Java experience. But I do know that Java has made some serious errors in its language design, such as exception specifications (even though many Java programmers still believe it is a feature, not a bug <g>). So, I don't necessarilly believe that Java necessarilly got things right because Java is successful. On the other hand, I have a lot of real world experience with C++ and where programmers routinely crash & burn with it, and this behavior of C++ just doesn't come up as needing fixing.
 3) Aliasing method names is truly non-intuitive. It stands out like a sore
 thumb within D as a kludge. It does not matter from whence it came, or

 supposed pedigree it has ~ so many other things in D are completely

That all depends on one's perspective. Aliasing symbols in D is the usual way to bring a name from one scope into another. It would seem counter-intuitive for this to not work with method names <g>.
 4) You may find that a required use of  "override" would alleviate some of
 those Stroustrup concerns.

I think that would require the base class designer to know about the derived classes, something that goes against OOP principles.
 5) You are at least as smart as B.S., and can do much better than this. I
 can't help but be entirely suspicious about the B.S. Gospel applying in

 same manner.

Bjarne Stroustrup is a very smart man, and I've found his arguments are well founded in experience and rationality and not gospel. That doesn't mean I always agree with his conclusions, but one needs to have all one's ducks in a row if one wants to challenge his conclusions.
 I challenge you, Walter, to exceed C++ in this respect also.

It's entirely possible that two reasonable people can look at the same data and draw different conclusions. In this case, which is better is a matter of opinion, and reasonable people can disagree. Using an alias declaration neatly produces the behavior you desire, but as another pointed out, if the Java rules were the default there doesn't seem to be nearly so simple a way to go the other way.
 I further challenge you to provide a reasonable D example showing where

 kind of alias thing is actually beneficial to the D programmer. If you
 cannot do that, then this Stroustrup argument holds no water whatsoever.
 Since we're all required to spend a bunch of time whittling down code in

 reports for you, I think it's only fair you do the same in return with
 respect to issues like this. I mean, if the value of alias is so obvious,
 how much effort would a few good examples really take?

Stroustrup gives two examples (slightly modified here): --------------------------------- class X1 { void f(int); } // chain of derivations X(n) : X(n-1) class X9: X8 { void f(double); } void g(X9 p) { p.f(1); // X1.f or X9.f ? } ----------------------------------- His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it. The other example involves operator=(), but since D doesn't allow overloading operator=() instead I'll rewrite it as if it were a function that needs to alter a class state, and a derived class written later that 'caches' a computation on the derived state: class B { long x; void set(long i) { x = i; } void set(int i) { x = i; } long squareIt() { return x * x; } } class D : B { long square; void set(long i) { B.set(i); square = x * x; } long squareIt() { return square; } } Now, imagine B were a complex class with a lot of stuff in it, and our optimizing programmer missed the existence of set(int). Then, one has: long foo(B b) { b.set(3); return b.squareIt(); } and we have an obscure bug.
Jul 25 2004
next sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Walter wrote:

 Stroustrup gives two examples (slightly modified here):
 
 ---------------------------------
 class X1 { void f(int); }
 
  // chain of derivations X(n) : X(n-1)
 
 class X9: X8 { void f(double); }
 
 void g(X9 p)
 {
     p.f(1);    // X1.f or X9.f ?
 }
 -----------------------------------

This creates a conflict and an explicit cast should be required.
 His argument is that one can easilly miss an overload of f() somewhere in a
 complex class heirarchy, and argues that one should not need to understand
 everything about a class heirarchy in order to derive from it. The other
 example involves operator=(), but since D doesn't allow overloading
 operator=() instead I'll rewrite it as if it were a function that needs to
 alter a class state, and a derived class written later that 'caches' a
 computation on the derived state:
 
 class B
 {    long x;
      void set(long i) { x = i; }
     void set(int i) { x = i; }
     long squareIt() { return x * x; }
 }
 class D : B
 {
     long square;
     void set(long i) { B.set(i); square = x * x; }
     long squareIt() { return square; }
 }
 
 Now, imagine B were a complex class with a lot of stuff in it, and our
 optimizing programmer missed the existence of set(int). Then, one has:
 
     long foo(B b)
     {
         b.set(3);
         return b.squareIt();
     }
 
 and we have an obscure bug.

This is clearly bad design/programming of the class D. If you override some methods of B deliberately (as this person clearly do, he both use B.x and B.set), you damn sure need to know what's in B. Whether programmers doing this is able to make a good OO program at all, I highly doubt. (I don't imply that this example show your skill level in OOP, Walter :), but you should find a better example.) Lars Ivar Igesund
Jul 26 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Lars Ivar Igesund" <larsivar igesund.net> wrote in message
news:ce2a40$f4t$1 digitaldaemon.com...
 This is clearly bad design/programming of the class D. If you override
 some methods of B deliberately (as this person clearly do, he both use
 B.x and B.set), you damn sure need to know what's in B.

Of course - and that's the problem. It's an easy mistake to make, in my opinion, too easy.
 Whether
 programmers doing this is able to make a good OO program at all, I
 highly doubt. (I don't imply that this example show your skill level in
 OOP, Walter :), but you should find a better example.)

It's obvious what's wrong when this is boiled down to such a tiny example. It isn't quite so obvious when it is married to all the usual cruft one finds in a class. Analogously, we both know that this C++ code: int *p; *p = 6; is bad code, and of course we wouldn't write that. But that kind of problem does crop up because it can be buried in a lot of other code. D has automatic initialization of variables to expose such problems. The same applies to the overloading/overriding issue. It's designed to make such unintentional mistakes unlikely - to overload based on methods from another scope, you have to do it intentionally (using the alias declaration).
Jul 26 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 02:12:04 -0700, Walter <newshound digitalmars.com> 
wrote:

 "Lars Ivar Igesund" <larsivar igesund.net> wrote in message
 news:ce2a40$f4t$1 digitaldaemon.com...
 This is clearly bad design/programming of the class D. If you override
 some methods of B deliberately (as this person clearly do, he both use
 B.x and B.set), you damn sure need to know what's in B.

Of course - and that's the problem. It's an easy mistake to make, in my opinion, too easy.
 Whether
 programmers doing this is able to make a good OO program at all, I
 highly doubt. (I don't imply that this example show your skill level in
 OOP, Walter :), but you should find a better example.)

It's obvious what's wrong when this is boiled down to such a tiny example. It isn't quite so obvious when it is married to all the usual cruft one finds in a class. Analogously, we both know that this C++ code: int *p; *p = 6; is bad code, and of course we wouldn't write that. But that kind of problem does crop up because it can be buried in a lot of other code. D has automatic initialization of variables to expose such problems. The same applies to the overloading/overriding issue. It's designed to make such unintentional mistakes unlikely - to overload based on methods from another scope, you have to do it intentionally (using the alias declaration).

Aren't you only 'overloading/overriding' if the function signature is identical? Otherwise you're providing a specialisation i.e. [oload1.d] module oload1; void abc(int abc) {} [oload2.d] module oload2; import oload1; void abc(long abc) {} void main() { foo(1); foo(1L); } The above prints "long" twice, which isn't desired. The 'abc' provided by oload2 does _not_ override 'abc' provided by oload1 it's a different function signature. I agree that if it did override, it should be an error, one you could catch at compile time, one that you could solve by using an alias to specify the one you mean. Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message
news:opsbrrs9pv5a2sq9 digitalmars.com...
 Aren't you only 'overloading/overriding' if the function signature is
 identical?

No. The rule is the names are looked up first, *then* overload resolution happens. In your example below, abc is found in the current scope. oload1.abc is not found in the current scope, and therefore does not participate in function overload resolution.
 Otherwise you're providing a specialisation i.e.

 [oload1.d]
 module oload1;
 void abc(int abc) {}

 [oload2.d]
 module oload2;
 import oload1;

 void abc(long abc) {}

 void main()
 {
 foo(1);
 foo(1L);
 }

 The above prints "long" twice, which isn't desired.

 The 'abc' provided by oload2 does _not_ override 'abc' provided by oload1
 it's a different function signature.

 I agree that if it did override, it should be an error, one you could
 catch at compile time, one that you could solve by using an alias to
 specify the one you mean.

 Regan.

 -- 
 Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/

Jul 26 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 19:50:11 -0700, Walter <newshound digitalmars.com> 
wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsbrrs9pv5a2sq9 digitalmars.com...
 Aren't you only 'overloading/overriding' if the function signature is
 identical?

No. The rule is the names are looked up first, *then* overload resolution happens.

I know what the rule is, all I'm saying is that you're not technically overloading unless the function signature is the same, you're supplying a specialisation. I was replying to the comment you made: "The same applies to the overloading/overriding issue. It's designed to make such unintentional mistakes unlikely - to overload based on methods from another scope, you have to do it intentionally (using the alias declaration)." I don't believe I am overloading or overriding below. Therefore I don't believe I should need to use alias.
 In your example below, abc is found in the current scope.
 oload1.abc is not found in the current scope, and therefore does not
 participate in function overload resolution.

I know.
 Otherwise you're providing a specialisation i.e.

 [oload1.d]
 module oload1;
 void abc(int abc) {}

 [oload2.d]
 module oload2;
 import oload1;

 void abc(long abc) {}

 void main()
 {
 foo(1);
 foo(1L);
 }

 The above prints "long" twice, which isn't desired.

 The 'abc' provided by oload2 does _not_ override 'abc' provided by 
 oload1
 it's a different function signature.

 I agree that if it did override, it should be an error, one you could
 catch at compile time, one that you could solve by using an alias to
 specify the one you mean.

 Regan.

 --
 Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/


-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message
news:opsbr73ixa5a2sq9 digitalmars.com...
 On Mon, 26 Jul 2004 19:50:11 -0700, Walter <newshound digitalmars.com>
 wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsbrrs9pv5a2sq9 digitalmars.com...
 Aren't you only 'overloading/overriding' if the function signature is
 identical?

No. The rule is the names are looked up first, *then* overload


 happens.

I know what the rule is, all I'm saying is that you're not technically overloading unless the function signature is the same,

I think you mean "overriding" <g>.
 you're supplying a specialisation.

Specialization is the wrong word to use here, specialization really applies to templates, not functions. I admit the various jargons can be confusing - I think we understand each other, we're just not understanding each other's jargon.
 I was replying to the comment you made:

 "The same applies to the overloading/overriding issue. It's designed to
 make such
 unintentional mistakes unlikely - to overload based on methods from

 scope, you have to do it intentionally (using the alias declaration)."

 I don't believe I am overloading or overriding below. Therefore I don't
 believe I should need to use alias.

 In your example below, abc is found in the current scope.
 oload1.abc is not found in the current scope, and therefore does not
 participate in function overload resolution.

I know.
 Otherwise you're providing a specialisation i.e.

 [oload1.d]
 module oload1;
 void abc(int abc) {}

 [oload2.d]
 module oload2;
 import oload1;

 void abc(long abc) {}

 void main()
 {
 foo(1);
 foo(1L);
 }

 The above prints "long" twice, which isn't desired.

 The 'abc' provided by oload2 does _not_ override 'abc' provided by
 oload1
 it's a different function signature.

 I agree that if it did override, it should be an error, one you could
 catch at compile time, one that you could solve by using an alias to
 specify the one you mean.

 Regan.

 --
 Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/


-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/

Jul 26 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 23:02:07 -0700, Walter <newshound digitalmars.com> 
wrote:
 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsbr73ixa5a2sq9 digitalmars.com...
 On Mon, 26 Jul 2004 19:50:11 -0700, Walter <newshound digitalmars.com>
 wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsbrrs9pv5a2sq9 digitalmars.com...
 Aren't you only 'overloading/overriding' if the function signature is
 identical?

No. The rule is the names are looked up first, *then* overload


 happens.

I know what the rule is, all I'm saying is that you're not technically overloading unless the function signature is the same,

I think you mean "overriding" <g>.

Either or, I used both above, you used both above in your original paragraph.
 you're supplying a specialisation.

Specialization is the wrong word to use here, specialization really applies to templates, not functions.

Common usage agrees with you, and perhaps my usage is a little loose, what I meant was that if you have a method foo(int) and you create foo(float) you're creating a method specifically to handle floats, thus it's a specialist method.. the looseness comes from the fact that the original method was also a specialist method to handle int, in fact that there is no generic method as it's impossible to write one, as we have no variant type.
 I admit the various jargons can be confusing -
 I think we understand each other, we're just not understanding each 
 other's
 jargon.

Perhaps.. what I am trying to say is that this: class X1 { void foo(int); } class X2 : X1 { void foo(float); } is _not_ the same as: class X1 { void foo(int); } class X2 : X1 { void foo(int); } the former 'extends' the parent class (I'll use extends instead of specialise), the latter overrides a method in the parent class. Ideally they should be treated differently. The former should be treated the same as if the methods had different names. This brings me to Issue 1: Requiring the use of 'override' facilitates treating them differently, if the programmer intends to override they say so, otherwise they assumed to be extending. This catches the accidental override and accidental not-override bugs. I believe it's a safe simple addition to the language. Issue 2: When you are extending a class you want it's public methods to be part of the child class, this works fine so long as the methods have different names, but, as soon as the methods have the same name you have to do something extra, i.e. use an alias, this is counter intuitive. I have no solution to this issue. Issue 3: When using interfaces, inherited methods do not satisfy the interface, this to me, is lunacy... why shouldn't they? The class does provide the methods the interface requires, it seems to me that to solve this all you need to do is walk the class heirarchy and notice the methods. Such that the following should be valid: interface FooBar { void foo(int a); void bar(int a); } class Parent { void foo(int a) { printf("Parent:foo:int\n"); } } class Child : Parent, FooBar { void bar(int a) { printf("Child:bar:int\n"); } } void main() { Child c = new Child(); c.foo(1); c.bar(2); } If Issue 2 and Issue 3 collide then it should be possible to use alias to alias-in the methods to satisfy the interface. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 27 2004
prev sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Regan Heath wrote:
 
 Aren't you only 'overloading/overriding' if the function signature is 
 identical?
 Otherwise you're providing a specialisation i.e.

I find it hard to follow your argumentation, probably due to the above statement. overloading and overriding is two different things: overriding is the reimplementation of a method in a subclass (having the same signature as a method in the superclass) overloading is when several methods/functions with the same name, but different signatures are provided. Lars Ivar Igesund
Jul 27 2004
next sibling parent Arcane Jill <Arcane_member pathlink.com> writes:
In article <ce4st1$1sc2$1 digitaldaemon.com>, Lars Ivar Igesund says...

overriding is the reimplementation of a method in a subclass (having the 
same signature as a method in the superclass)

overloading is when several methods/functions with the same name, but 
different signatures are provided.

So you can have a function which both overloads and overrides at the same time. And then there's "operator overloading" which means something else entirely, so now you can both overload and override an operator overload. Ah Jeez, I'm giving up on this one. Personally I think that "superclass" and "subclass" are named upside down*, so who am I to argue? Jill :) *If A is a superclass of B, then the set of member variables/methods of A is a SUBset of the set of member variables/methods of B.
Jul 27 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Tue, 27 Jul 2004 08:36:45 +0100, Lars Ivar Igesund 
<larsivar igesund.net> wrote:

 Regan Heath wrote:
 Aren't you only 'overloading/overriding' if the function signature is 
 identical?
 Otherwise you're providing a specialisation i.e.

I find it hard to follow your argumentation, probably due to the above statement. overloading and overriding is two different things: overriding is the reimplementation of a method in a subclass (having the same signature as a method in the superclass) overloading is when several methods/functions with the same name, but different signatures are provided.

I mean't overriding. I now have a better understanding of the common terminology, consider my paragraph to be... " Aren't you only overriding if the function signature is identical? Otherwise you're providing an overloading i.e. " Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 27 2004
prev sibling next sibling parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Thank you for the examples.

"Walter"  wrote .
 Stroustrup's arguments apply equally to D.

But yet again you fail to qualify why
 I am not an experienced Java programmer, so I cannot give you an in-depth
 view of this from real world Java experience. But I do know that Java has
 made some serious errors in its language design, such as exception
 specifications (even though many Java programmers still believe it is a
 feature, not a bug <g>). So, I don't necessarilly believe that Java
 necessarilly got things right because Java is successful. On the other

 I have a lot of real world experience with C++ and where programmers
 routinely crash & burn with it, and this behavior of C++ just doesn't come
 up as needing fixing.

Nobody is saying that Java got everything right Walter. If it did, we wouldn't be here now, would we? I happen to agree with you that the Exception problem is very real, but let's not divert our focus yet again please. The point remains: if Java can handle it, then so can D. Rather than give that assertion some credence, you appear to brush it off as not even worth looking into.
 4) You may find that a required use of  "override" would alleviate some


 those Stroustrup concerns.

I think that would require the base class designer to know about the

 classes, something that goes against OOP principles.

The D "override" keyword does not belong in a base class, as you well know. Instead, it goes into a derived class. I was referring to the debate over whether "override" should be mandatory or not, and perhaps that might help out with some of these concerns. Apparently it does not.
 opinion, and reasonable people can disagree. Using an alias declaration
 neatly produces the behavior you desire, but as another pointed out, if

 Java rules were the default there doesn't seem to be nearly so simple a

 to go the other way.

That's perhaps because you haven't given it very much thought? Do you perhaps think that not even one person on the NG could suggest a better approach? Or is that simply not an acceptable way to enhance the language? - Kris
Jul 26 2004
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 25 Jul 2004 23:18:41 -0700, Walter wrote:
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:cdvjvc$1osa$1 digitaldaemon.com...

[snip]
 I further challenge you to provide a reasonable D example showing where

 kind of alias thing is actually beneficial to the D programmer. If you
 cannot do that, then this Stroustrup argument holds no water whatsoever.
 Since we're all required to spend a bunch of time whittling down code in

 reports for you, I think it's only fair you do the same in return with
 respect to issues like this. I mean, if the value of alias is so obvious,
 how much effort would a few good examples really take?

Stroustrup gives two examples (slightly modified here): --------------------------------- class X1 { void f(int); } // chain of derivations X(n) : X(n-1) class X9: X8 { void f(double); } void g(X9 p) { p.f(1); // X1.f or X9.f ? } ----------------------------------- His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it.

This example has now convinced me the both Stroustrup and Walter have got it wrong. At first, I thought that all I need do is make some explicit casts, but... Here is some D code to show it's madness... <code> class X1 { void f(int x) {printf("1\n");} } class X2: X1 { void f(long x) {printf("2\n");} } class X3: X2 { void f(uint x) {printf("3\n");} } class X4: X3 { void f(char x) {printf("4\n");} } class X5: X4 { void f(double x){printf("5\n");} } void main() { X5 p = new X5; p.f(cast(int)1); p.f(cast(long)1); p.f(cast(uint)1); p.f(cast(char)1); p.f(cast(double)1); p.f(1); } </code> And the result... c:\temp>dmd test C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test 5 5 5 5 5 5 c:\temp> WHAT!?!? The coder has *explicitly* said he wanted an 'int'/'long'/'uint'... argument but D just went and converted to 'double' anyhow. Even the non-cast call did *not* use a double as the argument value; 1 is an integer not any form of floating point number. Then I changed X5 to include the 'alias X1.f f;' line and get this message... test.d(10): function f overloads void(double x) and void(int x) both match argument list for f WHAT?!?!? Since when is (double x) and (int x) the same? This is *not* a match. This current method matching rule is starting to seem more like lunacy than sanity. I thought compilers were supposed to help coders. This is just making more work rather than less work. Here is the code to make it work the 'intuitive' way... <code> class X1 { void f(int x) {printf("1\n");} } class X2: X1 { void f(long x) {printf("2\n");} alias X1.f f; } class X3: X2 { void f(uint x) {printf("3\n");} alias X1.f f; alias X2.f f; } class X4: X3 { void f(char x) {printf("4\n");} alias X1.f f; alias X2.f f; alias X3.f f; } class X5: X4 { void f(double x){printf("5\n");} alias X1.f f; alias X2.f f; alias X3.f f; alias X4.f f; } void main() { X5 p = new X5; p.f(cast(int)1); p.f(cast(long)1); p.f(cast(uint)1); p.f(cast(char)1); p.f(cast(double)1); p.f(1); } </code> <output> c:\temp>dmd test C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi; c:\temp>test 1 2 3 4 5 1 </output> And that's just for *one* method.
 The other
 example involves operator=(), but since D doesn't allow overloading
 operator=() instead I'll rewrite it as if it were a function that needs to
 alter a class state, and a derived class written later that 'caches' a
 computation on the derived state:
 
 class B
 {    long x;
      void set(long i) { x = i; }
     void set(int i) { x = i; }
     long squareIt() { return x * x; }
 }
 class D : B
 {
     long square;
     void set(long i) { B.set(i); square = x * x; }
     long squareIt() { return square; }
 }
 
 Now, imagine B were a complex class with a lot of stuff in it, and our
 optimizing programmer missed the existence of set(int). Then, one has:
 
     long foo(B b)
     {
         b.set(3);
         return b.squareIt();
     }
 
 and we have an obscure bug.

This type of bug is analogous to a spelling mistake. Its similar to the situation where two methods are spelled almost identically and the coder either chooses the wrong one or mistypes it. It is not a fault of OO or the language, but of the coder or class designer. I really don't mind being convinced that Walter (D) has got it right, but this argument doesn't do it. -- Derek Melbourne, Australia 26/Jul/04 4:45:21 PM
Jul 26 2004
next sibling parent stonecobra <scott stonecobra.com> writes:
Derek Parnell wrote:

 This example has now convinced me the both Stroustrup and Walter have got
 it wrong. At first, I thought that all I need do is make some explicit
 casts, but...
 
 Here is some D code to show it's madness...
 
 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");} }
 class X3: X2 { void f(uint x)  {printf("3\n");} }
 class X4: X3 { void f(char x)  {printf("4\n");} }
 class X5: X4 { void f(double x){printf("5\n");} }
 void main()
 {
   X5 p = new X5;
   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>
 
 And the result...
 
  c:\temp>dmd test
  C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;
 
  c:\temp>test
  5
  5
  5
  5
  5
  5
 
  c:\temp>
 
 
 WHAT!?!? The coder has *explicitly* said he wanted an
 'int'/'long'/'uint'... argument but D just went and converted to 'double'
 anyhow. Even the non-cast call did *not* use a double as the argument
 value; 1 is an integer not any form of floating point number.

 This current method matching rule is starting to seem more like lunacy than
 sanity. I thought compilers were supposed to help coders. This is just
 making more work rather than less work. Here is the code to make it work
 the 'intuitive' way...

So, Walter, are you saying that alias is necessary to protect us from this? I, for one do not need to be protected from superclasses, and I think quite a few people are saying that. Why can't the scoping rules search EVERYWHERE for an exact match (since 99.999999% of the time that's what you intend), and then use your current scoping rules if you can't find an exact match? I agree with Derek: 'This current method matching rule is starting to seem more like lunacy than sanity'. Are you saying that I didn't know what I was doing as a programmer when I inherited from a base class, and I MUST use alias to show my intentions? This is one of the most counter-intuitive ideas I've seen in quite a while, and will send many budding library writers to an early D death, which is not what you are looking for. This is not anywhere near "The Principle of Least Surprise" Scott Sanders
Jul 26 2004
prev sibling next sibling parent reply Andy Friesen <andy ikagames.com> writes:
Derek Parnell wrote:

 Here is some D code to show it's madness...
 
 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");} }
 class X3: X2 { void f(uint x)  {printf("3\n");} }
 class X4: X3 { void f(char x)  {printf("4\n");} }
 class X5: X4 { void f(double x){printf("5\n");} }
 void main()
 {
   X5 p = new X5;
   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>
 
 And the result...
 
  c:\temp>dmd test
  C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;
 
  c:\temp>test
  5
  5
  5
  5
  5
  5
 
  c:\temp>
 
 
 WHAT!?!? The coder has *explicitly* said he wanted an
 'int'/'long'/'uint'... argument but D just went and converted to 'double'
 anyhow. Even the non-cast call did *not* use a double as the argument
 value; 1 is an integer not any form of floating point number.

C++ and C# do exactly the same thing, which suggests that overloading and inheritance have never been good bedfellows, if for no other reason than because the need for it rarely arises. That being said, extending a class is a conceptual joining of everything in the base class, plus everything defined in the subclass. It seems logical that the language reflect this behaviour and resolve names using the full class, not just the part of it defined in the current class block. This also inhibits things like template classes which generate class heirarchies from typelists, but this might be a good thing. ;) -- andy
Jul 26 2004
parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <ce3abq$1778$1 digitaldaemon.com>, Andy Friesen says...

  c:\temp>test
  5
  5
  5
  5
  5
  5
 
  c:\temp>


C++ and C# do exactly the same thing, which suggests that overloading 
and inheritance have never been good bedfellows, if for no other reason 
than because the need for it rarely arises.

I actually got caught out with this one a few years back - in C++. I thought the behavior was wrong then, and I still think it's wrong now. At the time, I didn't realize that C++ was defined that way, I just assumed that the compiler was crap. I had to workaround it by overriding the base class function in the derived class with an exact signature match, and defining the overridden function to call the base class function. That was a very time consuming bug to find, and the workaround/fix was not very satisfying. C++ got this one wrong. Java seems to have got it right. (In my humble opinion). This use of alias (in D) looks just like #define to me. That's obfuscation, not clarity. Jill
Jul 26 2004
parent "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:ce3mb4$1dn3$1 digitaldaemon.com...
 I actually got caught out with this one a few years back - in C++. I

 behavior was wrong then, and I still think it's wrong now. At the time, I

 realize that C++ was defined that way, I just assumed that the compiler

 crap. I had to workaround it by overriding the base class function in the
 derived class with an exact signature match, and defining the overridden
 function to call the base class function. That was a very time consuming

 find, and the workaround/fix was not very satisfying. C++ got this one

 Java seems to have got it right. (In my humble opinion).

Inserting a 'using' declaration in C++ is the usual way to make this work, not creating wrappers. The using declaration in that case works analogously to the 'alias' declaration in D.
Jul 26 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Derek Parnell" <derek psych.ward> wrote in message
news:ce2ce5$ib8$1 digitaldaemon.com...
 Here is some D code to show it's madness...

 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");} }
 class X3: X2 { void f(uint x)  {printf("3\n");} }
 class X4: X3 { void f(char x)  {printf("4\n");} }
 class X5: X4 { void f(double x){printf("5\n");} }
 void main()
 {
   X5 p = new X5;
   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>

 And the result...

  c:\temp>dmd test
  C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;

  c:\temp>test
  5
  5
  5
  5
  5
  5

  c:\temp>


 WHAT!?!? The coder has *explicitly* said he wanted an
 'int'/'long'/'uint'... argument but D just went and converted to 'double'
 anyhow.

That's normal behavior, it's the integral argument promotion rules in C and C++ which survives intact in D.
 Even the non-cast call did *not* use a double as the argument
 value; 1 is an integer not any form of floating point number.

But it's implicitly convertible to a double.
 Then I changed X5 to include the 'alias X1.f f;' line and get this
 message...

   test.d(10): function f overloads void(double x) and void(int x) both
   match argument list for f

 WHAT?!?!? Since when is (double x) and (int x) the same? This is *not* a
 match.

That's a compiler bug :-(
 This current method matching rule is starting to seem more like lunacy

 sanity. I thought compilers were supposed to help coders. This is just
 making more work rather than less work. Here is the code to make it work
 the 'intuitive' way...

 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");}
                alias X1.f f;
              }
 class X3: X2 { void f(uint x)  {printf("3\n");}
                alias X1.f f;
                alias X2.f f;
              }
 class X4: X3 { void f(char x)  {printf("4\n");}
                alias X1.f f;
                alias X2.f f;
                alias X3.f f;
              }
 class X5: X4 { void f(double x){printf("5\n");}
                alias X1.f f;
                alias X2.f f;
                alias X3.f f;
                alias X4.f f;
              }
 void main()
 {
   X5 p = new X5;

   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>

 <output>
 c:\temp>dmd test
 C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;

 c:\temp>test
 1
 2
 3
 4
 5
 1
 </output>
 And that's just for *one* method.

It's not that bad. The following should work: class X1 { void f(int x) {printf("1\n");} } class X2: X1 { void f(long x) {printf("2\n");} alias X1.f f; } class X3: X2 { void f(uint x) {printf("3\n");} alias X2.f f; } class X4: X3 { void f(char x) {printf("4\n");} alias X3.f f; } class X5: X4 { void f(double x){printf("5\n");} alias X4.f f; } as the aliases become part of the names in a scope, and so get carried along with the other names with a subsequent alias. And I'd add that if one actually wrote such an inheritance graph like that, the aliases are a good thing because they show you *intended* to overload them based on names from another scope.
 This type of bug is analogous to a spelling mistake. Its similar to the
 situation where two methods are spelled almost identically and the coder
 either chooses the wrong one or mistypes it.

You're right. But spelling mistakes tend to get picked up by the compiler and show up as "undefined identifier" messages. Languages that implicitly declare misspelled identifiers tend to be very hard languages to debug code in. To extend the analogy to the example, I'd rather have to proactively say I want to include the declarations from another scope (with an alias declaration) than have it happen implicitly with no way to turn it off, and subtle problems result. Think of the alias declaration like an explicit cast - it tells the compiler (and the maintenance programmer) that "yes, I intended to do this" and the compiler says "yes sir, three bags full sir" and adds the methods into the scope and does the cast.
Jul 26 2004
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <ce3c7j$18du$1 digitaldaemon.com>, Walter says...
"Derek Parnell" <derek psych.ward> wrote in message
news:ce2ce5$ib8$1 digitaldaemon.com...
 Here is some D code to show it's madness...

 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");} }
 class X3: X2 { void f(uint x)  {printf("3\n");} }
 class X4: X3 { void f(char x)  {printf("4\n");} }
 class X5: X4 { void f(double x){printf("5\n");} }
 void main()
 {
   X5 p = new X5;
   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>

 And the result...

  c:\temp>dmd test
  C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;

  c:\temp>test
  5
  5
  5
  5
  5
  5

  c:\temp>

 WHAT!?!? The coder has *explicitly* said he wanted an
 'int'/'long'/'uint'... argument but D just went and converted to 'double'
 anyhow.

That's normal behavior, it's the integral argument promotion rules in C and C++ which survives intact in D.
 Even the non-cast call did *not* use a double as the argument
 value; 1 is an integer not any form of floating point number.

But it's implicitly convertible to a double.

I like the current D scheme. In a sense, every class defines its own interface and calls are evaluated against this interface before walking the inheritance tree. This safety is a good thing. The alternative would be code that subtly breaks if a function prototype is added or altered somewhere up the inheritance tree. I'm surprised that folks can argue that "override" should be mandatory and then argue that the current D lookup scheme is broken, since the goal of both things is to protect the programmer from subtle inheritance-based coding errors. As far as the above example is concerned, perhaps this is an argument for the "explicit" keyword. It would certainly reduce or eliminate much of the confusion about D's lookup rules. Sean
Jul 26 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Mon, 26 Jul 2004 19:12:00 +0000 (UTC), Sean Kelly <sean f4.ca> wrote:
 In article <ce3c7j$18du$1 digitaldaemon.com>, Walter says...
 "Derek Parnell" <derek psych.ward> wrote in message
 news:ce2ce5$ib8$1 digitaldaemon.com...
 Here is some D code to show it's madness...

 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");} }
 class X3: X2 { void f(uint x)  {printf("3\n");} }
 class X4: X3 { void f(char x)  {printf("4\n");} }
 class X5: X4 { void f(double x){printf("5\n");} }
 void main()
 {
   X5 p = new X5;
   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>

 And the result...

  c:\temp>dmd test
  C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;

  c:\temp>test
  5
  5
  5
  5
  5
  5

  c:\temp>

 WHAT!?!? The coder has *explicitly* said he wanted an
 'int'/'long'/'uint'... argument but D just went and converted to 
 'double'
 anyhow.

That's normal behavior, it's the integral argument promotion rules in C and C++ which survives intact in D.
 Even the non-cast call did *not* use a double as the argument
 value; 1 is an integer not any form of floating point number.

But it's implicitly convertible to a double.

I like the current D scheme. In a sense, every class defines its own interface and calls are evaluated against this interface before walking the inheritance tree. This safety is a good thing. The alternative would be code that subtly breaks if a function prototype is added or altered somewhere up the inheritance tree. I'm surprised that folks can argue that "override" should be mandatory and then argue that the current D lookup scheme is broken, since the goal of both things is to protect the programmer from subtle inheritance-based coding errors.

Requiring override protects you from doing this... class X1 { void read(ubyte[] buf); } classes X2 thru X8 class X9 : X8 { void read(ubyte[] buf); } In other words accidently overriding a function in a base class with the _exact same_ function signature. The problem with the lookup rules is _not_ that they do not find functions of the same signature, because they _do_, it's that they do not find functions of a different signature _without_ using alias. eg. class X1 { void read(char[] buf); } classes X2 thru X8 class X9 : X8 { void read(ubyte[] buf); } void main() { X9 p = new X9(); char[] line; p.read(line); }
 As far as the above example is concerned, perhaps this is an argument 
 for the
 "explicit" keyword.  It would certainly reduce or eliminate much of the
 confusion about D's lookup rules.


 Sean

-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 26 2004
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Mon, 26 Jul 2004 02:59:49 -0700, Walter wrote:

 "Derek Parnell" <derek psych.ward> wrote in message
 news:ce2ce5$ib8$1 digitaldaemon.com...
 Here is some D code to show it's madness...

 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");} }
 class X3: X2 { void f(uint x)  {printf("3\n");} }
 class X4: X3 { void f(char x)  {printf("4\n");} }
 class X5: X4 { void f(double x){printf("5\n");} }
 void main()
 {
   X5 p = new X5;
   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>

 And the result...

  c:\temp>dmd test
  C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;

  c:\temp>test
  5
  5
  5
  5
  5
  5

  c:\temp>


 WHAT!?!? The coder has *explicitly* said he wanted an
 'int'/'long'/'uint'... argument but D just went and converted to 'double'
 anyhow.

That's normal behavior, it's the integral argument promotion rules in C and C++ which survives intact in D.

...and 'normal' is equivalent to 'desirable', right? It seems wrong to me that if I _explicitly_ code an integer that the compiler silently converts this to something else *before* checking for a matching method signature. When I go to the beach and surf all day, its normal to get sand in my shorts, but it doesn't mean I like it.
 Even the non-cast call did *not* use a double as the argument
 value; 1 is an integer not any form of floating point number.

But it's implicitly convertible to a double.

It's also implicitly convertible to many other things too! So why pick on 'double'?
 Then I changed X5 to include the 'alias X1.f f;' line and get this
 message...

   test.d(10): function f overloads void(double x) and void(int x) both
   match argument list for f

 WHAT?!?!? Since when is (double x) and (int x) the same? This is *not* a
 match.

That's a compiler bug :-(

Oh! Okay then.
 This current method matching rule is starting to seem more like lunacy

 sanity. I thought compilers were supposed to help coders. This is just
 making more work rather than less work. Here is the code to make it work
 the 'intuitive' way...

 <code>
 class X1     { void f(int x)   {printf("1\n");} }
 class X2: X1 { void f(long x)  {printf("2\n");}
                alias X1.f f;
              }
 class X3: X2 { void f(uint x)  {printf("3\n");}
                alias X1.f f;
                alias X2.f f;
              }
 class X4: X3 { void f(char x)  {printf("4\n");}
                alias X1.f f;
                alias X2.f f;
                alias X3.f f;
              }
 class X5: X4 { void f(double x){printf("5\n");}
                alias X1.f f;
                alias X2.f f;
                alias X3.f f;
                alias X4.f f;
              }
 void main()
 {
   X5 p = new X5;

   p.f(cast(int)1);
   p.f(cast(long)1);
   p.f(cast(uint)1);
   p.f(cast(char)1);
   p.f(cast(double)1);
   p.f(1);
 }
 </code>

 <output>
 c:\temp>dmd test
 C:\DPARNELL\DMD\BIN\..\..\dm\bin\link.exe test,,,user32+kernel32/noi;

 c:\temp>test
 1
 2
 3
 4
 5
 1
 </output>
 And that's just for *one* method.

It's not that bad. The following should work: class X1 { void f(int x) {printf("1\n");} } class X2: X1 { void f(long x) {printf("2\n");} alias X1.f f; } class X3: X2 { void f(uint x) {printf("3\n");} alias X2.f f; } class X4: X3 { void f(char x) {printf("4\n");} alias X3.f f; } class X5: X4 { void f(double x){printf("5\n");} alias X4.f f; }

Yep it does. That is a suitable reduction in typing.
 as the aliases become part of the names in a scope, and so get carried along
 with the other names with a subsequent alias. And I'd add that if one
 actually wrote such an inheritance graph like that, the aliases are a good
 thing because they show you *intended* to overload them based on names from
 another scope.

Okay, so I need to read alias /classX/./funcA/ /funcB/ in this context as "Assume that all the methods called /funcA/ from /classX/ (and whatever it has aliased) have been typed into this class, but are called /funcB/ in this class."
 This type of bug is analogous to a spelling mistake. Its similar to the
 situation where two methods are spelled almost identically and the coder
 either chooses the wrong one or mistypes it.

You're right. But spelling mistakes tend to get picked up by the compiler and show up as "undefined identifier" messages. Languages that implicitly declare misspelled identifiers tend to be very hard languages to debug code in.

Agreed, but that's not what I was referring to. More like this ... void foo() { . . . }; void fooo() { . . . }; And the coder type in 'foo();' when they really meant 'fooo();' The compiler (and coder) aint <g> necessarily going to notice this until run time.
To extend the analogy to the example, I'd rather have to proactively say
 I want to include the declarations from another scope (with an alias
 declaration) than have it happen implicitly with no way to turn it off, and
 subtle problems result. Think of the alias declaration like an explicit
 cast - it tells the compiler (and the maintenance programmer) that "yes, I
 intended to do this" and the compiler says "yes sir, three bags full sir"
 and adds the methods into the scope and does the cast.

It just that I would have thought that just by deriving a class from another, the coder is already explicitly saying that they intend to use the methods in the super-class. To me this seems a normal (there's that word again) thing to do. Otherwise it like saying "Derive classX from classY and use methods A, B, and D from classY, and not any others.". Whereas I tend to think along the lines of "Derive classX from classY except for methods C, E and F". I will readjust my thinking, of course, as I don't expect D will change in this respect ;-) -- Derek Melbourne, Australia 27/Jul/04 11:05:01 AM
Jul 26 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Derek Parnell" <derek psych.ward> wrote in message
news:ce4bb2$1liq$1 digitaldaemon.com...
 WHAT!?!? The coder has *explicitly* said he wanted an
 'int'/'long'/'uint'... argument but D just went and converted to



 anyhow.



 C++ which survives intact in D.

that if I _explicitly_ code an integer that the compiler silently converts this to something else *before* checking for a matching method signature.

No, that's not what's happening. What is happening is that name lookup happens *before* signature matching. When a name is found, *only* matching names in that name's scope are checked for a signature match. In this case, the only name in the scope takes a double argument, and an int is implicitly convertible to a double, hence it is a match.
 But it's implicitly convertible to a double.

'double'?

Because the only name in the scope takes a double as an argument.
 as the aliases become part of the names in a scope, and so get carried


 with the other names with a subsequent alias. And I'd add that if one
 actually wrote such an inheritance graph like that, the aliases are a


 thing because they show you *intended* to overload them based on names


 another scope.

Okay, so I need to read alias /classX/./funcA/ /funcB/ in this context as "Assume that all the methods called /funcA/ from /classX/ (and whatever it has aliased) have been typed into this class, but are called /funcB/ in this class."

Yes.
 It just that I would have thought that just by deriving a class from
 another, the coder is already explicitly saying that they intend to use

 methods in the super-class. To me this seems a normal (there's that word
 again) thing to do. Otherwise it like saying "Derive classX from classY

 use methods A, B, and D from classY, and not any others.". Whereas I tend
 to think along the lines of "Derive classX from classY except for methods
 C, E and F".

 I will readjust my thinking, of course, as I don't expect D will change in
 this respect ;-)

The only rule to remember here is that name lookup happens before overload resolution <g>.
Jul 26 2004
parent Derek Parnell <derek psych.ward> writes:
On Mon, 26 Jul 2004 20:02:47 -0700, Walter wrote:

 "Derek Parnell" <derek psych.ward> wrote in message
 news:ce4bb2$1liq$1 digitaldaemon.com...

 I will readjust my thinking, of course, as I don't expect D will change in
 this respect ;-)

The only rule to remember here is that name lookup happens before overload resolution <g>.

Right! Thanks. It all makes sense now. Not good sense, not clever sense, but at least I know where the craziness lies now. I'll go away now and write lot's of D code to see if it still seems wrong. Maybe I could get used to the sand in my shorts <g> -- Derek Melbourne, Australia 27/Jul/04 1:44:30 PM
Jul 26 2004
prev sibling next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <ce27vn$diq$1 digitaldaemon.com>, Walter says...

Stroustrup gives two examples (slightly modified here):

---------------------------------
class X1 { void f(int); }

 // chain of derivations X(n) : X(n-1)

class X9: X8 { void f(double); }

void g(X9 p)
{
    p.f(1);    // X1.f or X9.f ?
}
-----------------------------------

X1.f, obviously. It's an exact match. There is no ambiguity there.
His argument is that one can easilly miss an overload of f() somewhere in a
complex class heirarchy,

Then one should RTFM. Java, of course, has a compiler which can automatically generate documentation, which is a big boon. When you look at the docs for a Java class, you see an inheritance diagram, and can click on the superclass to see /its/ documentation. I believe Doxygen can do something like that for us in D, but not everyone uses it, and I'm not completely sure how it can connect a class to its superclass if they were written by different authors and exist in two completely independent modules. For example, everything derives from Object, but Object is not doxygenated. Java's Object class, by contrast, is fully Javadocked. It seems to me that this is actually an argument in favor of properly documenting everything in an integrated way. Another way to look at this is, this is perhaps a place where a lint-like-tool (OT: Why is it called "lint" anyway?) might issue a warning. But since D itself doesn't do warnings, it should just go ahead and compile it.
and argues that one should not need to understand
everything about a class heirarchy in order to derive from it.

But why not? Okay, second example....
class B
{    long x;
     void set(long i) { x = i; }
    void set(int i) { x = i; }
    long squareIt() { return x * x; }
}
class D : B
{
    long square;
    void set(long i) { B.set(i); square = x * x; }
    long squareIt() { return square; }
}

Now, imagine B were a complex class with a lot of stuff in it, and our
optimizing programmer missed the existence of set(int). Then, one has:

    long foo(B b)
    {
        b.set(3);
        return b.squareIt();
    }

and we have an obscure bug.

<sarcasm>I take it you haven't heard of something called "design by contract" then? It's a feature of D which C++ does not possess.</sarcasm> # class D : B # { # invariant # { # assert(square == i * i); # } # }
Jul 26 2004
next sibling parent Daniel Horn <hellcatv hotmail.com> writes:
But now you have turned a compile time bug into a run time bug
I have to agree with walter here (I tend to undervalue DbC because so 
far it's been a runtime system, and I enjoy resting (more) easy once it 
compiles...)  I'm never gonna test every branch in my whole code in a 
big enough project.


however I still support this override keyword, and I have not heard 
Walter's comment on that :-)



 
 Okay, second example....
 
 
class B
{    long x;
    void set(long i) { x = i; }
   void set(int i) { x = i; }
   long squareIt() { return x * x; }
}
class D : B
{
   long square;
   void set(long i) { B.set(i); square = x * x; }
   long squareIt() { return square; }
}

Now, imagine B were a complex class with a lot of stuff in it, and our
optimizing programmer missed the existence of set(int). Then, one has:

   long foo(B b)
   {
       b.set(3);
       return b.squareIt();
   }

and we have an obscure bug.

<sarcasm>I take it you haven't heard of something called "design by contract" then? It's a feature of D which C++ does not possess.</sarcasm> # class D : B # { # invariant # { # assert(square == i * i); # } # }

Jul 26 2004
prev sibling parent "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:ce2d32$iso$1 digitaldaemon.com...
 In article <ce27vn$diq$1 digitaldaemon.com>, Walter says...
Stroustrup gives two examples (slightly modified here):

---------------------------------
class X1 { void f(int); }

 // chain of derivations X(n) : X(n-1)

class X9: X8 { void f(double); }

void g(X9 p)
{
    p.f(1);    // X1.f or X9.f ?
}
-----------------------------------

X1.f, obviously. It's an exact match. There is no ambiguity there.
His argument is that one can easilly miss an overload of f() somewhere in


complex class heirarchy,

Then one should RTFM.

The idea is to reduce the likelihood of unintentional behavior. It doesn't really do much good to keep telling people to RTFM.
and argues that one should not need to understand
everything about a class heirarchy in order to derive from it.


Because we are people and we don't work that way. How many of us even read the instructions that come with things? I can't remake people, all I can do is try to design a language that will reduce the probability of unintended consequences of not thoroughly understanding classes before making use of them. (Ever looked at STL classes? <g>) And I've myself made such mistakes even with base classes I wrote myself.
 Okay, second example....

class B
{    long x;
     void set(long i) { x = i; }
    void set(int i) { x = i; }
    long squareIt() { return x * x; }
}
class D : B
{
    long square;
    void set(long i) { B.set(i); square = x * x; }
    long squareIt() { return square; }
}

Now, imagine B were a complex class with a lot of stuff in it, and our
optimizing programmer missed the existence of set(int). Then, one has:

    long foo(B b)
    {
        b.set(3);
        return b.squareIt();
    }

and we have an obscure bug.

<sarcasm>I take it you haven't heard of something called "design by

 then? It's a feature of D which C++ does not possess.</sarcasm>

Of course, there's always the characteristic that if one boils a complex piece of code down to its minimum, the cause of the problem jumps out at one, and one could say don't write the code that way. When it happens in the real world, it happens buried in complex code with complicated interrelationships, it can be pretty hard to spot, and a simple DbC line may be inadequate. Furthermore, isn't it better to catch a bug without needing to write additional code to check for it?
Jul 26 2004
prev sibling parent reply Roberto Mariottini <Roberto_member pathlink.com> writes:
In article <ce27vn$diq$1 digitaldaemon.com>, Walter says...

His argument is that one can easilly miss an overload of f() somewhere in a
complex class heirarchy, and argues that one should not need to understand
everything about a class heirarchy in order to derive from it. The other
example involves operator=(), but since D doesn't allow overloading
operator=() instead I'll rewrite it as if it were a function that needs to
alter a class state, and a derived class written later that 'caches' a
computation on the derived state:

I translated your example in Java, and it shows the "wrong" (non-C++) behaviour. The attached Test.java prints: The square is: 0 Ciao
Jul 26 2004
parent "Walter" <newshound digitalmars.com> writes:
"Roberto Mariottini" <Roberto_member pathlink.com> wrote in message
news:ce2gc7$kr2$1 digitaldaemon.com...
 In article <ce27vn$diq$1 digitaldaemon.com>, Walter says...
His argument is that one can easilly miss an overload of f() somewhere in


complex class heirarchy, and argues that one should not need to


everything about a class heirarchy in order to derive from it. The other
example involves operator=(), but since D doesn't allow overloading
operator=() instead I'll rewrite it as if it were a function that needs


alter a class state, and a derived class written later that 'caches' a
computation on the derived state:

I translated your example in Java, and it shows the "wrong" (non-C++)

 The attached Test.java prints:

 The square is: 0

Thank-you. I expected Java to do that, it's nice to have confirmation.
Jul 26 2004
prev sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Once again, I find myself in the middle.  Theoretically, I agree with 
you, Kris; the current rules don't make any sense.  However, I think 
that I can use the same argument you used to support "override" to 
support Walter's position here.  Here's the argument:

You implement a class like this, where "Base" is a base class from some 
library:

class Child : Base {
   void foo(int x) {...}
}

Then you write this code:

extern (C) void call_foo(void *ptr, char c) {
   printf("I'm about to call foo(int)\n");
   Child ref = cast(Child)ptr;
   assert(ref !== null);
   ref.foo(c);
   printf("I'm done calling foo(int)\n");
}

This all works ok.  Your code correctly does the implicit cast of char 
to int.  However, later, the library adds a member function:

class Base {
   void foo(char c) {...}
}

Base's foo() has nothing to do with your version of foo().  Should the 
code call Base.foo(char) or Child.foo(int)?

Kris wrote:
 "Walter" <newshound digitalmars.com> wrote in message
 news:cdvhgv$1nf1$1 digitaldaemon.com...
 
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdu9ov$162i$1 digitaldaemon.com...

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.

Stroustrup has laid them out in chapter 13.1 of the "The Annotated C++ Reference Manual." The general rule for overloading is that a name is

looked
up first, and then overload resolution is applied. Stroustrup rejects the
idea of ignoring scope issues when doing overload resolution. He argues

that
it would be surprising in a deeply nested heirarchy of class derivation,
that a user of a derived class would have to know too much detail about a
base class that may be hidden. Also, he shows how surprising errors can
creep in, such as if a function in base class B copies only the B part of

an
object, but was inadvertantly called with an instance of D that didn't
override each of those B functions properly.

That is indeed the C++ view. However you forget a number of things: 1) D is better in so many ways than C++. It says so right there in the comparison charts ... <g> 2) C++ had to deal with multiple inheritance ~ I imagine that's a big factor in the above statement. D (and Java) do not. Notice how Java dispensed with the 'alias' notion completely, and did not pay this 'Stroustrup Tax' in any shape or form. 3) Aliasing method names is truly non-intuitive. It stands out like a sore thumb within D as a kludge. It does not matter from whence it came, or what supposed pedigree it has ~ so many other things in D are completely natural. 4) You may find that a required use of "override" would alleviate some of those Stroustrup concerns. 5) You are at least as smart as B.S., and can do much better than this. I can't help but be entirely suspicious about the B.S. Gospel applying in the same manner. I challenge you, Walter, to exceed C++ in this respect also. I further challenge you to provide a reasonable D example showing where this kind of alias thing is actually beneficial to the D programmer. If you cannot do that, then this Stroustrup argument holds no water whatsoever. Since we're all required to spend a bunch of time whittling down code in bug reports for you, I think it's only fair you do the same in return with respect to issues like this. I mean, if the value of alias is so obvious, how much effort would a few good examples really take? Regards; - Kris

Jul 26 2004
prev sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Walter wrote:
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:cdu9ov$162i$1 digitaldaemon.com...
 
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.

Stroustrup has laid them out in chapter 13.1 of the "The Annotated C++ Reference Manual." The general rule for overloading is that a name is looked up first, and then overload resolution is applied. Stroustrup rejects the idea of ignoring scope issues when doing overload resolution. He argues that it would be surprising in a deeply nested heirarchy of class derivation, that a user of a derived class would have to know too much detail about a base class that may be hidden. Also, he shows how surprising errors can creep in, such as if a function in base class B copies only the B part of an object, but was inadvertantly called with an instance of D that didn't override each of those B functions properly.

Sorry, but that example has little to do with overloading, but very much to do with overriding. The B and D example shows: A superclass can have a method that cause problems for subclasses if it isn't overridden. Consequence: If this problematic situation should be solvable by the language, then a keyword meaning 'must be overridden' should be included. <code> mustoverride foo() {} </code> would mean that subclasses down through the hierarchy *must* override, in some cases doing only super.foo(); Another solution is to have override in the most super class of them all mean that they must be overridden in subclasses, while override in subclasses means what it do today. What the B and D example don't show: The public methods of a superclass should be hidden from a subclass' interface, if and only if the subclass have methods with the same name as those in the superclass, regardless of their signature. Consequence: Methods are hidden in some cases (those where overloading is involved), even though the normal case is that you don't want to hide. Using alias to unhide is weird. The word alias imply that the real name is hidden, but here it works the other way! The normal case is to want the superclass' methods up there in the subclass, but D has turned this upside down. Conclusion: Argumentation for why it should work the way it do today is lacking, and the implementation/specification is unintuitive, badly documented and works against most common use. All IMHO. Lars Ivar Igesund
Jul 25 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Lars Ivar Igesund" <larsivar igesund.net> wrote in message
news:ce1ccn$2ru8$1 digitaldaemon.com...
 Argumentation for why it should work the way it do today is lacking, and
 the implementation/specification is unintuitive, badly documented and
 works against most common use.

 All IMHO.

I can concur with "badly documented" <g> but why is the way C++ has worked for 20 years against most common use?
Jul 25 2004
next sibling parent h3r3tic <h3r3tic_member pathlink.com> writes:
I can concur with "badly documented" <g> but why is the way C++ has worked
for 20 years against most common use?

The fact that everyone got used to it doesn't mean it's the best. If people aren't conscious there are other options, they are not looking for them. Maybe that's the case going on here. D is trying to improve on C++, isn't it ? Ok, just give me a good example that either of the options is right, then make it work and I'll shut up.
Jul 25 2004
prev sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Lars made a series of valid points in his three emails Walter; as have many
others. The fact that you choose to divert focus by ignoring his core points
does not serve you well. You have been asked to clarify the /benefits/ of
the current implementation several times now, in various ways, and still you
avoid the issue at hand. One might be given pause to think you're avoiding
this at all costs ...

Please; I ask you once again: show us /all/ why this C++ style method-hiding
is necessary and/or beneficial to D programmers via one or two examples. As
I've already stated, if the benefits are so obvious then it won't take much
effort on your part to do so.

Posting that example of how C++ operates had absolutely zero value: we
already /know/ it works that way ~ that's exactly the point of this issue ~
there's no perceived need for it to be that way in D at all!

If you cannot show any benefits (for D) with some solid examples, then your
position on this becomes seriously invalidated. Period. The whole point of
this is to improve the D language; not to posture and swagger over academic
excrement  ...

- Kris


"Walter" <newshound digitalmars.com> wrote in message
news:ce24ek$b1e$1 digitaldaemon.com...
 "Lars Ivar Igesund" <larsivar igesund.net> wrote in message
 news:ce1ccn$2ru8$1 digitaldaemon.com...
 Argumentation for why it should work the way it do today is lacking, and
 the implementation/specification is unintuitive, badly documented and
 works against most common use.

 All IMHO.

I can concur with "badly documented" <g> but why is the way C++ has worked for 20 years against most common use?

Jul 25 2004
parent reply Farmer <itsFarmer. freenet.de> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in
news:ce26ue$cm1$1 digitaldaemon.com: 

 Lars made a series of valid points in his three emails Walter; as have
 many others. The fact that you choose to divert focus by ignoring his
 core points does not serve you well. You have been asked to clarify the
 /benefits/ of the current implementation several times now, in various
 ways, and still you avoid the issue at hand. One might be given pause to
 think you're avoiding this at all costs ...

I don't get it. Why should the *benefits* be clarified, as long as the disadvantages remain unclear? Remember the old rule, if it's not broken, don't fix it.
 
 Please; I ask you once again: show us /all/ why this C++ style
 method-hiding is necessary and/or beneficial to D programmers via one or
 two examples. As I've already stated, if the benefits are so obvious
 then it won't take much effort on your part to do so.

Consider that the benefits could be *hidden*, so there might be no easy way to that. But I think, that Walter, already did that quite well. Why should D take the look-up rules of a legacy language called Java? Isn't it better to stick with the way modern languages like C++ and C# do it?
 Posting that example of how C++ operates had absolutely zero value: we
 already /know/ it works that way ~ that's exactly the point of this
 issue ~ there's no perceived need for it to be that way in D at all!

Disagree. It helped me better understand the D language and restrengthen my fading C++ skills. Futhermore some of those cool things you can do with C++ sprung back to my mind: class SomeClass : Base { alias Base.this this; // inherit *all* constructors } [I don't expect that this compiles with DMD, but the D language should allow it.] How do you do that the 'Java way'? Now some comments regarding your document (revision 4): Chapter External Names: Why is that chapter even still listed? Chapter Satisfying Interface Contracts: What's wrong with class Foo : IFoo { void foo() {} } class Bar : Foo, IBar { void bar() {} } And why do present the other solutions first, that are not really solutions, since why are *different* from the original example? <quote> Now your class design is explicitly bound by the design of the contract. In other words, the implementation is tightly bound to the layout of the contract. Given that the contract is supposed to be independent from the implementation, this is a truly sad place to be. <quote/> Why do you think that the *design* of the contract is of any importance? Generally, the *content* of the contract is of importance but not its design. I certainly could implement a class BarImpl2 in any way I could dream of, that fullfill's contract IBar and IFoo. Where's the catch, here? Chapter The Alias peek-a-boo Game: <quote> Hereís why: If one wishes to limit the exposure of class methods, one uses an Interface to represent only that portion of the concrete class that defines the external contract. <quote> And how do I get an interface of the hidden class instance? I would have to use factories, baggage, bloat, argh! <quote> Letís recap some basic OO assumptions: 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. <quote> Personally, my OO assumption goes like this: A subclass must provide all the methods of its super-classes. As matter of *convenience* compilers implicitely take the method from the super-class to the subclass. (Except for some cases, e.g. constructors) But my point of view is rather non-intuitive, (mostly) inappropriate, and certainly does not belongs to commonly accepted OO practices. Farmer.
Jul 26 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
<sigh>  Inline Farmer:

"Farmer"  wrote...
 I don't get it.
 Why should the *benefits* be clarified, as long as the disadvantages

 unclear? Remember the old rule, if it's not broken, don't fix it.

You are welcome to your opinion, just as I am. However, you discredit the opinion of several others who have also stated their case regarding the disadvantages.
 Consider that the benefits could be *hidden*, so there might be no easy

 to that. But I think, that Walter, already did that quite well.
 Why should D take the look-up rules of a legacy language called Java?
 Isn't it better to stick with the way modern languages like C++ and C# do

I'm tempted to giggle ... but I won't.
 Disagree. It helped me better understand the D language and restrengthen

 fading C++ skills. Futhermore some of those cool things you can do with

 sprung back to my mind:

Glad to hear it! My comments are all made in context Farmer: you're simply splitting hairs.
 class SomeClass : Base
 {
      alias Base.this this;  // inherit *all* constructors
 }
 [I don't expect that this compiles with DMD, but the D language should

 it.]

 How do you do that the 'Java way'?

Finally; this has some merit. Shame it's illegal syntax in D. What you fail to grasp, Farmer, is that I don't have a problem with alias as a concept at all. I've just documented a case where I feel it's required use is totally non-intuitive.
 Now some comments regarding your document (revision 4):

 Chapter External Names:

 Why is that chapter even still listed?

Apparently, it must be for no reason other than to annoy you, Farmer. Don't you think it has some "educational" value? Just as example above had for you? Wouldn't you rather see the entire document simply disappear instead?
 Chapter Satisfying Interface Contracts:

 What's wrong with

 class Foo : IFoo
 {
    void foo()
    {}
 }

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

It works great for small examples. You're simply repeating what I've already noted.
 And why do present the other solutions first, that are not really

 since why are *different* from the original example?

Forgive me; my parser just failed.
 Why do you think that the *design* of the contract is of any importance?
 Generally, the *content* of the contract is of importance but not its

 I certainly could implement a class BarImpl2 in any way I could dream of,
 that fullfill's contract IBar and IFoo. Where's the catch, here?

You're absolutely right about the contract design. It should have no bearing whatsoever upon the class layout. This is exactly where the problem lies. As it is, If I construct a contract from several others, the class layout has to follow along in sych (as in the above example), unless you also provide a ream of stub & dispatch methods.
 Personally, my OO assumption goes like this: A subclass must provide all

 methods of its super-classes. As matter of *convenience* compilers
 implicitely take the method from the super-class to the subclass. (Except

 some cases, e.g. constructors)
 But my point of view is rather non-intuitive, (mostly) inappropriate, and
 certainly does not belongs to commonly accepted OO practices.

Your just splitting hairs dude. I agree with your more pedantic version, but this has nothing to do with the issue. The fact is that the D compiler does not implicitly do what you state.
Jul 26 2004
parent Farmer <itsFarmer. freenet.de> writes:
Kris, I see I can follow your thoughts, and you can follow mine:

We both agree that the overloading-rules are non-intuitive (I never
admitted this before, but I do agree). We both agree that the 'alias hack'
solves your problems, but it's non-intuitive, inconvenient, and against
accepted OO-principles. 

We both agree, that the way, D currently supports interfaces, you *can* gain 
the basic (important) benefits of interfaces. although, it's non-intuitive, 
inconvenient, and limiting. 

What's different are not our *thoughts* (they are pretty much in line),
but our conclusions. <em> You repeately argued that things are bullshit,
since they are not intuitive. </em>
Here my fundamental opinion is apparently different from yours. When I think 
of programming matters (language, libraries, methods), it is irrelevant 
whether sth. is intuitive or not. It is far more important, that things are 
consistent and logical.
I prefer simple rules, that can be learnt and understood rather than
intuitive rules or rules that are tweaked to archive intuitive results. I
think that intuitive things are easy grasp, at first, but in the long run
they are impossible to master.  But the worst thing about intuitive designs 
are that they are context sensitive. What's intuitive depends on your 
background, on what you are currently working, and on personal preference. 

Since I have so strong feelings about the uselessness of intuitive design
[Not long, when I read about Ruby that its designer made the language as
intuitive as he could do. My intuition said 'Bullshit, don't bother to
look at this.'], I fail to understand your paper, which appeals more to
the reader's intuition than providing well presented trains of thoughts. 


I don't think its much of relevance, but my inituition share's you concerns
that D's rules for implementation of interfaces, will/should be improved.
I'm especialy concerned regarding templates (as Andy Friesen pointed out)
and big messy class inheritance tries build by (even bigger an messier)
programming teams. Regarding the 'alias peek a boo' game, I only comment that 
in the past, Walter had constantly sticked with 'C++' concepts,  even when 
these concepts were undeniably more flawed, than the 'alias peek a boo' game. 


Farmer.


 Disagree. It helped me better understand the D language and
 restrengthen 

 fading C++ skills. Futhermore some of those cool things you can do with

 sprung back to my mind:

Glad to hear it! My comments are all made in context Farmer: you're simply splitting hairs.

I'm splitting hairs for good reasons: I consider your comment <quote> Posting that example of how C++ operates had absolutely zero value: we already /know/ it works that way ~ [...] </quote> as rude and unjustified: Not everyone has your skills and background to properly evaluate the 'alias- peek-a-boo game'. Walter is doing good in *repeatedly* explaining and clarifying the rules of D. (Especially if the description of these rules are so badly lacking in the specification ;-)
 Now some comments regarding your document (revision 4):

 Chapter External Names:

 Why is that chapter even still listed?

Apparently, it must be for no reason other than to annoy you, Farmer. Don't you think it has some "educational" value? Just as example above had for you? Wouldn't you rather see the entire document simply disappear instead?

No, D needs critique papers to evolve and get rid of it's flaws. But why is there? (well, annoying me is for sure a good reason) Walter has already admitted that the presented problems are due to compiler- bugs. Would you also leave functions originally written to work-around some OS bugs, in code - or would you clean it up, once the issues were solved?
 And why do present the other solutions first, that are not really

 since why are *different* from the original example?

Forgive me; my parser just failed.

I can fix that: And why do <you> present the other solutions first, that are not really solutions, since <they> are *different* from the original example? And to make sure it gets understood in the semantic phase ;-) Just one comment: Though, I'm used to see deliberate manipulations of the uninformed reader in sneaky marketing papers, I still consider it bad and inappropriate style.
Jul 27 2004
prev sibling next sibling parent Lars Ivar Igesund <larsivar igesund.net> writes:
Farmer wrote:

 Why should D take the look-up rules of a legacy language called Java?
 Isn't it better to stick with the way modern languages like C++ and C# do it? 

Heh, funny.
Jul 27 2004
prev sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <Xns95337DF59537itsFarmer 63.105.9.61>, Farmer says...

Why should D take the look-up rules of a legacy language called Java?
Isn't it better to stick with the way modern languages like C++ and C# do it? 

Not wishing to be /too/ pedantic, but C++ came before Java. So by that reckoning, C++ is "legacy" and Java is "modern". Jill
Jul 26 2004
parent Farmer <itsFarmer. freenet.de> writes:
Arcane Jill <Arcane_member pathlink.com> wrote in
news:ce4ucm$1sqm$1 digitaldaemon.com: 

 In article <Xns95337DF59537itsFarmer 63.105.9.61>, Farmer says...
 
Why should D take the look-up rules of a legacy language called Java?
Isn't it better to stick with the way modern languages like C++ and C#
do it? 

Not wishing to be /too/ pedantic, but C++ came before Java. So by that reckoning, C++ is "legacy" and Java is "modern". Jill

should read: Forget about the rules of the archaic C++ language and the legacy language Java. Better stick with modern languages: Smalltalk. :-) Farmer.
Jul 27 2004
prev sibling next sibling parent reply Sha Chancellor <schancel pacific.net> writes:
In article <cdt9m6$puh$1 digitaldaemon.com>,
 "Walter" <newshound digitalmars.com> wrote:

 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>.

I think his point has some validity to it. Importing should have a method by which to NOT implicitly be included in the current namespace as well as the module namespace. At least by my understanding of D imports he cannot do: std.c.linux.linux.close(...) without first importing. And by importing, he automatically gets close() in the current namespace. If there is already a way to "include" the module without "importing" the namespace then his point is moot. Otherwise I think there's something to this.
Jul 24 2004
parent Sha Chancellor <schancel pacific.net> writes:
In article <schancel-23ED78.09265224072004 digitalmars.com>,
 Sha Chancellor <schancel pacific.net> wrote:

 I think his point has some validity to it.  Importing should have a 
 method by which to NOT implicitly be included in the current namespace 
 as well as the module namespace.   
 
 At least by my understanding of D imports he cannot do: 
 
 std.c.linux.linux.close(...) without first importing. 
 
 And by importing, he automatically gets close() in the current 
 namespace. 
 
 If there is already a way to "include" the module without "importing" 
 the namespace then his point is moot.  Otherwise I think there's 
 something to this.

Pardon my stupidity, I meant to respond to external name resolution.
Jul 24 2004
prev sibling next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
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

 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>.

Jul 24 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdu57d$1474$1 digitaldaemon.com...
 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.

1) I find Stroustrup's argument in the ARM 13.1 (outlined in another message) reasonable. 2) In my experience with C++, this behavior has not caused an outcry and is not listed in any diatribes I've seen as one of the broken features of C++. 3) So I am reluctant to break with the C++ rule on this. The rule to apply is straightforward - look up the name, *then* apply overload resolution.
Jul 24 2004
next sibling parent reply John Reimer <brk_6502 yahoo.com> writes:
On Sat, 24 Jul 2004 22:48:51 -0700, Walter wrote:

 
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:cdu57d$1474$1 digitaldaemon.com...
 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.

1) I find Stroustrup's argument in the ARM 13.1 (outlined in another message) reasonable.

As brilliant as he is, could Stroustrup actually be wrong on the matter? Or to put it another way, D is not C++ so why do we have to follow Stroustrup's conventions within a language Stroustrup did not design?
 2) In my experience with C++, this behavior has not caused an outcry and
 is not listed in any diatribes I've seen as one of the broken features of
 C++.

D is a different language, and this issue is now causing an outcry here that you deny exists elsewhere. The language seems to be different enough (perhaps because of so many features similar to Java) that this is indeed now an issue. The fact that D has different features from C++ makes a solution to these problems important to the language's future (even if they apparently weren't significant in C++). Also C++ has it's own community of personalities. The fact that complaints might be nonexistent in that community doesn't imply that the issue isn't important in the D community. What it comes down to is that it seems you have decided to follow C++ standards either for convenience or preference. I guess that's good to know.
 3) So I am reluctant to break with the C++ rule on this.
 
 The rule to apply is straightforward - look up the name, *then* apply
 overload resolution.

This appears to be a Java verses C++ debate. Does java define name lookup rules differently (someone indicated so in another message, I believe)? If so, why is the C++ way better then the java way? The C++ model shouldn't always be the right one to imitate, should it? As it stands, I think your adoption of C++ name lookup rules into the D language has made it confusing. Is that what happens when you mix C++ and Java ancestry? I don't fully comprehend the nitty gritty details of this, but it makes for interesting debate! I just hope there's a useful resolution to it all. Later, John
Jul 24 2004
next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"John Reimer" <brk_6502 yahoo.com> wrote in message
news:pan.2004.07.25.06.30.45.581020 yahoo.com...
 1) I find Stroustrup's argument in the ARM 13.1 (outlined in another
 message) reasonable.

Or to put it another way, D is not C++ so why do we have to follow Stroustrup's conventions within a language Stroustrup did not design?

Heaven knows I disagree with Dr. Stroustrup on many points. However, this is a case where I agree with him. I am not simply doing it that way because Dr. Stroustrup did it that way.
 2) In my experience with C++, this behavior has not caused an outcry and
 is not listed in any diatribes I've seen as one of the broken features


 C++.

D is a different language, and this issue is now causing an outcry here that you deny exists elsewhere. The language seems to be different enough (perhaps because of so many features similar to Java) that this is indeed now an issue. The fact that D has different features from C++ makes a solution to these problems important to the language's future (even if they apparently weren't significant in C++). Also C++ has it's own community of personalities. The fact that complaints might be nonexistent in that community doesn't imply that the issue isn't important in the D community. What it comes down to is that it seems you have decided to follow C++ standards either for convenience or preference. I guess that's good to know.

I followed this aspect of C++ because I found the argument in favor of it compelling, and since D is a refactoring of C++ and not Java, it gives greater weight to C++ behavior over Java behavior.
 The rule to apply is straightforward - look up the name, *then* apply
 overload resolution.

rules differently (someone indicated so in another message, I believe)? If so, why is the C++ way better then the java way? The C++ model shouldn't always be the right one to imitate, should it?

Java explicitly differs from C++ in this regard. Kris is arguing for Java lookup/overriding rules. I see no reason that Java should be always be the right one to imitate, either <g>.
Jul 25 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Walter"  wrote ...
 Java explicitly differs from C++ in this regard. Kris is arguing for Java
 lookup/overriding rules. I see no reason that Java should be always be the
 right one to imitate, either <g>.

I am, huh? I thought I was advocating logical progression and continuity of behavior. This is /not/ something the D compiler currently has with respect to name resolution. Far from it actually ... what about imitating ease-of-use and common sense, Walter? <g> I really think you need to show us some good code examples of why this is so much to our benefit, and not a maligned detraction instead.
Jul 25 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <cdvrf1$1unf$1 digitaldaemon.com>, Kris says...
"Walter"  wrote ...
 Java explicitly differs from C++ in this regard. Kris is arguing for Java
 lookup/overriding rules. I see no reason that Java should be always be the
 right one to imitate, either <g>.

I am, huh? I thought I was advocating logical progression and continuity of behavior. This is /not/ something the D compiler currently has with respect to name resolution. Far from it actually ... what about imitating ease-of-use and common sense, Walter? <g>

This discussion would probably be better continued once the associated compiler bugs have been fixed. Without the ability to offer practical working examples of how the existing scheme does not work, I think there's the potential for a bit of confusion over specifics. Sean
Jul 25 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
That would be a fair point Sean, but the issue is not about the bugs
surrounding alias; it's about the necessity of its arcane and non-intuitive
use. My document deliberately avoided pointing out the bugs still there, to
avoid exactly this kind of confusion. It doesn't change the basic premise
... <g>

I'm still waiting for Walter (or anyone) to provide a good code example or
two that shows exactly why D needs this. Without that, the vague argument
about what B.S. believes holds no water whatsoever with respect to D -- in
other words, there is /still/ no solid counter-point to examine. It's in all
our interests to get this resolved properly ~ so bring on the examples I
say!

:-)


"Sean Kelly" <sean f4.ca> wrote in message
news:ce0s5l$2gne$1 digitaldaemon.com...
 In article <cdvrf1$1unf$1 digitaldaemon.com>, Kris says...
"Walter"  wrote ...
 Java explicitly differs from C++ in this regard. Kris is arguing for



 lookup/overriding rules. I see no reason that Java should be always be



 right one to imitate, either <g>.

I am, huh? I thought I was advocating logical progression and continuity


behavior. This is /not/ something the D compiler currently has with


to name resolution. Far from it actually ... what about imitating
ease-of-use and common sense, Walter? <g>

This discussion would probably be better continued once the associated

 bugs have been fixed.  Without the ability to offer practical working

 of how the existing scheme does not work, I think there's the potential

 bit of confusion over specifics.

 Sean

Jul 25 2004
prev sibling next sibling parent Lars Ivar Igesund <larsivar igesund.net> writes:
Walter wrote:

 I followed this aspect of C++ because I found the argument in favor of it
 compelling, and since D is a refactoring of C++ and not Java, it gives
 greater weight to C++ behavior over Java behavior.

 Java explicitly differs from C++ in this regard. Kris is arguing for Java
 lookup/overriding rules. I see no reason that Java should be always be the
 right one to imitate, either <g>.

IMHO, C++ is *not* the language you want to imitate when it comes to OOP issues. Java and Eiffel are better suited; Java because the single inheritance/interface model is used by D, Eiffel because it seems to work even if everything is possible. Lars Ivar Igesund
Jul 25 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Sun, 25 Jul 2004 00:06:49 -0700, Walter <newshound digitalmars.com> 
wrote:

 "John Reimer" <brk_6502 yahoo.com> wrote in message
 news:pan.2004.07.25.06.30.45.581020 yahoo.com...
 1) I find Stroustrup's argument in the ARM 13.1 (outlined in another
 message) reasonable.

Or to put it another way, D is not C++ so why do we have to follow Stroustrup's conventions within a language Stroustrup did not design?

Heaven knows I disagree with Dr. Stroustrup on many points. However, this is a case where I agree with him. I am not simply doing it that way because Dr. Stroustrup did it that way.
 2) In my experience with C++, this behavior has not caused an outcry 

 is not listed in any diatribes I've seen as one of the broken features


 C++.

D is a different language, and this issue is now causing an outcry here that you deny exists elsewhere. The language seems to be different enough (perhaps because of so many features similar to Java) that this is indeed now an issue. The fact that D has different features from C++ makes a solution to these problems important to the language's future (even if they apparently weren't significant in C++). Also C++ has it's own community of personalities. The fact that complaints might be nonexistent in that community doesn't imply that the issue isn't important in the D community. What it comes down to is that it seems you have decided to follow C++ standards either for convenience or preference. I guess that's good to know.

I followed this aspect of C++ because I found the argument in favor of it compelling, and since D is a refactoring of C++ and not Java, it gives greater weight to C++ behavior over Java behavior.
 The rule to apply is straightforward - look up the name, *then* apply
 overload resolution.

lookup rules differently (someone indicated so in another message, I believe)? If so, why is the C++ way better then the java way? The C++ model shouldn't always be the right one to imitate, should it?

Java explicitly differs from C++ in this regard. Kris is arguing for Java lookup/overriding rules. I see no reason that Java should be always be the right one to imitate, either <g>.

You should choose the one that best suits D. It appears you believe you have done that. It appears Kris disagrees. So, I am on the fence until I have seen: - a good outline of both the C++ and Java name resolution methods. - some D code examples where one or the other is better/easier/more intuitive. I personally find having to use 'alias' counter intuitive. My background is one in C (the last 6 years of my life), not C++ or Java. I have used Java a tiny bit, and C++ perhaps 10x more than that. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Jul 25 2004
prev sibling parent reply Sha Chancellor <schancel pacific.net> writes:
In article <pan.2004.07.25.06.30.45.581020 yahoo.com>,
 John Reimer <brk_6502 yahoo.com> wrote:

 On Sat, 24 Jul 2004 22:48:51 -0700, Walter wrote:
 
 
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:cdu57d$1474$1 digitaldaemon.com...
 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.

1) I find Stroustrup's argument in the ARM 13.1 (outlined in another message) reasonable.

As brilliant as he is, could Stroustrup actually be wrong on the matter? Or to put it another way, D is not C++ so why do we have to follow Stroustrup's conventions within a language Stroustrup did not design?
 2) In my experience with C++, this behavior has not caused an outcry and
 is not listed in any diatribes I've seen as one of the broken features of
 C++.

D is a different language, and this issue is now causing an outcry here that you deny exists elsewhere. The language seems to be different enough (perhaps because of so many features similar to Java) that this is indeed now an issue. The fact that D has different features from C++ makes a solution to these problems important to the language's future (even if they apparently weren't significant in C++). Also C++ has it's own community of personalities. The fact that complaints might be nonexistent in that community doesn't imply that the issue isn't important in the D community. What it comes down to is that it seems you have decided to follow C++ standards either for convenience or preference. I guess that's good to know.
 3) So I am reluctant to break with the C++ rule on this.
 
 The rule to apply is straightforward - look up the name, *then* apply
 overload resolution.

This appears to be a Java verses C++ debate. Does java define name lookup rules differently (someone indicated so in another message, I believe)? If so, why is the C++ way better then the java way? The C++ model shouldn't always be the right one to imitate, should it? As it stands, I think your adoption of C++ name lookup rules into the D language has made it confusing. Is that what happens when you mix C++ and Java ancestry? I don't fully comprehend the nitty gritty details of this, but it makes for interesting debate! I just hope there's a useful resolution to it all. Later, John

If the alias thing is a bug, I disagree on all counts. I like the option of not including the overloaded members which I didn't overload. There may be cases where a child class does not want those to be inherited. Which is allowed by the current method. If it becomes the default behavior to inherit them, you'd need to have some kind of convention to get RID of them.
Jul 25 2004
parent "Walter" <newshound digitalmars.com> writes:
"Sha Chancellor" <schancel pacific.net> wrote in message
news:schancel-512344.09114325072004 digitalmars.com...
 If the alias thing is a bug, I disagree on all counts.  I like the
 option of not including the overloaded members which I didn't overload.
 There may be cases where a child class does not want those to be
 inherited.   Which is allowed by the current method. If it becomes the
 default behavior to inherit them, you'd need to have some kind of
 convention to get RID of them.

That is another way to look at it. It can be a problem if one has an arbitrarilly deep inheritance heirarchy, and there's an overlooked (!) overload in there. The alias can also be used to 'opt-in' only the functions from a particular subclass in the inheritance heirarchy, not all of them. The 'alias' idiom for doing an 'opt-in' can be thought of another way - by creating an alias for a symbol in the current scope, doesn't it make sense that it then enables overloading between the scopes on that name? Note that this is the same behavior when one creates an alias for other function names, even names in other imports, etc. It's a powerful thing, but it shouldn't be on by default.
Jul 25 2004
prev sibling next sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Walter wrote:
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:cdu57d$1474$1 digitaldaemon.com...
 
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.

1) I find Stroustrup's argument in the ARM 13.1 (outlined in another message) reasonable. 2) In my experience with C++, this behavior has not caused an outcry and is not listed in any diatribes I've seen as one of the broken features of C++. 3) So I am reluctant to break with the C++ rule on this. The rule to apply is straightforward - look up the name, *then* apply overload resolution.

The rule would still be straightforward if it was: look up the name, apply overload resolution if overload resolution fails; repeat above for superclass Lars Ivar Igesund
Jul 25 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Lars Ivar Igesund" <larsivar igesund.net> wrote in message
news:ce1bpg$2rjr$2 digitaldaemon.com...
 Walter wrote:
 The rule to apply is straightforward - look up the name, *then* apply
 overload resolution.

The rule would still be straightforward if it was: look up the name, apply overload resolution if overload resolution fails; repeat above for superclass

That's a good thought, but overload resolution is not pass/fail, it has multiple levels of matching. You'll have to decide if a worse match in the current scope is better or worse than a better match in another scope. It'll be an arbitrary rule, one more bit of arcana needed to program in D.
Jul 25 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
What you specifically avoid in your reply, Walter, is that an EXACT (yes,
that is a shout) match may be in the very next scope. Additionally, the
rules involved are no different that those applied for the current scope;
there's simply more signatures to choose from. You really appear to be
deliberately obfuscating the issue ... why?

- Kris


"Walter" <newshound digitalmars.com> wrote in message
news:ce2676$c4d$1 digitaldaemon.com...
 "Lars Ivar Igesund" <larsivar igesund.net> wrote in message
 news:ce1bpg$2rjr$2 digitaldaemon.com...
 Walter wrote:
 The rule to apply is straightforward - look up the name, *then* apply
 overload resolution.

The rule would still be straightforward if it was: look up the name, apply overload resolution if overload resolution fails; repeat above for superclass

That's a good thought, but overload resolution is not pass/fail, it has multiple levels of matching. You'll have to decide if a worse match in the current scope is better or worse than a better match in another scope.

 be an arbitrary rule, one more bit of arcana needed to program in D.

Jul 25 2004
prev sibling parent reply parabolis <parabolis softhome.net> writes:
Walter wrote:

 
 The rule to apply is straightforward - look up the name, *then* apply
 overload resolution.
 

I have read what /feels/ like thousands of posts in which you repeated that phrase. I am reluctant to add even another drop to this river but sadly it looks like I will be contributing. In the context of resolving names within a nested scope the rule is simple and straightforward: 1) Attempt overload resolution in the most specific scope first and if that fails then try the next more general scope... rinse... repeat. How could it be simpler? You have said elsewhere that you are concerned making exceptions to this rule will result in an arcane language with arcane rules. That is a valid fear. The very people asking for an exception are motivated by exactly the same fear. However they want to see this rule applied: 2) A subclass inherits everything from its base class. (emphasis on the period.) Surely you can understand that the following seems arcane: 2) A subclass inherits everything from its base class, 2a) except for the purpose of satisfying an interface and 2b) except when overload resolution is satisfied by something in the subclass 2bi) unless that something is 'alias'ed to bring a base class' name into scope. Now consider the suggested amended 1): 1) Attempt overload resolution in the most specific scope first and if that fails then try the next more general scope... rinse... repeat 1a) except when finding inherited functions which should first be attempted without overload resolution. It seems obvious to me that either the scope resolution rules or the inheritance rules are going to get more complicated. Unless someone else can find a less complicated way to amend 2) I do not believe it is wise to make inheritance overly complicated to keep scope resolution simple. Of course this tendency is also influenced by the fact that I believe inheritance is more important than scope rules simply because it has more intuitive oomph which is really what makes the OO paradigm so useful. It feels like the scope rules are more helpful to people from a C++ background than the general population who can inuit OO's inheritance rather easily. That is why I believe you have had to explain the simple scope rules over and over in the thread...
Jul 28 2004
parent reply Sean Kelly <sean f4.ca> writes:
parabolis wrote:
 
 It feels like the scope rules are more helpful to people from a C++ 
 background than the general population who can inuit OO's inheritance 
 rather easily. That is why I believe you have had to explain the simple 
 scope rules over and over in the thread...

They are. But as I pointed out in another post, D is different from C++ in that D method functions are virtual by default. For this reason I think your argument is worth considering. In C++ ther rules make sense because all default behavior is static. In D the rules make less sense because most default behavior is dynamic. The presence of interfaces and the lack of multiple inheritance just strengthens the argument in my mind. Though I think it is important to note that the override issue that Regan has mentioned should be considered separately from this one. Sean
Jul 27 2004
parent parabolis <parabolis softhome.net> writes:
Sean Kelly wrote:

 parabolis wrote:
 
 It feels like the scope rules are more helpful to people from a C++ 
 background than the general population who can inuit OO's inheritance 
 rather easily. That is why I believe you have had to explain the 
 simple scope rules over and over in the thread...

They are. But as I pointed out in another post, D is different from C++ in that D method functions are virtual by default. For this reason I think your argument is worth considering. In C++ ther rules make sense because all default behavior is static. In D the rules make less sense because most default behavior is dynamic. The presence of interfaces and the lack of multiple inheritance just strengthens the argument in my

That is good to hear. I am by no means an avid C++ programmer mostly because I have not internalized what other C++ programmers have internalized and so I am rather cautious about advocating a suggestion for D that would be violently counter-intuitive to C++ programmers.
 mind.  Though I think it is important to note that the override issue 
 that Regan has mentioned should be considered separately from this one.

Yes the two are seperate and it was probably bad form to include the interface issue in a post that deals mostly with the seperate issue of overriding. (The post was actually about overriding and not interfaces) However I felt I had to include the interface issue to illustrate the ways in which D is already tending towards arcane as far exceptions to the inheritance go.
Jul 28 2004
prev sibling next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
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"
Jul 24 2004
next sibling parent Sean Kelly <sean f4.ca> writes:
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
Jul 24 2004
prev sibling next sibling parent J C Calvarese <jcc7 cox.net> writes:
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/
Jul 24 2004
prev sibling next sibling parent reply Derek <derek psyc.ward> writes:
On Sat, 24 Jul 2004 10:17:01 -0700, 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"

This is just a bug. If the sub-class has a method with the same signature as the *first* method in the super class and the alias appears before the methods, then the alias will generate an error message. Try swapping the methods in the Writer class. Message goes away. Change the signature in the sub-class to (int x). Message goes away. Move the alias to be after the method in the sub-class. Message goes away. This is just a bug. -- Derek Melbourne, Australia
Jul 24 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
I don't think anyone suggested that particular one was anything else, Derek.
It's broken. Full Stop. With luck it'll get fixed.

If you are feeling some animosity towards me, then please email directly
outlining your issue. And thanks for not shouting this time!

- Kris


"Derek" <derek psyc.ward> wrote in message
news:i546196c2uti$.1stk2dxkftgx4.dlg 40tude.net...
 On Sat, 24 Jul 2004 10:17:01 -0700, 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"

This is just a bug. If the sub-class has a method with the same signature as the *first* method in the super class and the alias appears before the methods, then the alias will generate an error message. Try swapping the methods in the Writer class. Message goes away. Change the signature in the sub-class to (int x). Message goes away. Move the alias to be after the method in the sub-class. Message goes away. This is just a bug. -- Derek Melbourne, Australia

Jul 24 2004
parent Derek <derek psyc.ward> writes:
On Sat, 24 Jul 2004 17:47:19 -0700, Kris wrote:

 I don't think anyone suggested that particular one was anything else, Derek.
 It's broken. Full Stop. With luck it'll get fixed.

Sorry, it seemed to me that you were implying that this current behaviour is designed that way.
 If you are feeling some animosity towards me, then please email directly
 outlining your issue. And thanks for not shouting this time!

What????? "animosity" ... "shouting" ... I don't get it. Why do you think that I'm angry or being nasty. Sorry if I come across that way, but it isn't (isnt/aint/isnot) so. -- Derek Melbourne, Australia
Jul 24 2004
prev sibling parent "Walter" <newshound digitalmars.com> writes:
And that is a compiler bug!

"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdu5f0$14dm$1 digitaldaemon.com...
 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"

Jul 24 2004
prev sibling parent reply parabolis <parabolis softhome.net> writes:
Walter wrote:

 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:
 
 --------------------------------------

 -------------------------------------------
 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>.
 

I believe this example is misleading. The name resolution is important in C++ only because struct B might inherit from a second struct, say Y, which also implements 'int foo(int i)'. C++ cannot (and would not) be critiqued because it must resolve against multiple super classes. Using C++ rules designed to select from a multiple inheritance hierarchy in D does not make sense. I do not believe it is possible to justify hiding anything in a single inheritance language.
Jul 25 2004
parent "Walter" <newshound digitalmars.com> writes:
"parabolis" <parabolis softhome.net> wrote in message
news:ce12bi$2l2r$1 digitaldaemon.com...
 Walter wrote:

 You describe it as "The latter is utter nonsense, and smacks of either


 implementation-specific hack or a half-baked and headless C-style
 implementation of method hiding." However, consider the following C++
 program:

 --------------------------------------

 -------------------------------------------
 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


 lot of critiques of C++, and this has not been mentioned. Or perhaps my


 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>.

I believe this example is misleading. The name resolution is important in C++ only because struct B might inherit from a second struct, say Y, which also implements 'int foo(int i)'. C++ cannot (and would not) be critiqued because it must resolve against multiple super classes. Using C++ rules designed to select from a multiple inheritance hierarchy in D does not make sense. I do not believe it is possible to justify hiding anything in a single inheritance language.

C++ had this behavior from the beginning, long before it had multiple inheritance. (Although the 'using' declaration came years after MI was implemented.)
Jul 25 2004
prev sibling next sibling parent reply "Vathix" <vathixSpamFix dprogramming.com> writes:
"some idiot" <fu bar.org> wrote in message
news:cdsd5t$u5$1 digitaldaemon.com...
 Attached is a small pdf noting three outstanding issues ~ each of which

 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.

 Such things might simply fall by the wayside otherwise.

External Names: I can't seem to recreate that problem, but I have some thoughts on it. I think importing in a class is like importing in a module. If there is a symbol collision, you have to specify from which scope you want the symbol. What comes to mind is this, which does not work: FileConduit fc = new FileConduit; fc.FileConduit.read("foo"); Although, I think if you don't have access (it's private, etc) to something it should not cause a collision. I've been meaning to make a post on that. A working solution is to wrap your imports in a struct or something, and instead of std.linux.linux.read(), simply use mystruct.read(). Satisfying Interface Contracts: From class.html#interface it says "A reimplemented interface must implement all the interface functions, it does not inherit them from a super class." I don't see why you're specifying to (re)implement IFoo without reimplementing it. You can't specify a base class twice, why are you doing it for an interface? It just so happens that specifying to implement an interface has an added feature for reimplementing. In fact, when I tried it with a class, the compiler crashed! The Alias peek-a-boo Game: I remember reading that it helps prevent the possibility of accidentally calling the wrong function. Actually, I think there was a big long thread on it, and I think people said they'd rather opt-into this danger using an alias. However, there is a problem when trying to both override and alias overload; I've been meaning to bug post it (wow I'm lazy ;).
Jul 24 2004
next sibling parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Vathix"  wrote
 Satisfying Interface Contracts:
 From class.html#interface it says "A reimplemented interface must

 all the interface functions, it does not inherit them from a super class."

 don't see why you're specifying to (re)implement IFoo without

 it. You can't specify a base class twice, why are you doing it for an
 interface? It just so happens that specifying to implement an interface

 an added feature for reimplementing. In fact, when I tried it with a

 the compiler crashed!

Sorry ... there was a typo in the example. Fixed in the revision.
 The Alias peek-a-boo Game:
 I remember reading that it helps prevent the possibility of accidentally
 calling the wrong function. Actually, I think there was a big long thread

 it, and I think people said they'd rather opt-into this danger using an
 alias. However, there is a problem when trying to both override and alias
 overload; I've been meaning to bug post it (wow I'm lazy ;).

This is just the kind of sneaky issue that manifests once the compiler starts to special-case certain conditions. My position is that the compiler should treat inherited methods in effectively the same manner as if they were all declared within the subclass itself (sans override for the moment). It's simpler for the compiler and simpler for the user. If this were the case, you would not have the problem you ran into <g>
Jul 24 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Vathix" <vathixSpamFix dprogramming.com> wrote in message
news:cdta9c$q1c$1 digitaldaemon.com...
 Satisfying Interface Contracts:
 From class.html#interface it says "A reimplemented interface must

 all the interface functions, it does not inherit them from a super class."

 don't see why you're specifying to (re)implement IFoo without

 it. You can't specify a base class twice, why are you doing it for an
 interface? It just so happens that specifying to implement an interface

 an added feature for reimplementing. In fact, when I tried it with a

 the compiler crashed!

I hope you send me the example that caused a crash!
Jul 24 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
I see some things that I'd like to clarify and expand upon, if I may:

1)

These three related issues have somehow turned into a vague Java vs C++
debate. That is not what I had in mind at all. Rather, these issues identify
"special case" situations where things don't happen as one might expect.
That, of course, is based upon the assumption that you'd expect a sub-class
to inherit all its super-class methods (non private). What I'm advocating is
symmetry and consistency ~ inversely: a lack of special cases. Certainly,
any system of appropriate complexity will tend to exhibit some kind of
special-case scenario, but surely we can agree that those should be
minimized? The document was intended to highlight the area of name
resolution as apparently riddled with special cases, and that something
should be done about it.

2)

What I personally find truly frustrating about the current situation is
this: Walter effectively states "it works for me" and that's that. Doesn't
matter that there could be a far superior mechanism in existence, or that
someone from the NG might be primed with a truly spectacular solution. Such
an approach is often termed "stonewalling".

Yes, some additional tact might not go amiss on my part; yet past experience
on this NG indicates such an approach has little benefit with respect to
addressing issues. One example that immediately leaps to mind is the
Interface debacle at the beginning of April. For those who don't recall,
Walter railed black-against-white that D had all the Interface support one
could ever need, and deflected every argument and position in a manner
similar to recent responses. The "Interface Expose" was subsequently posted
to illustrate just how much bullshit was involved (and I was simply the last
in a long procession of those who battled to get the point across). There
was an explicit note made at the time about the lack of Contract Resolution
Through Inheritence (dated April 9th; 2004. It was termed "manifest" within
that document. Email sent to Walter on April 7th). That particular issue is
simply raising its head once more.

This is why the current document "appeared" without prior debate (Walter did
acknowledge a pre-post courtesy copy).

3)

I do not profess to be an expert at anything. Rather, I'm pointing out where
some things in D just don't seem right. Having written a reasonably
substantial body of code in D (35,000 lines), I think I have a feel for
that. The /real/ issue at hand is that of confusion: Three confusing
situations in one single area of the language is surely a problem, and I
really do think something needs to be done about it. Of course, I might be
completely and thoroughly misguided instead.

Thank you;  now, I really must get on with my life.

- Kris
Jul 26 2004
prev sibling next sibling parent reply "Walter" <newshound digitalmars.com> writes:
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.
Jul 24 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"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

 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

 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

 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.
Jul 24 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdu94m$15t6$1 digitaldaemon.com...
 That's wonderful! However, you avoided a significant attribute of the

 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

 the other signatures. This seems like a half-way house, but I can

 /imagine/ why your compiler might choose not to do that.

It's following the rule that name lookup happens first, *then* overload resolution. Signatures only have relevance to overload resolution, and no relevance to name lookup. I worry that if I start breaking this rule for special cases here and there, the resulting confusion would be far, far worse, and would relegate D to an "experts only" language. I think we're served better by simple, universally applied rules even if we're not happy with the result in some odd cases.
 Perhaps the most serious problem is that said issue does not manifest

 FileConduit itself. It's only when a call is placed (from some other

 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.

By importing a bunch of names into a scope, well, those names get found in that scope. Put the import outside of that scope!
 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

 the class-scope just so we can call certain methods therein. Do you see

 conflict here?

The later DMD versions do a better job of handling forward references. If I have specific examples of where that still fails, I can work on that. Putting the imports in the class scope is, I believe, the wrong solution, and even worse would be trying to hammer it to fit.
 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.

What I interpret is going on here is that imports inside classes are attempts at working around a forward reference problem. The solution is to get at the forward reference problem.
Jul 24 2004
next sibling parent John Reimer <brk_6502 yahoo.com> writes:
 The later DMD versions do a better job of handling forward references. If
 I have specific examples of where that still fails, I can work on that.
 Putting the imports in the class scope is, I believe, the wrong solution,
 and even worse would be trying to hammer it to fit.
 
 
 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.

What I interpret is going on here is that imports inside classes are attempts at working around a forward reference problem. The solution is to get at the forward reference problem.

I'm satisfied with this solution. As long as forward reference issues can be settled, I'll have no problem putting imports external to classes (that's where I figure they should be anyway). But fixing those forward reference errors will be a long, difficult road anyway because the complicated problems typically don't melt down to the simple examples you so admire. I still don't like the way an import can override the way it does, but it obviously becomes a non-issue at this point if the necessity of the internal import is taken away.
Jul 24 2004
prev sibling next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
If you can't fix imports to act as though outer-scope methods have somehow
been "aliased into scope", then I humbly suggest you make it illegal to
allow an import within the class scope.

I do that all the time for one good reason: all version(linux) and
version(Win32) stuff should be isolated together wherever possible. This
means I deliberately place imports inside class boundaries, because it is
the most maintainable way to set things up (you don't have to go grubbing
around trying to find/edit some reference elsewhere). If you make inner
imports illegal, then you'll ultimately make the code less maintainable. But
even I would grudgingly admit that's better than the obnoxious behavior
currently
exposed.

Of course, one or the other really has to be remedied: forward references
or, uhhhh, smarter name resolution ... current versions of  DMD do not cut
it for the former or the latter.

- Kris


"Walter" <newshound digitalmars.com> wrote in message
news:cdvimn$1o5c$1 digitaldaemon.com...
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:cdu94m$15t6$1 digitaldaemon.com...
 That's wonderful! However, you avoided a significant attribute of the

 in that signature matching is abandoned during the matching process.


 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

 the other signatures. This seems like a half-way house, but I can

 /imagine/ why your compiler might choose not to do that.

It's following the rule that name lookup happens first, *then* overload resolution. Signatures only have relevance to overload resolution, and no relevance to name lookup. I worry that if I start breaking this rule for special cases here and there, the resulting confusion would be far, far worse, and would relegate D to an "experts only" language. I think we're served better by simple, universally applied rules even if we're not happy with the result in some odd cases.
 Perhaps the most serious problem is that said issue does not manifest

 FileConduit itself. It's only when a call is placed (from some other

 to either read() or close() that the compile breaks. I thought John


 stated it rather well in his post (7/24/2004,12:41) on the subject.

By importing a bunch of names into a scope, well, those names get found in that scope. Put the import outside of that scope!
 Lastly: You, and many others, are acutely aware of the problems related


 forward references. Placing the import inside the class scope is


 the /only/ way to resolve some of those. Now we have to move imports

 the class-scope just so we can call certain methods therein. Do you see

 conflict here?

The later DMD versions do a better job of handling forward references. If

 have specific examples of where that still fails, I can work on that.
 Putting the imports in the class scope is, I believe, the wrong solution,
 and even worse would be trying to hammer it to fit.


 What I personally find strange, Walter, is that such conflicts just


 seem to register as valid problems. At least now we know what the


 does.

What I interpret is going on here is that imports inside classes are attempts at working around a forward reference problem. The solution is to get at the forward reference problem.

Jul 25 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdvrns$1uqn$1 digitaldaemon.com...
 If you can't fix imports to act as though outer-scope methods have somehow
 been "aliased into scope", then I humbly suggest you make it illegal to
 allow an import within the class scope.

 I do that all the time for one good reason: all version(linux) and
 version(Win32) stuff should be isolated together wherever possible. This
 means I deliberately place imports inside class boundaries, because it is
 the most maintainable way to set things up (you don't have to go grubbing
 around trying to find/edit some reference elsewhere). If you make inner
 imports illegal, then you'll ultimately make the code less maintainable.

 even I would grudgingly admit that's better than the obnoxious behavior
 currently
 exposed.

 Of course, one or the other really has to be remedied: forward references
 or, uhhhh, smarter name resolution ... current versions of  DMD do not cut
 it for the former or the latter.

C'mon, Kris! You import the names into a scope, and then want the names to not be found. I don't think that is a resolvable problem. I admit to the documentation being inadequate, but after I explained the 4 simple steps of name lookup rules and when in the process overload resolution applies, the behavior you've seen is straightforward. C++ has loads of magic special rules that apply in arcane situations, and nobody understands them, they just dink around with the source until it appears to work. I'll be happy to work on any forward referencing issues.
Jul 25 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
"Walter"  wrote ..
 C'mon, Kris! You import the names into a scope, and then want the names to
 not be found. I don't think that is a resolvable problem. I admit to the
 documentation being inadequate, but after I explained the 4 simple steps

 name lookup rules and when in the process overload resolution applies, the
 behavior you've seen is straightforward. C++ has loads of magic special
 rules that apply in arcane situations, and nobody understands them, they
 just dink around with the source until it appears to work.

 I'll be happy to work on any forward referencing issues.

Once again, Walter; you appear to be deliberately ignoring the core issue. I most certainly did not state anything of the sort, regarding your opening salvo. I absolutely do want the names to be found: all signatures! That's the problem here! And again, this is not C++. How many people have to say that to you? It's not good enough to just DINK AROUND until it works, as you say. Unless this is just a bit of a laugh for you? If it is, then I'll stop wasting my time with your little game and move on. Try to look at this from an objective standpoint please? 1) Some external names were imported. Period. 2) They happened to conflict with some existing class method names. Period. 3) Instead of overloading, the imports hid the internal names completely from the outside world. No errors. Period. 4) In this case, when switching from Win32 to Linux, a bunch of class methods mysteriously disappeared from view. Period. 5) Hours of debugging time was spent tracking down thoroughly misleading compiler error messages. Period. This maketh a problem Walter. Not a personal opinion. Yes, you explained the four steps and they simply point out the serious limitations of the C++ model as applied to D. Indeed, you can simply cover up said limitations by not allowing imports within a class scope. Fair enough. I simply pointed out why I was taking advantage of the inner import, with respect to "locality of reference" vis-a-vis maintainability. For doing so, I proffer my deepest apologies. - Kris
Jul 25 2004
prev sibling parent Charlie <Charlie_member pathlink.com> writes:
The later DMD versions do a better job of handling forward references. If I
have specific examples of where that still fails, I can work on that.
Putting the imports in the class scope is, I believe, the wrong solution,
and even worse would be trying to hammer it to fit.

Very true, I tried compiling DFL with .94 ( or some such ) and it died with tons of forward refrences, while .96 worked without a hitch. I Think that old FR workaround should be abandoned ( thank goodness ). Charlie In article <cdvimn$1o5c$1 digitaldaemon.com>, Walter says...
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cdu94m$15t6$1 digitaldaemon.com...
 That's wonderful! However, you avoided a significant attribute of the

 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

 the other signatures. This seems like a half-way house, but I can

 /imagine/ why your compiler might choose not to do that.

It's following the rule that name lookup happens first, *then* overload resolution. Signatures only have relevance to overload resolution, and no relevance to name lookup. I worry that if I start breaking this rule for special cases here and there, the resulting confusion would be far, far worse, and would relegate D to an "experts only" language. I think we're served better by simple, universally applied rules even if we're not happy with the result in some odd cases.
 Perhaps the most serious problem is that said issue does not manifest

 FileConduit itself. It's only when a call is placed (from some other

 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.

By importing a bunch of names into a scope, well, those names get found in that scope. Put the import outside of that scope!
 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

 the class-scope just so we can call certain methods therein. Do you see

 conflict here?

The later DMD versions do a better job of handling forward references. If I have specific examples of where that still fails, I can work on that. Putting the imports in the class scope is, I believe, the wrong solution, and even worse would be trying to hammer it to fit.
 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.

What I interpret is going on here is that imports inside classes are attempts at working around a forward reference problem. The solution is to get at the forward reference problem.

Jul 25 2004
prev sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
I'm somewhat loath to post this (for a number of reasons), but will anyway.
Here's a revised edition, with some additional clarification in the
Interfaces section (the grayed sections), some minor example fixes per
Derek's post, and an addendum that speculates as to what's really going on
behind all this.

JCC makes a very good point about trying to address each of these in
individual threads. They may exist already, but I could be wrong.

For those who may think I'm just having a go at Walter, please consider how
confusing it can be to have three individual non-obvious issues within just
one specific area of the language.

- Kris


"some idiot" <fu bar.org> wrote in message
news:cdsd5t$u5$1 digitaldaemon.com...
 Attached is a small pdf noting three outstanding issues ~ each of which

 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.

 Such things might simply fall by the wayside otherwise.

Jul 24 2004
parent "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Dagnabit. I apologize: made a glaring typo ... will link here instead

http://svn.dsource.org/svn/projects/mango/trunk/doc/Names.pdf



"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:cduv7v$1e9u$1 digitaldaemon.com...
 I'm somewhat loath to post this (for a number of reasons), but will

 Here's a revised edition, with some additional clarification in the
 Interfaces section (the grayed sections), some minor example fixes per
 Derek's post, and an addendum that speculates as to what's really going on
 behind all this.

 JCC makes a very good point about trying to address each of these in
 individual threads. They may exist already, but I could be wrong.

 For those who may think I'm just having a go at Walter, please consider

 confusing it can be to have three individual non-obvious issues within

 one specific area of the language.

 - Kris


 "some idiot" <fu bar.org> wrote in message
 news:cdsd5t$u5$1 digitaldaemon.com...
 Attached is a small pdf noting three outstanding issues ~ each of which

 various bug reports stretching back into the old NG. Given that the v1.0
 release is getting closer, it seemed appropriate to try getting some
 priority and/or traction.

 Such things might simply fall by the wayside otherwise.


Jul 24 2004