View mode: basic / threaded / horizontal-split · Log in · Help
July 26, 2004
Re: The Alias peek-a-boo Game
"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.
July 26, 2004
Re: The Alias peek-a-boo Game
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?
>
>
July 26, 2004
Re: The Alias peek-a-boo Game
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.
It'll
> be an arbitrary rule, one more bit of arcana needed to program in D.
>
>
July 26, 2004
Re: The Alias peek-a-boo Game
"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
> 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>

Stroustrup's arguments apply equally to D.

> 2) C++ had to deal with multiple inheritance ~ I imagine that's a big
factor
> 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
pay this 'Stroustrup Tax' in any
> 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
what
> supposed pedigree it has ~ so many other things in D are completely
natural.

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

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.
July 26, 2004
Re: External Names
"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
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.


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
July 26, 2004
Re: The Alias peek-a-boo Game
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
July 26, 2004
Re: The Alias peek-a-boo Game
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
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.

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

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
the
> Java rules were the default there doesn't seem to be nearly so simple a
way
> 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
July 26, 2004
Re: The Alias peek-a-boo Game
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
> 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?
> 
> 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
July 26, 2004
Re: The Alias peek-a-boo Game
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);
#        }
#    }
July 26, 2004
Re: The Alias peek-a-boo Game - Test.java
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
4 5 6 7 8 9 10 11 12
Top | Discussion index | About this forum | D home