2 ms·
> Annotations can and should be checked Unfortunately Python’s type system is unsound. It’s possible to pass all the checks and yet still have a function annot
by pansa2 2y ago
> Annotations can and should be checked
Unfortunately Python’s type system is unsound. It’s possible to pass all the checks and yet still have a function annotated `int` that returns a `list`.
- __MatrixMan__ 2y agoDo you mean that you're allowed to only use types where you want to, which means maybe the type checker can't check in cases where you haven't hinted enough, or is there some problem with the type system itself?
- pansa2 2y agoThe type system itself is unsound. For example, this code passes `mypy --strict`, but prints `<class 'list'>` even though `bar` is annotated to return an `int`: i : int | list[int] = 0 def foo() -> None: global i i = [] def bar() -> int: if isinstance(i, int): foo() return i return 0 print(type(bar()))
- _dain_ 2y agoSo don't do this then? The type system does not have to be sound to be useful; Typescript proves this.
- pansa2 2y ago> So don't do this then? Don't do what? - Don't write unsound code? There's no way to know until you run the program and find out your `int` is actually a `list`. - Don't assume type annotations are correct? Then what's the point of all the extra code to appease the type checker if it doesn't provide any guarantees?
- _dain_ 2y agoDon't do this stupid party trick with `global`. You may as well argue that unit tests are pointless because you could cheat by making the implementations return just the hardcoded values from the test cases.
- pansa2 2y agoThis isn't a "party trick" with `global`, it's a fundamental hole in the type system: class C: def __init__(self) -> None: self.i : int | list[int] = 0 def foo(self) -> None: self.i = [] def bar(self) -> int: if isinstance(self.i, int): self.foo() return self.i return 0 print(type(C().bar()))
- maleldil 2y agoThis seems to work in TypeScript too: class C { i: number | number[]; constructor() { this.i = 0; } foo(): void { this.i = []; } bar(): number { if (typeof this.i === 'number') { this.foo(); return this.i; } return 0; } } console.log(typeof new C().bar()); It seems to be a problem with "naked" type unions in general. It's unfortunate.
- __MatrixMan__ 2y agoAgreed, but I've never found it to be especially problematic. The type checker still catches the vast majority of things you'd expect a type checker to catch. If you want to be able to change the type of something at runtime, static analysis isn't always going to be able to have your back 100% of the time. Turns out that's a tradeoff that many are willing to make.
- maleldil 2y agoYes, 100%. I believe that any new Python codebase should embrace typing as much as possible[1], and any JavaScript library should be TypeScript instead. A type system with holes like this is better than no type system. [1] Unfortunately, many important 3rd party libraries aren't typed. I try to wrap them in type-safe modules or localise their use, but if your codebase is deeply dependent on them, this isn't always feasible.
- sevensor 2y agoTrue and irrelevant. Type annotations catch whole swathes of errors before they cause trouble and they nudge me into writing clearer code. I know they’re not watertight. Sometimes the type checker just can’t deal with a type and I have to use Any or a cast. Still better than not using them.