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/

Reply via email to