I think the problem comes up more with invariant types than covariant types (if I remember terms correctly).
If the element type doesn't matter, then Sequence[object] is more type-safe than Sequence[Any] because it'll catch if you accidentally access something on the elements (copy/paste mistake, leftover debug code, etc). Since Sequence (and the other read-only iterables iirc) is covariant, you can pass any Sequence in and there isn't really a problem in practice.
But, that trick doesn't really work for MutableSequence because it's invariant. Passing [1, 2, 3] ( MutableSequence[int] ) to MutableSequence[object] is a type error. So, you're forced to do MutableSequence[Any] -- but now you've lost the stricter type checking from above.
The example given on the pytype issue tracker was:
def swap(items: MutableSequence[???], i: int, j: int):
items[i], items[j] = items[j], items[i]
Which I supposed would generalize to something like, "You can't write a type-safe function that accepts a mutable generic" (a mutable generic should probably be invariant).
I suppose you could work around this by using T and returning T, but this again forces you into a bad design decision (returning a modified input is, at the least, questionable, since it can lead to confusing behavior/incorrect assumptions). Or I guess having a private method that does T->T, with a public wrapper that just accepts T. Or sprinkle casting around. But these really seem like playing games to accomplish what seems like something that should be relatively straight forward.
- I think every time pytype has warned me about an unused T, it was right for some reason. e.g. I copy/pasted something and forgot to fully clean it up/finish it. So I've generally liked the error.
- That said, I can't recall any of the cases where the runtime behavior would have been incorrect. I don't often deal with mutable, generic'd inputs, though. Losing this error wouldn't be a big deal.
- I don't think a lone T in a signature should be treated as Any because it makes code less type-safe; if you're using a type var, you're clearly going out of your way to be more type safe.
This is a timely question. I just implemented a check in pyright for this condition because I found many cases where TypeVars were being used incorrectly within a function signature. I cleaned up a bunch of these cases within our code base thanks to this check.
I'm not sure what you mean by "Any swallows all errors" in the context of a `Sequence[Any]`. Presumably, the type checker would still treat that parameter as a Sequence even though it knows nothing about its contents. I don't see how the presence of a TypeVar would change that behavior. Could you elaborate?
If we think there is value is allowing a single instance of a TypeVar in a signature _and_ we can agree on a well-defined behavior for this case, then I think it makes sense to allow it. Otherwise, I think it's reasonable to emit an error to tell the user that they are using a language feature in a manner that is undefined.
Incidentally, when I implemented this check I noticed that it reported many errors within typeshed stdlib stubs. For example, `_SupportsLessThanT` appears only once in numerous signatures within builtins.pyi.
Contributor to pyright & pylance
Typing-sig mailing list -- firstname.lastname@example.org
To unsubscribe send an email to email@example.com
Member address: firstname.lastname@example.org