I'm developing a statically- and strongly-typed, compiled language, and I'm revisiting the idea of whether to include function overloading as a language feature. I realized that I'm a little bit biased, coming mainly from a C[++|#] background.

What are the most convincing arguments for and against including function overloading in a language?

a vanity mechanism that brings nothing to the semantic power of an O-O language, but hampers readability and complicates everyone's task

Now those are some sweeping generalizations, but he's a smart guy, so I think it's safe to say he could back them up if he needed to. In fact, he almost had Brad Abrams (one of the CLSv1 developers) convinced that .NET shouldn't support method overloading. (source) That's some powerful stuff. Can anyone shed some light on his thoughts, and whether his viewpoint is still justified 25 years later?

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
If this question can be reworded to fit the rules in the help center, please edit the question.

10 Answers
10

Function overloading is absolutely critical for C++-style template code. If I have to use different function names for different types, I can't write generic code. That would eliminate a large and heavily used part of the C++ library, and much of C++'s functionality.

It's usually present in member function names. A.foo() can call an entirely different function from B.foo(), but both functions are named foo. It's present in operators, as + does different things when applied to integers and floating-point numbers, and it's often used as a string concatenation operator. It seems odd not to allow it in regular functions as well.

It enables the use of Common Lisp-style "multimethods", in which the exact function called depends on two data types. If you haven't programmed in the Common Lisp Object System, try it before you call this useless. It's vital for C++ streams.

I/O without function overloading (or variadic functions, which are worse) would require a number of different functions, either to print values of different types or to convert values of different types to a common type (like String).

Without function overloading, if I change the type of some variable or value I need to change every function that uses it. It makes it much harder to refactor code.

It makes it easier to use APIs when the user doesn't have to remember which type naming convention is in use, and the user can just remember standard function names.

Without operator overloading, we'd have to label each function with the types it uses, if that base operation can be used on more than one type. This is essentially Hungarian notation, the bad way of doing it.

+1, all very good points. And trust me, I don't think multimethods are useless... I curse the very keyboard I type on every time I'm forced to use the visitor pattern.
–
Note to self - think of a nameSep 29 '10 at 17:11

I recommend at least being aware of type classes in Haskell. Type classes were created to be a disciplined approach to operator overloading, but have found other uses, and to an extent, made Haskell what it is.

A downside of this is that you have to come up with funky names for all your typeclasses (like in Haskell, you have the rather abstract Monad, Functor, Applicative as well as the simpler and more recognizable Eq, Num, and Ord).

An upside is that, once you're familiar with a typeclass, you know how to use any type in that class. Plus, it's easy to guard functions from types that don't implement the needed classes, as in:

group :: (Eq a) => [a] -> [[a]]
group = groupBy (==)

Edit: In Haskell, if you wanted an == operator that accepts two different types, you could use a multi-parameter type class:

Of course, this is probably a bad idea, since it explicitly lets you compare apples and oranges. However, you might want to consider this for +, since adding a Word8 to an Int really is a sensible thing to do in some contexts.

+1, I've been trying to grasp this concept ever since I first read it, and this helps. Does this paradigm make it impossible to define, for example, (==) :: Int -> Float -> Bool anywhere? (regardless of whether it's a good idea, of course)
–
Note to self - think of a nameSep 29 '10 at 17:17

If you allow multi-parameter type classes (which Haskell supports as an extension), you can. I updated the answer with an example.
–
Joey AdamsSep 29 '10 at 17:44

Hmm, interesting. So basically, class Eq a ... translated into pseudo-C-family would be interface Eq<A> {bool operator==(A x, A y);}, and instead of using templated code to compare arbitrary objects, you use this 'interface'. Is that right?
–
Note to self - think of a nameSep 30 '10 at 19:41

Right. You might also want to take a quick look at interfaces in Go. Namely, you don't have to declare a type as implementing an interface, you just have to implement all the methods for that interface.
–
Joey AdamsOct 2 '10 at 2:07

1

@Notetoself-thinkofaname: Regarding additional operators - yes and no. It allows for == to be in a different name-space but doesn't allow to override it. Please note that there is a single namespace (Prelude) included by default but you can prevent loading it by using extensions or by importing it explicitly (import Prelude () will import nothing from Prelude and import qualified Prelude as P will not insert symbols into current namespace).
–
Maciej PiechotkaJan 18 '13 at 18:48

That said, I realize sometimes overloaded functions do different things, rather than just calling another variant with default parameters...but in such a case, it's not a bad idea (in fact, it's probably a good idea) to just give it a different name.

