<p>In most languages that support function overloading, this is done rather ad hoc. To create a set of overloaded function you simply define all functions in that desired set with the same name in the same scope.</p>
<p>This has a few downsides when you want to refer to a particular overload. For example to pass it as a callable or to disambiguate which one you wish to call.</p>
<p>What are the alternatives to this? What are the pros and cons of those approaches?</p>

<overloading>
What are the alternatives to classic ad hoc function overloading?
CC BY-SA 4.0
2023-06-28T22:28:47.427Z
Related SO question/answer: https://stackoverflow.com/questions/47971360/typeclasses-and-overloading-what-is-the-connection/47971589#47971589
Alexis King
<h2>The trivial option: just don't have overloads</h2>
<p>Assuming function overloads are not <a href="https://en.wikipedia.org/wiki/Dynamic_binding" rel="noreferrer">dynamically dispatched</a> based on the arguments' types, there is little need for them beyond the fact that it's nice for different versions of the same method to have the same name. But every program with function overloads can trivially be rewritten to not use overloads, by just giving the functions different names. So overloads are fundamentally just a convenience feature, and you can leave them out of a language without needing to offer an alternative.</p>
<p>In most cases it really is that simple, but there are two notable counterexamples:</p>
<ul>
<li>Overloaded constructors or initialisers in most languages can't be given different names, because they either don't have names (e.g. Java, Javascript), or their name is mandated (e.g. Python). This can be addressed by using static factory methods instead of constructor overloads, albeit with the small inconvenience that subclass constructors cannot invoke static factory methods instead of a superclass constructor.</li>
<li>When two or more interfaces use the same name for methods with different signatures, if the interfaces belong to third-party libraries then the user cannot just rename them. In this case, overloading is necessary to implement both interfaces. However, this still leaves the problem where two interfaces have the same method name with the same signature, but you want to implement each interface differently.</li>
</ul>
<p>Neither of these concerns really <em>necessitate</em> overloads, and overloads don't fully solve the second issue anyway, so you might well just decide to go the simple route and not have overloads at all.</p>
<h2>Default arguments</h2>
<p>Java's method overloads are mainly useful for things like this:</p>
<pre class="lang-java prettyprint-override"><code>void foo() {
    foo(0);
}

void foo(int bar) {
    // ...
}
</code></pre>
<p>In many other languages such as Python, this is achieved more simply by giving <code>bar</code> a default value:</p>
<pre class="lang-python prettyprint-override"><code>def foo(bar=0):
    # ...
</code></pre>
<p>It is difficult to support both overloads and default arguments, because some calls might be compatible with multiple overload signatures, where neither is &quot;more specific&quot;. Normally languages only have one or the other; Typescript is a notable exception, but Typescript's overloads are ordered by priority and only affect the function's signature, not its implementation. So in Typescript, even if multiple overload signatures are applicable at a call-site, the highest-priority overload is selected, but also the runtime behaviour would be the same regardless.</p>
<p>The upsides of default arguments are that they can make code more readable, and don't require the function signatures to be duplicated; particular if there are multiple arguments with default values, there might need to be exponentially many overloads. That is, if three arguments all have default values then in a language like Java which has overloads instead of defaults, the user would need to write 2<sup>3</sup> = 8 different overloads to handle all the combinations.</p>
<p>One downside of default arguments is that they don't allow different &quot;versions&quot; of the function to have drastically different implementations. But in practice, overloads are mainly used to mimic default arguments, and even when they aren't, branching based on the argument values is generally sufficient.</p>

CC BY-SA 4.0
2023-06-22T01:39:19.327Z
2023-06-22T14:50:15.493Z
2023-06-22T14:50:15.493Z
kaya3
CC BY-SA 4.0
2023-06-22T03:22:14.537Z
Objective-C gets around the constructor issue by not necessarily having constructors in the traditional sense; `+alloc` returns uninitialized memory and your `-init...` methods initialize it. Where C++ would have `new Foo(3)`, Objective-C has `[[Foo alloc] initWithValue:3]`.
Bbrk24
CC BY-SA 4.0
2023-06-22T07:58:43.350Z
There is nothing that says you cannot allow for a name to be added to a constructor in your language,
ratchet freak
CC BY-SA 4.0
2023-06-22T21:28:37.407Z
Templates are another strong reason to have overloads
abel1502
CC BY-SA 4.0
2023-06-27T17:30:36.857Z
As another example, C++ supports both overloading and default parameters
CoffeeTableEspresso
<h1>Explicit overload set</h1>
<p>You first define all the possible function you wish to include in the overload set and then define the overload set and reference all other functions by name. For example <a href="https://github.com/odin-lang/Odin/tree/master" rel="noreferrer">Odin</a> uses this.</p>
<pre><code>add :: proc{add_ints, add_floats, add_numbers}
</code></pre>
<p>This has a consequences:</p>
<ul>
<li>Each function in the overload set is explicit, there are no hidden surprises when you reuse a common name.</li>
<li>The set is entirely defined in a single location and is closed and cannot be expanded after the fact.</li>
<li>It provides an order to the functions in the set which could be used as a tie breaker in case ambiguity.</li>
<li>You can create multiple sets that include a particular function without needing to duplicate that function or needing to create a function that only forwards.</li>
<li>You can create that overload set local to a scope.</li>
</ul>

