For contemporary languages, it's just syntactic sugar; in a completely language-agnostic sort of way, it's more than that.
Previously this answer stated simply that it's more than syntactic sugar, but if you'll see in the comments, Falco raised the point that there was one piece of the puzzle that contemporary languages appear to all be missing; they don't mix method overloading with dynamic determination of which function to call in the same step. This will be clarified later.
Here's why it should be more.
Consider a language that supports both method overloading and untyped variables. You could have the following method prototypes:
bool someFunction(int arg);
bool someFunction(string arg);
In some languages, you would be probably be resigned to knowing at compile time which one of these would be called by a given line of code. But in some languages, not all variables are typed (or they're all implicitly typed as Object
or whatever), so imagine building a dictionary whose keys map to values of different types:
dict roomNumber; // some hotels use numbers, some use letters, and some use
// alphanumerical strings. In some languages, built-in dictionary
// types automatically use untyped values for their keys to map to,
// so it makes more sense then to allow for both ints and strings in
// your code.
Now then, what if you wanted to apply someFunction
to one of those room numbers? You call this:
someFunction(roomNumber[someSortOfKey]);
Is someFunction(int)
called, or is someFunction(string)
called? Here you see one example where these are not totally orthogonal methods, especially in higher-level languages. The language has to figure out - during runtime - which one of these to call, so it still has to regard these as being at least somewhat the same method.
Why not simply use templates? Why not simply use an untyped argument?
Flexibility and finer-grained control. Sometimes using templates / untyped arguments are a better approach, but sometimes they're not.
You have to think about cases where, for instance, you might have two method signatures that each take an int
and a string
as arguments, but where the order is different in each signature. You may very well have a good reason to do this, as each signature's implementation may do largely the same thing, but with just a slightly different twist; the logging could be different, for example. Or even if they do the same exact thing, you may be able to automatically glean certain information from just the order in which the arguments were specified. Technically you could just use pseudo-switch statements to determine the type of each of the arguments passed in, but that gets messy.
So is this next example bad programming practice?
bool stringIsTrue(int arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
bool stringIsTrue(Object arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
bool stringIsTrue(string arg)
{
if (arg == "0")
{
return false;
}
else
{
return true;
}
}
Yes, by and large. In this particular example, it could keep somebody from trying to apply this to certain primitive types and getting back unexpected behavior (which could be a good thing); but let's just assume I abbreviated the code above, and that you, in fact, have overloads for all the primitive types, as well as for Object
s. Then this next bit of code really is more appropriate:
bool stringIsTrue(untyped arg)
{
if (arg.toString() == "0")
{
return false;
}
else
{
return true;
}
}
But what if you only needed to use this for int
s and string
s, and what if you want it to return true based on simpler or more complicated conditions accordingly? Then you have a good reason to use overloading:
bool appearsToBeFirstFloor(int arg)
{
if (arg.digitAt(0) == 1)
{
return true;
}
else
{
return false;
}
}
bool appearsToBeFirstFloor(string arg)
{
string firstCharacter = arg.characterAt(0);
if (firstCharacter.isDigit())
{
return appearsToBeFirstFloor(int(firstCharacter));
}
else if (firstCharacter.toUpper() == "A")
{
return true;
}
else
{
return false;
}
}
But hey, why not just give those functions two different names? You still have the same amount of fine-grained control, don't you?
Because, as stated before, some hotels use numbers, some use letters, and some use a mixture of numbers and letters:
appearsToBeFirstFloor(roomNumber[someSortOfKey]);
// will treat ints and strings differently, without you having to write extra code
// every single spot where the function is being called
This still isn't precisely the same exact code I would use in real life, but it should illustrate the point I'm making just fine.
But... Here's why it isn't more than syntactic sugar in contemporary languages.
Falco raised the point in the comments that current languages basically don't mix method overloading and dynamic function selection within the same step. The way I previously understood certain languages to work was that you could overload appearsToBeFirstFloor
in the example above, and then the language would determine at runtime which version of the function to be called, depending on the runtime value of the untyped variable. This confusion partially stemmed from working with ECMA-sorts of languages, like ActionScript 3.0, in which you can easily randomize which function gets called on a certain line of code at runtime.
As you may know, ActionScript 3 doesn't support method overloading. As for VB.NET, you can declare and set variables without assigning a type explicitly, but when you try to pass these variables as arguments to overloaded methods, it still doesn't want to read the runtime value to determine which method to call; it instead wants to find a method with arguments of type Object
or no type or something else like that. So the int
vs. string
example above wouldn't work in that language either. C++ has similar issues, as when you use something like a void pointer or some other mechanism like that, it still requires you to manually disambiguate the type at compile time.
So as the first header says...
For contemporary languages, it's just syntactic sugar; in a completely language-agnostic sort of way, it's more than that. Making method overloading more useful and relevant, like in the example above, may actually be a good feature to add to an existing language (as has been widely implicitly requested for AS3), or it could also serve as one among many different fundamental pillars for the creation of a new procedural / object-oriented language.