python / peps

Python Enhancement Proposals
https://peps.python.org
4.35k stars 1.5k forks source link

Could it possible to make Python declare overloaded function like this? #3841

Closed fResult closed 2 months ago

fResult commented 2 months ago

In TypeScript, it has the way to create compose function with overloaded something like this:

type UnaryFunc<T, R> = (arg: T) => R;

export function compose<T1, T2, R>(
  fn2: UnaryFunc<T2, R>,
  fn1: UnaryFunc<T1, T2>
): UnaryFunc<T1, R>;
export function compose<T1, T2, T3, R>(
  fn3: UnaryFunc<T3, R>,
  fn2: UnaryFunc<T2, T3>,
  fn1: UnaryFunc<T1, T2>
): UnaryFunc<T1, R>;
export function compose<T1, T2, T3, T4, R>(
  fn4: UnaryFunc<T4, R>,
  fn3: UnaryFunc<T3, T4>,
  fn2: UnaryFunc<T2, T3>,
  fn1: UnaryFunc<T1, T2>
): UnaryFunc<T1, R>;
export function compose<T1, T2, T3, T4, T5, R>(
  fn5: UnaryFunc<T5, R>,
  fn4: UnaryFunc<T4, T5>,
  fn3: UnaryFunc<T3, T4>,
  fn2: UnaryFunc<T2, T3>,
  fn1: UnaryFunc<T1, T2>
): UnaryFunc<T1, R>;
export function compose<T1, T2, T3, T4, T5, T6, R>(
  fn6: UnaryFunc<T6, R>,
  fn5: UnaryFunc<T5, T6>,
  fn4: UnaryFunc<T4, T5>,
  fn3: UnaryFunc<T3, T4>,
  fn2: UnaryFunc<T2, T3>,
  fn1: UnaryFunc<T1, T2>
): UnaryFunc<T1, R>;
export function compose<T1, T2, T3, T4, T5, T6, T7, R>(
  fn7: UnaryFunc<T7, R>,
  fn6: UnaryFunc<T6, T7>,
  fn5: UnaryFunc<T5, T6>,
  fn4: UnaryFunc<T4, T5>,
  fn3: UnaryFunc<T3, T4>,
  fn2: UnaryFunc<T2, T3>,
  fn1: UnaryFunc<T1, T2>
): UnaryFunc<T1, R>;
export function compose<T = any, R = any>(
  ...fns: UnaryFunc<any, any>[]
): UnaryFunc<T, R> {
  return function (arg: T): R {
    return fns.reduceRight((acc, fn) => fn(acc), arg);
  };
}

// Example usage
function addOne(x: number): number {
  return x + 1;
}
function twoTimes(x: number): number {
  return x * 2;
}
function displayResult(x: number): string {
  return `The result is ${x}.`;
}

const resultAfterCalculated = compose(displayResult, twoTimes, addOne);
console.log(resultAfterCalculated(3)) // "The result is 8."

In this way above, it makes the compose function's type inference display a type hint accurately

I wanna do something equivalent to the above in Python like this:

from typing import TypeVar, Callable, Any

T1 = TypeVar('T1')
T2 = TypeVar('T2')
T3 = TypeVar('T3')
T4 = TypeVar('T4')
T5 = TypeVar('T5')
T6 = TypeVar('T6')
T7 = TypeVar('T7')
R = TypeVar('R')

def compose(fn1: Callable[[T1], T2], fn2: Callable[[T2], R]) -> Callable[[T1], R],
def compose(
    fn1: Callable[[T1], T2],
    fn2: Callable[[T2], T3],
    fn3: Callable[[T3], R]) -> Callable[[T1], R],
def compose(fn1: Callable[[T1], T2], fn2: Callable[[T2], T3], fn3: Callable[[T3], T4],
    fn4: Callable[[T4], R],) -> Callable[[T1], R],
def compose(
    fn1: Callable[[T1], T2],
    fn2: Callable[[T2], T3],
    fn3: Callable[[T3], T4],
    fn4: Callable[[T4], T5],
    fn5: Callable[[T5], R],
) -> Callable[[T1], R],
def compose(
    fn1: Callable[[T1], T2],
    fn2: Callable[[T2], T3],
    fn3: Callable[[T3], T4],
    fn4: Callable[[T4], T5],
    fn5: Callable[[T5], T6],
    fn6: Callable[[T6], R],
) -> Callable[[T1], R],
def compose(
    fn1: Callable[[T1], T2],
    fn2: Callable[[T2], T3],
    fn3: Callable[[T3], T4],
    fn4: Callable[[T4], T5],
    fn5: Callable[[T5], T6], 
    fn6: Callable[[T6], T7],
    fn7: Callable[[T7], R],
) -> Callable[[T1], R],
def compose(*funcs: Callable[[Any], Any]) -> Callable[[Any], Any]:
    def composed(arg: Any) -> Any:
        for func in reversed(funcs):
            arg = func(arg)
        return arg
    return composed

# Example usage
add_one: Callable[[int], int] = lambda x: x + 1
two_times: Callable[[int], int] = lambda x: x * 2
display_result = lambda x: f"The result is {x}."

result_after_calculated = compose(displayResult, twoTimes, addOne)
print(result_after_calculated) # "The result is 8."

If it can be, will it possible to make this support Python version lower than v.3.12?

Rosuav commented 2 months ago

This is better discussed on the Python Discourse. This repository is for PEP documents, not general ideas.

JelleZijlstra commented 2 months ago

Note this already exists: https://docs.python.org/3/library/typing.html#overload