I've been thinking about readability hard because I share many of your concerns about readability.
I'm starting to think parenthesizing the outside might work much better in practice: ``` (int, str -> bool) ``` instead of ``` (int, str) -> bool ``` This looks a bit less familiar, but it - eliminates any situation - helps the eye group together functions (and lets us use an editor's go-to-delimiter if we get lost!) An example ========= The function ---------------- To get at what I mean, here's a nice simple function: ``` def zip(f, g): def zipped(x: int, y: str): return f(x), g(y) return zipped ``` typing.Callable -------------------- Here's it's type using typing.Callable: ``` typing.Callable[ [typing.Callable[[int], bool], typing.Callable[[str], float]], typing.Callable[[int, str], tuple[bool, float] ] ``` which seems ugly. It's actually not bad compared to a lot of production code, but this is the kind of thing that led to PEP 677. current PEP 677 proposal ---------------------------------- With the current PEP 677 proposal, writing the type on one line we get ``` ((int) -> float, (str) -> bool) -> (int, str) -> tuple[float, bool] ``` I prefer this to the Callable version. But it’s dense, and I do agree it illustrates some readability issues with PEP 677. Using (int, str -> bool) syntax -------------------------------- Here’s the type if we change the syntax to put the right parenthesis after the return type: ``` ((int -> float), (str -> bool) -> (int, str -> tuple[float, bool]) ``` To my eyes, most of the pain points are now eliminated. - there’s never a double-arrow due to callable in return position - even for argument types, to my eyes it’s now easier to read An added bonus is we no longer have to think about double-arrows when a callable type is in the return position of a function, e.g.: ``` def f() -> (int, str -> bool): ... ``` And it solves another major usability problem we found - writing optional callables - because it’s now no problem at all to write ``` (int, str -> bool) | None ``` as the type of an optional callable argument. New edge cases / readability issues? ------------------------------------- I’m pretty sure *all* existing edge cases and readability issues are basically solved by this syntax because the grouping becomes obvious. The only new edge case I can see is that we might not like `(-> bool)` for parameterless callables. I’d be okay with that, but I’d also be down with a hack like `(() -> bool)`. I don’t actually think it matters much - by making this change we would trade multiple edge cases in *complicated* examples for a single edge case in the *simplest possible* example where readability isn’t a big problem. _______________________________________________ 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/AXRKGCPP2GTDRINQ6NQYM743Y6EH6FO5/ Code of Conduct: http://python.org/psf/codeofconduct/