www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Irritating problem with overloading functions in separate modules

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
OK, let's assume that you have the following code:

import std.stdio;
import std.string;

enum E
{
 A,
 B
}

char[] toString(E e)
{
 switch(e)
 {
  case E.A: return "E.A".dup;
  case E.B: return "E.B".dup;
 }
}

void main()
{
 E e;
 e=E.B;
 writefln(toString(e));
}

This works just fine, as the toString() referenced in main() first looks up 
the function in the module's namespace, and finds toString(E e).

Now let's put the enum and the toString() in another module, called mymod. 
So now the main program file contains the following:

import std.stdio;
import std.string;
import mymod;

void main()
{
 E e;
 e=E.B;
 writefln(toString(e));
}

This causes a strange error.  Supposedly, mymod.toString() conflicts with 
std.string.toString().  This is because the function is overloaded in a 
separate module from std.string, and although it has different parameters, 
it has the same name.  The compiler doesn't go any further than the name 
when determining conflicts.

Why can't the compiler determine which toString() to call?  It's obvious 
which one I want to call; the one that takes an E as an argument.  But 
instead, I have to use an alias to tell the compiler which one, or use a 
fully qualified name.  Both methods defeat the purpose of having a 
toString() method, as the correct toString() should just be selected based 
on the parameter type.

Is the name conflict pass done before the function overloads are determined? 
This doesn't make sense for functions, as overloading functions _requires_ 
for there to be a name conflict. 
Jul 06 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 This causes a strange error.  Supposedly, mymod.toString() conflicts with 
 std.string.toString().  This is because the function is overloaded in a 
 separate module from std.string, and although it has different parameters, 
 it has the same name.  The compiler doesn't go any further than the name 
 when determining conflicts.

correct.
 Why can't the compiler determine which toString() to call?  It's obvious 
 which one I want to call; the one that takes an E as an argument.  But 
 instead, I have to use an alias to tell the compiler which one, or use a 
 fully qualified name.  Both methods defeat the purpose of having a 
 toString() method, as the correct toString() should just be selected based 
 on the parameter type.

You can also bring the toStrings into the module in question by aliasing both: alias std.string.toString toString; alias mymod.toString toString; I believe the reason Walter has the current behavior is it prevents casual mistakes from suddenly using wildly different functions from different modules. As a result one can either be pretty explicit about bringing other module symbols into the current module.
Jul 06 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:dahdn9$2r1t$1 digitaldaemon.com...
 I believe the reason Walter has the current behavior is it prevents casual 
 mistakes from suddenly using wildly different functions from different 
 modules.

I'd like to know how this would be possible. Thanks to D's rather strict function overloading, if there were any parameter conflicts, they'd show up as an error, saying that there's an ambiguous overload.
Jul 06 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:dahkg2$30j8$1 digitaldaemon.com...
 "Ben Hinkle" <bhinkle mathworks.com> wrote in message 
 news:dahdn9$2r1t$1 digitaldaemon.com...
 I believe the reason Walter has the current behavior is it prevents 
 casual mistakes from suddenly using wildly different functions from 
 different modules.

I'd like to know how this would be possible. Thanks to D's rather strict function overloading, if there were any parameter conflicts, they'd show up as an error, saying that there's an ambiguous overload.

module1.toString(int) module1.toString(short) are more likely to be "desirable" overloads vs module1.toString(int) module2.toString(short) for user code like import module1; import module2; ... int x; ... toString(x); changing int to short seems rather harmless, no? But it could change to call a very different toString.
Jul 06 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:dahm4j$3h$1 digitaldaemon.com...
 module1.toString(int)
 module1.toString(short)

 are more likely to be "desirable" overloads vs

 module1.toString(int)
 module2.toString(short)

 for user code like

 import module1;
 import module2;
 ...
 int x;
 ...
 toString(x);

 changing int to short seems rather harmless, no? But it could change to 
 call a very different toString.

I see what you're saying, but I still don't think it'd be much of a problem. The chances of two modules having two functions with the same name is rare enough. And if that does happen, they often don't have similar parameter lists. And if they do, the problem only exists if you use integral types and take advantage of the implicit casting between integral types. And usually, if you have two functions which have the same name and differ only by which type of integer they take, chances are, they're _meant_ to be that way ;) It's very frustrating right now to add any kind of toString() methods for our own types (which kind of sucks, considering that toString() seems to be the "standard" name for such a method in D), as you have to use all kinds of aliases that seem rather unnecessary.
Jul 07 2005
parent "Walter" <newshound digitalmars.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message
news:dajgt8$1cij$1 digitaldaemon.com...
 I see what you're saying, but I still don't think it'd be much of a

What you're asking for is how C++ works, and it is a problem, especially when the complexity of the program passes a certain point.
 The chances of two modules having two functions with the same name is rare
 enough.

Up to a point, that's true. The proplems crop up, though, when very complex programs get put together.
 And if that does happen, they often don't have similar parameter
 lists.  And if they do, the problem only exists if you use integral types
 and take advantage of the implicit casting between integral types.  And
 usually, if you have two functions which have the same name and differ

 by which type of integer they take, chances are, they're _meant_ to be

 way ;)

These problems do happen in C++, which is why the C++ committee felt compelled to add namespaces. That didn't work too well, either. It's an ongoing problem, and I occaisionally hear from a frustrated C++ developer who eventually managed to track a problem down to calling a completely unexpected function because of this.
 It's very frustrating right now to add any kind of toString() methods for
 our own types (which kind of sucks, considering that toString() seems to

 the "standard" name for such a method in D), as you have to use all kinds

 aliases that seem rather unnecessary.

It's difficult to strike the right balance between doing things implicitly and having too much done implicitly. D errs on the side of a bit of caution by requiring one to say which groups of functions are to be overloaded together, rather than the C++ rule of looking at every function in the program.
Jul 07 2005