TypeGuard isn’t my favourite feature. It feels boilerplate-y to me. It puts responsibility for soundness with the user (similar to
cast, but with less cultural awareness of that fact).
My ideal solution for narrowing is a small fantasy I call
@inline_guards. You put this decorator on top of your utility functions, wave a magic wand, and type checkers inline the implications for control flow (sort of like pyright’s ability to alias conditional expressions, but on steroids). Such a thing could potentially be a really nice replacement for the proposed
TypeAssert. (And if you continue waving the magic wand, maybe you can get better semantics when method calls assign instance variables)
This theoretical construct is my bar for how usable a type guard construct could be. It’s low boilerplate — you don’t have to re-encode the same information that’s in your main code into your type annotation code. There is a safety net — if you mess something up, the type checker will not narrow incorrectly.
There are several problems with this idea, but a reason I never floated it when TypeGuard was being discussed is that
@inline_guards simply doesn’t work for the “non-strict” narrowing that PEP 647 lets you do.
However, it is much more relevant to the strict narrowing semantics PEP 724 tries to enable, so I’m now thinking of it again. Many of the cases I saw in PEP 724: Stricter Type Guards - #97 by hauntsaninja would be more conveniently expressed with
@inline_guards. In particular, the functions I noticed where unsoundness was possible under 724 semantics would be sound if the functions were decorated with
@inline_guards but would still perform the narrowing you’d like.
Of course, there are several problems with this half-baked idea:
- The feature would pose problems for ensuring type checker consistency, because narrowing behaviour between different type checkers currently diverges a fair amount (although in most cases of divergence I think the desirable behaviour is pretty clear). This divergence previously did not affect public API symbols, but would with
@inline_guards(although it seems like
TypeGuardhasn’t yet found a huge amount of use in public APIs).
- For some advanced cases, you’d have to use
if TYPE_CHECKINGto be able to get good strict narrowing with
@inline_guards. This might be a good thing, since it’s usually pretty obvious to users that they need to fulfill the promises they are making to the type checker when using
if TYPE_CHECKINGthan it is with
-> TypeGuard[XYZ], but it certainly cedes ergonomic benefits
- You’d need to define the behaviour allowed quite carefully, e.g. all recursion would need to be disallowed
- If you wanted to use the feature in stubs, you’d need to have a non-trivial body
- …and many more
Like any good ideas thread, I don’t know where I’m going while typing this up / and this was always going to be the kind of idea that sounds nice in theory but where devils lie in the details.
(These ramblings are mine alone, not the Typing Council’s. This is not a coherent proposal, so doesn’t really affect my views on PEP 724)