A case I think is worth consideration: the runtime use of type hints for serialization/deserialization of values.
If deserializing a value that's annotated as `list[str | int]`, if UnionType argument order is applied, every deserialized list element would resolve to a `str` type. This is in fact how a library I maintain currently performs deserialization. To date, for cases like this I've recommended the order be switched so as to avoid every element falling into the catch-all `str` deserialization.
I'm not familiar with any documentation (in PEP 484 or elsewhere) that explicitly states that unions in the Python type system are unordered, but I have made that assumption in Pyright. I think mypy makes a similar assumption.
In type theory, it makes sense that unions would be order-independent and fully commutative — i.e. `X | Y` should be equivalent to `Y | X`.
There are a few places where this comes up. One is when determining whether two types are compatible, especially when a union is used for the specialization of an invariant type parameter. I think we can agree that the following is type safe and should be allowed.
```python
def func(a: list[str | int]):
b: list[int | str] = a
```
Another place where union ordering becomes important is in the constraint solver. Martin, I'm guessing this is the case that you're referring to?
The way I solve the union ordering problem in the constraint solver in Pyright is to attempt type matches for each subtype within the union and "score" each successful match. The score represents the "simplicity" of the solution. I choose the one with the simplest solution. For example:
```python
from typing import TypeVar
T = TypeVar("T")
def func1(a: T | list[T]) -> T: ...
def func2(a: list[T] | T) -> T: ...
def test(val: list[int]):
reveal_type(func1(val)) # int
reveal_type(func2(val)) # int
```
In this example, the constraint solver attempts to match `list[int]` against both `list[T]` and `T`. Both of these are valid solutions given the constraints, but the simplest solution is `T = int` (versus `T = list[int]`). The "simplicity calculation" is a heuristic, and I've needed to tune it over time to achieve the best results, but it seems to work well and produce deterministic results that do not depend on union ordering — unless two solutions happen to result in exactly the same simplicity score.
Interestingly, mypy emits false positive errors for the above example.
-Eric
--
Eric Traut
Contributor to Pyright & Pylance
Microsoft
_______________________________________________