Why can't overloads take into account the return type.

  • Thread starter Thread starter Michael C
  • Start date Start date
M

Michael C

eg

void DoIt()
{
int i = FromString("1");
double d = FromString("1.1");
}

int FromString(string SomeValue)
{
return int.Parse(SomeValue);
}

double FromString(string SomeValue)
{
return double.Parse(SomeValue);
}

The return type is really just a byref param underneath anyway I presume.
 
eg

void DoIt()
{
int i = FromString("1");
double d = FromString("1.1");
}

int FromString(string SomeValue)
{
return int.Parse(SomeValue);
}

double FromString(string SomeValue)
{
return double.Parse(SomeValue);
}

The return type is really just a byref param underneath anyway I
presume.

Because the function call and assigning the result are two completely
different things. You don't even have to assign the result. So, tell
me... For example, what would happen if I did these:

// This doesn't get assigned anywhere - which one should I call?
FromString("3.14159");

// What should this return - a double or an int?
int i = (int)FromString("3.14159");

// Or even worse...
double d = (int)FromString("3.14159");

I'm sure you can see how ridiculous this would get if it did consider
return type...

-mdb
 
mdb said:
Because the function call and assigning the result are two completely
different things.

In VB6 the return value was just another parameter under the hood. The
actual return value was the error code, which I presume is the same in C#.
Someone correct me if i'm wrong.
You don't even have to assign the result. So, tell
me... For example, what would happen if I did these:

In any of those cases it would give an error in exactly the same way as it
would if it couldn't resolve an overload. See code below.
I'm sure you can see how ridiculous this would get if it did consider
return type...

Not at all. In this case the developer has chosen to have the return type
part of the signature by creating 2 overloads with the only difference being
the return type. Because it can be turned on and off I can't see any problem
with it.

Michael

[STAThread]
static void Main()
{
DoIt(null);
}

static void DoIt(Class1 SomeValue)
{
}

static void DoIt(Class2 SomeValue)
{
}
 
Michael C said:
In VB6 the return value was just another parameter under the hood. The
actual return value was the error code, which I presume is the same in C#.
Someone correct me if i'm wrong.

Sorry, you're wrong about C#.
 
Michael C said:
In VB6 the return value was just another parameter under the hood. The
actual return value was the error code, which I presume is the same in C#.
Someone correct me if i'm wrong.

I think you are confusing VB6 with COM. It is true that the COM Invoke()
function always returns an HRESULT, with the actual function return value
being an out parameter of Invoke(). However, I don't believe that is true
of the core language implementation. And anyway, neither VB nor COM allow
function overloading.

The answer to your original question about return values not being taken
into account in function overloads has to do with ambiguity. If you had two
functions by the same name that differed only by return type, say

int Foo() {return 3;}
string Foo() {return "xyz";}

What would the compiler assign to x?

object x = Foo() + Foo();

-- Alan
 
Alan Pretre said:
I think you are confusing VB6 with COM. It is true that the COM Invoke()
function always returns an HRESULT, with the actual function return value
being an out parameter of Invoke(). However, I don't believe that is true
of the core language implementation. And anyway, neither VB nor COM allow
function overloading.

VB6 uses the HRESULT method throughout except for code in modules, which is
why a module is the only place you can put an API callback.
The answer to your original question about return values not being taken
into account in function overloads has to do with ambiguity. If you had
two functions by the same name that differed only by return type, say

int Foo() {return 3;}
string Foo() {return "xyz";}

What would the compiler assign to x?

object x = Foo() + Foo();

But why is that such a problem? In the cases where the programmer decided to
use this it is just something they would have to take into account. They
have to take it into account now with parameters of the function, it's not
really any different. If it was only used where appropriate it would be
quite effective, eg

