On Sun, Jan 09, 2022 at 08:42:14AM -0800, Christopher Barker wrote:
Perhaps it's worth remembering that this thread spun off one about adding syntax to Python because the current syntax isn't capable of easily expressing an important type hinting concept (i.e. Callable).
I shall quote the PEP's justification (in part) for the benefit of those who haven't read it.
[quote PEP 677] There are a few usability challenges with Callable we can see here:
• It is verbose, particularly for more complex function signatures.
• It relies on two levels of nested brackets, unlike any other generic type. This can be especially hard to read when some of the type parameters are themselves generic types.
• The bracket structure is not visually similar to how function signatures are written.
• It requires an explicit import, unlike many of the other most common types like list.
Possibly as a result, programmers often fail to write complete Callable types. Such untyped or partially-typed callable types do not check the parameter types or return types of the given callable and thus negate the benefits of static typing. [/quote]
These are legitimate issues with the Callable type hint. Does that mean that the existing syntax "isn't capable of easily" expressing callables? I don't think so.
"Easily" is subjective, and at the risk of undermining the justification for the PEP, I think it is fairly easy:
# type hint for a function of two parameters, the first takes # an int, float or complex, the second a bool, and returns # either a str or bytes.
from typing import Callable T = Callable[[int|float|complex, bool], str|bytes]
It was harder to write it out in English than to write the type hint.
So its not *difficult*, there's one import and one class with a subscript needed. The type system is perfectly capable of expressing that. It is no more hard to read than any other expression of equivalent complexity:
from aardvark import Surveying T = Surveying((spam+eggs+cheese, eels), foo*bar)
But can we do better? The PEP authors think we can, and I am inclined to agree with them.
While it is true that "not everything needs to be a builtin", in the case of typing, we are evolving to use builtins where possible:
* we use int, and always have, not typing.Int;
* what was once typing.List[int] is now list[int];
* what was once Union[float, int] is now float|int
etc, but Callable is the last remaining common compound type without a syntactic shortcut. Its not that declaring callables are *too hard* (let alone impossible) but that we can do better than what we've got.
Callable[[int|float|complex, bool], str|bytes]
(int|float|complex, bool) -> str|bytes
The second looks like executable pseudo-code for a function declaration. It avoids the import and the nested square brackets, and it is shorter without being so terse it becomes cryptic.
The improved syntax looks just like a function signature with the parameter names excised:
def func(a: int|float|complex, b: bool) -> str|bytes
which I think is a win.
So arguing that Python is completely readable for type hints is a bit off-mark, isn't it?
What does "completely readable" mean?
If you mean that it is impossible to compose a type-hint of such stupendous complexity and obfuscatory brilliance that even the infamous RFC-822 email address validator regex appears trivial in comparison?
Of course I don't mean that. Type hints are expressions, and expressions can have arbitrary complexity, including "too damn high".
I argued that type hints are no harder to read than other expressions of the equivalent complexity. I didn't argue that they can't be abused or that the syntax cannot be improved.
More to follow...