CC BY-SA 4.0
2023-06-21T14:28:47.653Z
2023-06-21T14:28:47.653Z
ratchet freak
<p>Julia provides an alternative approach: dynamic dispatch based on types. When you define a function and there is already another with the same name, you're actually modifying the old function by adding a new way to call it. This gives you another way to do overloading, and the disambiguation is done at runtime.</p>

CC BY-SA 4.0
2023-06-24T00:01:39.533Z
2023-06-24T00:01:39.533Z
ice1000
CC BY-SA 4.0
2023-06-24T00:22:21.550Z
Is this like [how TypeScript does it](https://languagedesign.stackexchange.com/a/1606/15)?
Bbrk24
CC BY-SA 4.0
2023-06-24T00:31:42.657Z
@Bbrk24 sounds similar, but I don't know TS well enough to say yes.
ice1000
CC BY-SA 4.0
2023-06-24T00:40:59.133Z
The way you overload a function in TypeScript is by forward-declaring it (yes, like in C) multiple times with different signatures, and then providing a singular implementation that works for all of the forward-declared versions.
Bbrk24
CC BY-SA 4.0
2023-06-24T00:44:29.150Z
Oh, then it's completely different ;)
ice1000
<h1>Rust-style Traits</h1>
<p>In trait/typeclass-based type systems, instead of overloading functions, you put the function in a trait, and implement that trait with different types of arguments. For example, the <code>Add</code> trait has an <code>add</code> function, which has a signature along the lines of <code>fn add(self, other: Rhs) -&gt; Output</code>.</p>
<p>This function can then be referred to in a few ways. You can refer to <code>add</code> functions in general with <code>Add::add</code>; passing this as an argument to something would allow you to ignore what types are actually added. But if you want to be more specific, you can specify, for example, <code>i64::add</code>.</p>

CC BY-SA 4.0
2023-06-21T15:52:38.400Z
2023-06-21T15:52:38.400Z
rydwolf
<h1>TypeScript: on the callee</h1>
<p>Function overloads in TypeScript are the same function at runtime, and it’s on the callee to handle all possible sets of inputs and change behavior accordingly.</p>
<p>This approach only works if your compilation target is dynamically typed and okay with mismatched argument counts, as JavaScript is. It wouldn’t work in a strongly-typed language with a strict calling convention, such as Swift.</p>

CC BY-SA 4.0
2023-06-21T16:14:46.553Z
2023-06-21T16:14:46.553Z
Bbrk24
# What are the alternatives to classic ad hoc function overloading?
Posted by: username_0 on 2023-06-21
In most languages that support function overloading, this is done rather ad hoc. To create a set of overloaded function you simply define all functions in that desired set with the same name in the same scope.

This has a few downsides when you want to refer to a particular overload. For example to pass it as a callable or to disambiguate which one you wish to call.

What are the alternatives to this? What are the pros and cons of those approaches?
**Tags:** overloading
> **Comments:**
> **username_1**: Related SO question/answer: https://stackoverflow.com/questions/47971360/typeclasses-and-overloading-what-is-the-connection/47971589#47971589  
---
## 5 answers
### Answer by username_2 on 2023-06-22. Score: 6
The trivial option: just don't have overloads
Assuming function overloads are not dynamically dispatched based on the arguments' types, there is little need for them beyond the fact that it's nice for different versions of the same method to have the same name. But every program with function overloads can trivially be rewritten to not use overloads, by just giving the functions different names. So overloads are fundamentally just a convenience feature, and you can leave them out of a language without needing to offer an alternative.
In most cases it really is that simple, but there are two notable counterexamples:

Overloaded constructors or initialisers in most languages can't be given different names, because they either don't have names (e.g. Java, Javascript), or their name is mandated (e.g. Python). This can be addressed by using static factory methods instead of constructor overloads, albeit with the small inconvenience that subclass constructors cannot invoke static factory methods instead of a superclass constructor.
When two or more interfaces use the same name for methods with different signatures, if the interfaces belong to third-party libraries then the user cannot just rename them. In this case, overloading is necessary to implement both interfaces. However, this still leaves the problem where two interfaces have the same method name with the same signature, but you want to implement each interface differently.

Neither of these concerns really necessitate overloads, and overloads don't fully solve the second issue anyway, so you might well just decide to go the simple route and not have overloads at all.
Default arguments
Java's method overloads are mainly useful for things like this:
void foo() {
    foo(0);
}

void foo(int bar) {
    // ...
}

In many other languages such as Python, this is achieved more simply by giving bar a default value:
def foo(bar=0):
    # ...

It is difficult to support both overloads and default arguments, because some calls might be compatible with multiple overload signatures, where neither is "more specific". Normally languages only have one or the other; Typescript is a notable exception, but Typescript's overloads are ordered by priority and only affect the function's signature, not its implementation. So in Typescript, even if multiple overload signatures are applicable at a call-site, the highest-priority overload is selected, but also the runtime behaviour would be the same regardless.
The upsides of default arguments are that they can make code more readable, and don't require the function signatures to be duplicated; particular if there are multiple arguments with default values, there might need to be exponentially many overloads. That is, if three arguments all have default values then in a language like Java which has overloads instead of defaults, the user would need to write 23 = 8 different overloads to handle all the combinations.
One downside of default arguments is that they don't allow different "versions" of the function to have drastically different implementations. But in practice, overloads are mainly used to mimic default arguments, and even when they aren't, branching based on the argument values is generally sufficient.
> **Comments:**
> **username_3** (2023-06-22): Objective-C gets around the constructor issue by not necessarily having constructors in the traditional sense; `+alloc` returns uninitialized memory and your `-init...` methods initialize it. Where C++ would have `new Foo(3)`, Objective-C has `[[Foo alloc] initWithValue:3]`.  
> **username_0** (2023-06-22): There is nothing that says you cannot allow for a name to be added to a constructor in your language,  
> **username_4** (2023-06-22): Templates are another strong reason to have overloads  
> **username_5** (2023-06-27): As another example, C++ supports both overloading and default parameters  
---
### Answer by username_0 on 2023-06-21. Score: 5
Explicit overload set
You first define all the possible function you wish to include in the overload set and then define the overload set and reference all other functions by name. For example Odin uses this.
add :: proc{add_ints, add_floats, add_numbers}

This has a consequences:

Each function in the overload set is explicit, there are no hidden surprises when you reuse a common name.
The set is entirely defined in a single location and is closed and cannot be expanded after the fact.
It provides an order to the functions in the set which could be used as a tie breaker in case ambiguity.
You can create multiple sets that include a particular function without needing to duplicate that function or needing to create a function that only forwards.
You can create that overload set local to a scope.
---
### Answer by username_6 on 2023-06-24. Score: 5
Julia provides an alternative approach: dynamic dispatch based on types. When you define a function and there is already another with the same name, you're actually modifying the old function by adding a new way to call it. This gives you another way to do overloading, and the disambiguation is done at runtime.
> **Comments:**
> **username_3** (2023-06-24): Is this like [how TypeScript does it](https://languagedesign.stackexchange.com/a/1606/15)?  
> **username_6** (2023-06-24): @Bbrk24 sounds similar, but I don't know TS well enough to say yes.  
> **username_3** (2023-06-24): The way you overload a function in TypeScript is by forward-declaring it (yes, like in C) multiple times with different signatures, and then providing a singular implementation that works for all of the forward-declared versions.  
> **username_6** (2023-06-24): Oh, then it's completely different ;)  
---
### Answer by username_7 on 2023-06-21. Score: 3
# Rust-style Traits

In trait/typeclass-based type systems, instead of overloading functions, you put the function in a trait, and implement that trait with different types of arguments. For example, the `Add` trait has an `add` function, which has a signature along the lines of `fn add(self, other: Rhs) -> Output`.

This function can then be referred to in a few ways. You can refer to `add` functions in general with `Add::add`; passing this as an argument to something would allow you to ignore what types are actually added. But if you want to be more specific, you can specify, for example, `i64::add`.
---
### Answer by username_3 on 2023-06-21. Score: 3
# TypeScript: on the callee

Function overloads in TypeScript are the same function at runtime, and it’s on the callee to handle all possible sets of inputs and change behavior accordingly.

This approach only works if your compilation target is dynamically typed and okay with mismatched argument counts, as JavaScript is. It wouldn’t work in a strongly-typed language with a strict calling convention, such as Swift.
---
