I want a function to have two signatures: an internal and an external one.
Here’s one way to do it:
from typing import overload
class A: ...
class A_(A): ...
@overload
def f1(x: int, a: A_, y: int): ... # type: ignore
def f1(x: int, a: A, y: int):
f1(x, a, y) # type error expected
The # type: ignore
is necessary because there’s only one overload. This check can’t be globally disabled in pyright (the type checker I’m currently using) without disabling other important checks.
Note that the type error on the last line is exactly what I want.
Why am I doing this? To force the programmer to give explicit permission to pass certain types to certain functions. For example, the error above would be avoided by doing something like this:
@overload
def f1(x: int, a: A_, y: int): ... # type: ignore
def f1(x: int, a: A, y: int):
f1(x, lift(a), y)
A better alternative to the overload approach would be a decorator:
@access
def f1(x: int, a: A, y: int):
f1(x, a, y) # type error expected
Unfortunately, Python doesn’t let us do full-fledge type-level programming like TypeScript, so I don’t think this is a viable route.
I also tried playing with covariance, unions, etc… to get the same result without overloads or decorators, but I don’t think that’s possible.
That said, I’d be extremely grateful to anyone who can prove me wrong!
Is it possible to come up with a signature such that when a type “enters” a function it changes in such a way that it can’t “enter” the function a second time?
def f1(a: ???):
f1(a) # type error
Thank you for your time!