On Fri, Feb 12, 2021 at 10:27:01AM +0000, Mark Shannon wrote: > It impairs readability, because it muddles the return type. > The function in the example returns a bool. > The annotation is also misleading as the annotation is on the return > type, not on the parameter that is narrowed. > > At a glance, most programmers should be able to work out what > > def is_str_list(val: List[object]) -> bool: > > returns. > > But, > > def is_str_list(val: List[object]) -> TypeGuard[List[str]]: > > is likely to confuse and require careful reading. > Type hints are for humans as well as type checkers.
This! Without reading the PEP, how is anyone supposed to know that this returns a bool? This is obfuscated code. It looks like it returns an object of type `TypeGuard`. Even having read the PEP, it's still misleading: instead of a straightforward return type, I have to memorise that `TypeGuard[...]` is a magic thing that means * the return type is actually a bool; * and the type checker can narrow the type of the first parameter. Yuck. Consider: def is_list_of(val: List[object], T: Type) -> bool: return all(isinstance(x, T) for x in val) `if is_list_of(items, str)` should narrow the type of items to `List[str]`, but I don't think that there is any way to specify that. In fact that seems to be explicitly rejected: the decorator syntax is rejected because "it requires runtime evaluation of the type, precluding forward references". But type hints have to deal with forward references in other situations, and have had an effective mechanism for forward references since the beginning: https://www.python.org/dev/peps/pep-0484/#forward-references so I think that's a weak argument for rejection. And the argument that overriding the return type with a magic special value is "easier to understand" seems wrong to me. I think that Mark's proposal reads well: > An alternative, and more explicit, approach would be to use variable > annotations. [...] > def is_str_list(val: List[object]) -> bool: > """Determines whether all objects in the list are strings""" > val: NarrowsTo[List[str]] > return all(isinstance(x, str) for x in val) > > Although the above lacks flow control and is thus ambiguous without the > convention that `NarrowsTo` only applies if the result is True. But we need that convention for the TypeGuard return value annotation too. def is_str_list(val: List[object]) -> TypeGuard[List[str]]: only narrows the type of val if the function returns True. So I think that Mark's variable annotation has many benefits: - doesn't mangle the return annotation; - which is much more readable to the human reader; - naive software that doesn't care about type narrowing doesn't need to care about the variable annotation; - explicitly tells you which parameter is being narrowed; - easy to narrow any parameter, or more than one, without requiring special treatment of the first positional argument; - because the parameter is explicit, there is no need for special handling of self or cls. The only weakness is that we need the convention that the variable annotation will only apply if the function returns True, but that's the same convention as the TypeGuard. -- Steve _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/VENVXOTDSKPIC2BPFFKJ23G2DXFWJ6DO/ Code of Conduct: http://python.org/psf/codeofconduct/