Okay, let's try that again, and I'll try to make sense this time... What about Array Slice(int start, int length) {...} overloaded with Array Slice(int start) {return this.Slice(start, this.Count - start);}? That can't be coded using default parameters. Do you think they should be given different names? If so, how would you name them?
–
Note to self - think of a nameOct 22 '10 at 23:00

That doesn't address all uses of overloading.
–
MetalMikesterNov 26 '10 at 21:22

@MetalMikester: What uses do you feel I missed?
–
mipadiNov 29 '10 at 15:57

There's a reason why the return type isn't part of the method signature--or at least not a part that can be used for overload resolution--in any language I'm aware of. When you call a function as a procedure, without assigning the result to a variable or property, how is the compiler supposed to figure out which version to invoke if all the other arguments are identical?
–
Mason WheelerSep 29 '10 at 16:09

@Mason: You could heuristically detect the expected return type based on what is expected to be returned, however I don't expect that to be done.
–
Josh KSep 29 '10 at 16:43

1

Umm... how does your heuristic know what is expected to be returned when you're not expecting any return value?
–
Mason WheelerSep 29 '10 at 17:07

1

The type-expectation heuristic actually is in place... you can do things like EnumX.Flag1 | Flag2 | Flag3. I won't be implementing this, though. If I did and the return type wasn't used, I'd look for a return type of void.
–
Note to self - think of a nameSep 29 '10 at 17:15

1

@Mason: That's a good question, but in that case I would look for a void function (as mentioned). Also in theory you could choose any of them, because they will all perform the same function, simply return data in a different format.
–
Josh KSep 29 '10 at 17:39

I chose to provide ordinary overloading and multi-type type-classes in my language Felix.

I consider (open) overloading essential, especially in a language which as a lot of numeric types (Felix has all C's numeric types). However unlike C++ which abuses overloading by making templates depend on it, Felix polymorphism is parametric: you need overloading for templates in C++ because templates in C++ are badly designed.

Type classes are provided in Felix as well. For those that know C++ but don't grok Haskell, ignore those who describe it as overloading. It isn't remotely like overloading, rather, it is like template specialisation: you declare a template which you don't implement, then provide implementations for particular cases as you need them. The typing is parametrically polymorphic, the implementation is by ad hoc instantiation but it is not intended to be unconstrained: it has to implement the intended semantics.

In Haskell (and C++) you cannot state the semantics. In C++ the "Concepts" idea is roughly an attempt to approximate the semantics. In Felix, you can approximate the intention with axioms, reductions, lemmas and theorems.

The main, and only advantage of (open) overloading in a well principled language like Felix is that it makes it easier to remember library function names, both for the program writer and for the code reviewer.

The primary disadvantage of overloading is the complex algorithm required to implement it. It also doesn't sit very well with type inference: although the two are not entirely exclusive, the algorithm to do both is complex enough the programmer probably wouldn't be able to predict the results.

In C++ this is also a problem because it has a sloppy matching algorithm and also supports automatic type conversions: in Felix I "fixed" this problem by requiring an exact match and no automatic type conversions.

So you have a choice I think: overloading or type inference. Inference is cute, but it is also very hard to implement in a way that properly diagnoses conflicts. Ocaml, for example, tells you where it detects a conflict, but not where it inferred the expected type from.

Overloading is not much better, even if you have a quality compiler that tries to tell you all the candidates, it can be hard to read if the candidates are polymorphic, and even worse if it's C++ template hackery.

@Mason Wheeler: Be aware then of Ada, which does overload on return type.
Also my language Felix does it too in some contexts, specifically, when a function returns another function and there is a call like:

f a b // application is left assoc: (f a) b

the type of b can be used for overload resolution. Also C++ overloads on return type in some circumstances:

int (*f)(int) = g; // choses g based on type, not just signature

In fact there are algorithms for overloading on return type, using type inference. It's actually not that hard to do with a machine, the problem is that humans find it hard. (I think the outline is given in the Dragon Book, the algorithm is called the see-saw algorithm if I remember correctly)

Use-case against implementing function overloading: 25 like-named methods that kind of do the same things but with completely different sets of arguments in a wide variety of patterns.

Use-case against not implementing function overloading: 5 similarly-named methods with very similar sets of types in the exact same pattern.

At the end of the day, I'm not looking forward to reading the docs for an API produced in either case.

But in one case it's about what users might do. In the other case it's what users must do because of a language restriction. IMO, it's better to at least allow for the possibility of program authors being smart enough to overload sensibly without creating ambiguity. When you slap their hands and take the option away you're basically guaranteeing ambiguity is going to happen. I'm more about trusting the user to do the right thing than assume they'll always do the wrong thing. In my experience, protectionism tends to lead to even worse behavior on the part of a language's community.