Overloading
When a compiler or interpreter encounters a variable in a program, it must determine what memory we are trying to access. It resolves the access by looking through the visible scopes for the nearest definition of a variable with the given name. Function calls must also be resolved to figure out which function the programmer is intending to call. Some languages base this resolution only on the function's name, just as with variables. Some also consider other information like parameters. What does Ruby consider?
If we define a function in a scope and then try to define another version of it in the same scope, the second overwrites the first—even if the parameters are different. In Ruby, there's no way to have two functions in the same scope with the same name. Does C support multiple versions?
Other languages do allow us to have multiple functions of the same name visible within a scope. A function with one name but multiple definitions is overloaded. Why would one want overloading? If you have ever printed anything to System.out
in Java, you have been the beneficiary of the overloading of println
in the PrintStream
class. There are many versions of println
, each handling a different type. They all have the same purpose, so giving them all the same name is reasonable.
Polymorphism is the idea that one notional unit of code can operate on data of many shapes. This term is often applied in the context of an inheritance hierarchy. There may be many definitions of a method with a particular signature scattered across the superclass and subclasses of a hierarchy, but we think of them as a single behavior. We can call that method on an object without knowing its exact type, and somehow the appropriate definition of the method will run. This is subtype polymorphism.
Overloading presents a similar situation to subtype polymorphism but without a hierarchy. With overloading, there are many definitions of a function with a particular name but differing signatures. As a family, these functions serve data of many types. Overloading is sometimes called ad hoc polymorphism.
To resolve which definition of an overloaded function is being called, the compiler or interpreter examines clues from the calling context. The function is commonly identified using a compound criteria involving some subset of the following information:
- name
- arity
- actual parameter names
- actual parameter types
- modifiers
- exceptions
- return type
Many dynamically-typed languages don't have a lot of this information and therefore do not support overloading. Like Ruby and Python. Only statically-typed languages have known parameter and return types. Only languages with named parameters can consider their names. Surely dynamically-typed languages could at least use arity to disambiguate calls? But default parameters and variadic functions complicate how parameters should be counted. Return types are often not included because not every call consumes a function's return value or uses it in a disambiguating manner. This is the case in Java, but not the case in Swift.
The subset of this data that is included in the compound key used to resolve the overload is the function's unique signature. Many languages forbid two functions having the same signature. The criteria for resolving which function is being called based on the signature can be a complex task, especially in C++.