int i = Parse("123");
double d = Parse(123.45");

what you are saying is that it can be misused therefore it should not be
implemented but every feature can be misued.

Michael
 
Michael C said:
what you are saying is that it can be misused therefore it should not be
implemented but every feature can be misued.

When you add implicit type conversions to the mix, then besides the
technical problems there would be for code generation, there would be many
opportunities for subtle bugs.

-- Alan
 
Alan said:
When you add implicit type conversions to the mix, then besides the
technical problems there would be for code generation, there would be many
opportunities for subtle bugs.
No implicit type conversions there (I think).
What Michael is saying is that there would be a
double Parse(string value)
int Parse(string value)

Therefore since you are assigning to a double, the return type of double
method would be used.

etc..

If you use implicit casting say from int to double then you could maybe
run into trouble.

I think
JB
 
John said:
What Michael is saying is that there would be a
double Parse(string value)
int Parse(string value)
Therefore since you are assigning to a double, the return type of
double method would be used.

Ok. Now, let's pretend that we also have some overloaded methods that take
different argument types, say:

void Print(int i)
void Print(double i)

Now, someone comes along and writes:

int x = 5;

Print( x * Parse(something) );

Besides not having any clue which Print() and which Parse() to use, the
compiler doesn't even have a clue whether to use integer or floating point
multiplication.

The other problem with this is that it most definitely doesn't encourage
good object oriented design.
 
Chris Priede said:
Ok. Now, let's pretend that we also have some overloaded methods that
take different argument types, say:

void Print(int i)
void Print(double i)

Now, someone comes along and writes:

int x = 5;

Print( x * Parse(something) );

Besides not having any clue which Print() and which Parse() to use, the
compiler doesn't even have a clue whether to use integer or floating point
multiplication.

That's no big problem it should just give an error like it does with current
ambiguous overloads.
The other problem with this is that it most definitely doesn't encourage
good object oriented design.

How is that?

Michael
 
Michael C said:
That's no big problem it should just give an error like it does with
current ambiguous overloads.

Currently, errors due to ambiguious overloads are quite rare, what you are
proposing would bring them into the forefront. You also have failed to
provide any workable syntax that differentiates between casting the return
value to a given type and selecting a given return type.

Now, I know it sounds easy, but its not. Overload selection is a complex
beast already, expanding it so that the method call is related to factors
outside of the method itself would compliate a compiler considerably.
Currently the language works from the inside out. To determine the
particular method the caller wants to call you only need the method call
itself:

Parse(something);

that tells the compiler everything it needs to know. The call expression is
simple, life is good.

Now, you add into that a requirement to know the type of the variable the
return type is going to be assigned to or the parameter type it will be
passed to. This forces the compiler to have to be able to see

int x = Parse(something);

The method call is not individually resolvable, it becomes an expression
that means nothing concrete without being contained within another
expression(I do not believe this issue exists *anywhere* else in the
language, but I'm not certain). The compiler is harder to write, the
language harder to learn. Bam, instant complexity at the base level of the
compiler.

Now, lets take this a step further and produce a few difficult or impossible
to resolve scenarios.

Given:
int Parse(string value);
double Parse(string value);

int Calc(int value);
double Calc(double value);

double d = Calc(Parse("15")):

When the compiler takes a look at the call to Parse it cannot resolve what
overload to call. The compile rmust then take a look at the parameter type
for Calc, which also cannot be resolved atomicall ywithout knowing the
resolved return type of the Parse call, so you have yet another unresolved
method. You step back a little farther and finally discover an assignment
meaning you can determine the desired return type of Calc, and in this
example, determine which overload of Parse must be called. However,
considering the compiler without return type overloads would have had no
trouble doing this, what is the cost in complexity?

Return type overloading where the return type is chosen based on assignment
is going to atleast double(and probably treble) the complexity of overload
resolution, which is already one of the trickest parts of the language.

Given:
int Parse(string value);
double Parse(string value);
string Parse(string value);

int Calc(int value);
double Calc(double value);
double Calc(string value);

double d = Calc(Parse("apple")):

In this circumstance the call to Parse cannot be resolved on its own, thus
the call to Calc cannot be resolved on its own, and since there are two calc
overloads that return double, resolution fails.


Is this really as simple as you think it is? I've not even started on the
possible issues with generics and anonymous methods and the language itself
is already considerably more complex. Realistically implementing return type
overloading, IMHO, requires a mechanism to manually select the override
instead of relying on automatic selection that ignores casts and
converstions.
 
TJB replied to:
The method call is not individually resolvable, it becomes an expression
that means nothing concrete without being contained within another
expression(I do not believe this issue exists *anywhere* else in the
language, but I'm not certain). The compiler is harder to write, the
language harder to learn. Bam, instant complexity at the base level of the
compiler.

The lack of this feature makes the language a lot less elegant. Little
traversal methods such as T Next(), T First(), T Last(), etc, means
that an entire style of an immutable navigator chumpy becomes pretty
much impossible to write. I would question the idea of the language
being "harder to learn". It is actually more intuitive to let the
return type be of anything just the way the rest of the parameters are.

Microsoft has billions of dollars and since they jack up the price of
Visual Studio with every release they ought to suck it up and get some
people that can write compilers rather than whine and wring hands about
excessive complexity!
 
Microsoft has billions of dollars and since they jack up the price of
Visual Studio with every release they ought to suck it up and get some
people that can write compilers rather than whine and wring hands about
excessive complexity!

Hey calm down! If you don't like it the way it is. Use another programming
language and another framework. Use Java - oh, wow, same problem there...
Use C++ - oh, again, same problem there...
 
stork said:
The lack of this feature makes the language a lot less elegant. Little

Perhaps you should try a language that does context-sensitive
overloading. The only *language* i know of that has this is perl, where
functions behave differently depening on whether they are in a scalar or
array context.
 
Willy Denoyette said:
And wrong about VB6 too, what you describe is what happens when you call a
COM method.

I'd bet my house I'm right. The only exception is modules but anything in a
class, form or usercontrol works as I described even if the class is
private.

Michael
 
Claudio Grazioli said:
Hey calm down! If you don't like it the way it is. Use another programming
language and another framework. Use Java - oh, wow, same problem there...
Use C++ - oh, again, same problem there...

I noticed you described it has a problem. :-)

Michael
 
Daniel O'Connell said:
Now, you add into that a requirement to know the type of the variable the
return type is going to be assigned to or the parameter type it will be
passed to. This forces the compiler to have to be able to see

int x = Parse(something);

The compiler is going to need to know the return type anyway. I don't see it
much different from:

Parse(Something, ref x);
The method call is not individually resolvable, it becomes an expression
that means nothing concrete without being contained within another
expression(I do not believe this issue exists *anywhere* else in the
language, but I'm not certain). The compiler is harder to write, the
language harder to learn. Bam, instant complexity at the base level of the
compiler.

Overloading makes the language harder to learn, as does inheritance and
interfaces, we'd better leave those out too. :-)
Now, lets take this a step further and produce a few difficult or
impossible to resolve scenarios.

Given:
int Parse(string value);
double Parse(string value);

int Calc(int value);
double Calc(double value);

double d = Calc(Parse("15")):

When the compiler takes a look at the call to Parse it cannot resolve what
overload to call. The compile rmust then take a look at the parameter type
for Calc, which also cannot be resolved atomicall ywithout knowing the
resolved return type of the Parse call, so you have yet another unresolved
method. You step back a little farther and finally discover an assignment
meaning you can determine the desired return type of Calc, and in this
example, determine which overload of Parse must be called. However,
considering the compiler without return type overloads would have had no
trouble doing this, what is the cost in complexity?

But it can do it and it makes sense to the programmer for it to be double
all the way through.
Is this really as simple as you think it is? I've not even started on the
possible issues with generics and anonymous methods and the language
itself is already considerably more complex. Realistically implementing
return type overloading, IMHO, requires a mechanism to manually select the
override instead of relying on automatic selection that ignores casts and
converstions.

I'm sure it can get complex but in any case it can't resolve the call it
should just say so. Programmers would know to specify the return type in the
necessary cases. Maybe something like double d = Calc((double)Parse("15")):
could be used.
 
Back
Top