[This is based on a discussion between me and @gvanrossum last week. I'm writing this down so that we won't forget about this.]
Several stdlib functions have signatures where the return type depends on the value of an argument. Examples:
openreturns anIO[str]orIO[bytes]depending on whether the mode argument has'b'as a substring.subprocess.check_outputreturns astrorbytesdepending on the value of the booleanuniversal_newlinesargument.
A simple way to support these would be to introduce a few additional types that work with str and bool literals:
- A string pattern type that can describe the contents of a string literal, perhaps using a regular expression.
- Types for
FalseandTrue.
For example, if we had FalseType and TrueType, we could write a function whose return value depends on the value of a boolean argument:
@overload def f(x: FalseType) -> bytes: ... @overload def f(x: TrueType) -> str: ... def f(x: Any) -> Any: if x: return 'x' else: return b'x' reveal_type(f(False)) # bytes reveal_type(f(True) # str
Not sure about this one:
... x = bool('') reveal_type(f(x)) # Union[str, bytes] since we don't know the boolean value statically?
We could also have a generic class where a generic type argument depends on the value of an argument to __init__. Not sure how we should represent these -- here is one potential approach:
class C(Generic[AnyStr]): @overload @returntype('C[str]') def __init__(self, x: TrueType) -> None: ... @overload @returntype('C[bytes]') def __init__(self, x: FalseType) -> None: ... ... reveal_type(C(True)) # C[str]
Alternatively, we could use __new__, but this would be problematic if there is no corresponding method defined at runtime.