# Multiple dispatch based on typing.overload

**URL:** https://discuss.python.org/t/multiple-dispatch-based-on-typing-overload/26197
**Category:** Ideas
**Tags:** typing
**Created:** [April 26, 2023, 12:26am UTC](https://discuss.python.org/t/multiple-dispatch-based-on-typing-overload/26197 "2023-04-26T00:26:27Z")
**Posts on this page:** 1
**Showing post:** 1

<div class="post-metadata">

### Author: ![ntessore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ntessore/32/10786_2.png) [@ntessore](https://discuss.python.org/u/ntessore)
#### Post date: [April 26, 2023, 12:26am UTC](https://discuss.python.org/t/multiple-dispatch-based-on-typing-overload/26197/1 "2023-04-26T00:26:27Z")

</div>

Looking at the original issues [[1]](https://github.com/python/typing/issues/14), [[2]](https://github.com/python/mypy/issues/563), it sounds as if there was initially an idea to have `@typing.overload` enable actual multiple dispatch, before it became informational only. This idea seems to resurface occasionally [[3]](https://github.com/python/cpython/issues/89263).

So, why **isn’t** there something like `@functools.multidispatch` that uses `@typing.overload` to actually do multiple dispatch? Since libraries for multiple dispatch exist, the actual dispatching is presumably not an insurmountable problem. And with `@typing.overload` there is **already** a standard mechanism to describe the individual specialisations.

To illustrate that point, now that Python 3.11 has added the ability to return the overloads of a given function, the following is entirely possible with a bit of DIY:

```py
from typing import Any, overload

@overload
def concat(a: list, b: list) -> list:
    return a + b

@overload
def concat(a: list, b: object) -> list:
    return a + [b]

@overload
def concat(a: object, b: list) -> list:
    return [a] + b

# decorator that does multiple dispatch with a stub function
@multipledispatch
def concat(a: Any, b: Any) -> Any:
    ...

print(concat([1], [2])) # [1, 2]
print(concat([1], 2)) # [1, 2]
print(concat(1, [2])) # [1, 2]
print(concat(1, 2)) # TypeError: No overload variant ...

```

As you can see, the idea is roughly that the overloads have all necessary type information for multiple dispatch anyway, so one only has to add the specific implementations. While this turns the usual pattern of only having stubs for `@typing.overload` exactly on its head, it does yield somewhat type-checked multiple dispatch right now at no extra cost.\[1\]

> **Toy implementation**
>
> Below is a **very** naive implementation of the dispatcher, just to run the example. It would probably be better to reuse the `@singledispatch` mechanism.
> 
> ```py
> from functools import wraps
> from inspect import signature
> from typing import Any, Callable, TypeVar, cast, get_overloads, get_type_hints
> 
> F = TypeVar('F', bound=Callable[..., Any])
> 
> def _dispatch(classes, registry):
> match = next((types for types in registry.keys()
> if all(issubclass(c, t) for c, t in zip(classes, types))),
> None)
> return registry.get(match)
> 
> def multipledispatch(func: F) -> F:
> registry = {}
> sig = signature(func)
> par = list(sig.parameters)
> for overload in get_overloads(func):
> ann = get_type_hints(overload)
> types = tuple(map(ann.get, par))
> registry[types] = overload
> 
> @wraps(func)
> def wrapper(*args, **kwargs):
> ba = sig.bind(*args, **kwargs)
> ba.apply_defaults()
> classes = tuple(ba.arguments[name]. __class__ for name in par)
> overload = _dispatch(classes, registry)
> if not overload:
> given = '", "'.join(cls. __name__ for cls in classes)
> raise TypeError(f'No overload variant of "{func. __name__ }" '
> f'matches argument types "{given}"')
> return overload(*args, **kwargs)
> 
> return cast(F, wrapper)
> 
> ```

* * *

1. Somewhat, because there is probably no good way to implement support for generic types.

---

_[View the full topic](https://discuss.python.org/t/multiple-dispatch-based-on-typing-overload/26197)._
