Thread overview
Irritating problem with overloading functions in separate modules
Jul 06, 2005
Ben Hinkle
Jul 06, 2005
Ben Hinkle
Jul 08, 2005
Walter
July 06, 2005
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.


July 06, 2005
> 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.


July 06, 2005
"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.


July 06, 2005
"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.


July 07, 2005
"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.


July 08, 2005
"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
problem.

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
only
> by which type of integer they take, chances are, they're _meant_ to be
that
> 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
be
> the "standard" name for such a method in D), as you have to use all kinds
of
> 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.