Skip to content

Assignment of Any does not get rid of Optional (add AnyUnion type?) #3526

Closed
@ddfisher

Description

@ddfisher

Consider this code (run with --strict-optional):

from typing import Optional
def f():
  # untyped function; often from silent import
  return 0  # always returns an int

def g(x: Optional[int]) -> int
  if x is None:
    x = f()
  return x  # E: Incompatible return value type (got "Optional[int]", expected "int")

Mypy doesn't infer that x is not None in g. I think this a bug -- mypy
should be permissive here.

If we wanted to fix this, we'd run into a problem: what type should x have
after x = f()? We don't want it to be Any, because we don't want that
behavior in the general case. It shouldn't be int -- f() could be intended
to unconditionally return None instead. To fix this, I think we need to
introduce a new special type, which I will call AnyUnion. AnyUnion[A, B, C] would represent a type which is allowed to be used as any of A, B, or
C (similarly to how Any is allowed to be used as any type). In this case,
we could have x's type bound to AnyUnion[int, None], which would fix this
error.

Other helpful uses of AnyUnion:

  • pow will no longer need to return Any in builtins. Instead, it could
    return the much less broad AnyUnion[int, float].
  • AnyUnion[X, None] would provide a per-variable opt-out of strict None
    checking. This could be helpful for classes with delayed initialization where
    a lot of effort would be needed to refactor them into a state that would work
    properly with strict None checking.

Cons:

  • Additional complexity of the type system affects implementation complexity and
    makes things slightly harder for users to understand.
  • The motivating problem also exists with subclasses (with the way the binder
    currently works), but AnyUnion doesn't help much with that.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions