On Tue, Sep 8, 2020 at 7:00 PM Shantanu Jain <hauntsaninja@gmail.com> wrote:
I found Jukka’s referenced comment to be valuable, so here’s a direct link: https://github.com/python/mypy/issues/2008#issuecomment-296213676 Eg, I believe pyright’s narrowing to Animal corresponds in part to Jukka’s Proposal 2 (or Proposal 4), which he mentions is TypeScript’s behaviour: when narrowing a Union, narrow to a member of the Union.

My view on the invariance issue is the most natural flow is to start with:
def g(x: Foo[Union[int, str]]): ...

def f() -> None:
    y = 1
    a = Foo(y)
Then get an error (with a very clear message / suggestion) and add:
def f() -> None:
    y = 1
    a: Foo[Union[int, str]] = Foo(y)
Which is fine in the presence of type narrowing, because obviously we shouldn’t narrow to an incompatible type. (It’s also my opinion that this is the type annotation that best helps describe what’s happening)

I don’t know that I’d be so quick to write off always narrowing (modulo Any), which I believe corresponds to Jukka’s Proposal 3. I’d be curious to know more about the significant number of false positives you’d expect; that seems contra my intuition and the results on the sample corpus Jukka described. It also feels wrong to mandate that all future type checkers can’t narrow in part because a majority of our current ones do not. It feels like if a type checker can prove something about a value, it should be fair game (and that as long as users can see that proof is possible, we’ll get bug reports).

Generally speaking, it’s surprising to me to make a usability argument for a distinction that most end users wouldn’t notice or weren’t making with intention. But maybe this is a case where if the behaviour is PEP-ed users will no longer find it surprising.

These are all good points. I remember that in Closure Compiler, we used to have the semantics where we keep the rhs type. We ran into issues occasionally where the user needed to use the value with the declared type, so they had to cast to that, which was annoying. We ended up changing the semantics to use the declared type. Unfortunately I can't remember the examples anymore though; just the generics one I already mentioned.

On Tue, 8 Sep 2020 at 09:28, dimvar--- via Typing-sig <typing-sig@python.org> wrote:
I also prefer form (1) to respect the declared type and not use the rhs type. Keeping the declared type can avoid a spurious warning in cases where type invariance matters, e.g., with generics.

T = TypeVar('T')

class Foo(Generic[T]):
    def __init__(self, x: T) -> None:
        self.attr = x

def g(x: Foo[Union[int, str]]):
    return x

def f(x: int):
    y: Union[int, str] = 1
    a = Foo(y)
    g(a) # no warning

    y = 2
    b = Foo(y)
    g(b) # warning
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
Member address: hauntsaninja@gmail.com