Thanks for the comment. Although weird, it does allow one to create specialized versions of “functions” that have fewer generics. I think this is not possible today without classes.
import asyncio
class my_generic_function[A, B]:
async def __new__(cls, a: A, b: B) -> tuple[A, B]:
return a,b
class my_specialized_function[B](my_generic_function[str, B]): ...
async def main():
await my_specialized_function('hello', 1) # OK
await my_specialized_function(1, 1) # Type checker error
if __name__ == "__main__":
out = asyncio.run(main())
The async aspect is really irrelevant in this example, but it is good this trick also works for async functions.